remove NoOpCaches in favor of CacheManagementService

This commit is contained in:
Hazelnoot 2025-10-01 11:48:56 -04:00
parent c8976fef94
commit 3f950e398b
10 changed files with 240 additions and 554 deletions

View file

@ -1,212 +0,0 @@
/*
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as Redis from 'ioredis';
import { Inject } from '@nestjs/common';
import { FakeInternalEventService } from './FakeInternalEventService.js';
import type { BlockingsRepository, FollowingsRepository, MiUser, MutingsRepository, NoteThreadMutingsRepository, RenoteMutingsRepository, UserProfilesRepository, UsersRepository, ChannelFollowingsRepository, InstancesRepository } from '@/models/_.js';
import type { MiLocalUser } from '@/models/User.js';
import { MemoryKVCache, MemorySingleCache, RedisKVCache, RedisSingleCache } from '@/misc/cache.js';
import { QuantumKVCache, QuantumKVOpts } from '@/misc/QuantumKVCache.js';
import { CacheService, FollowStats } from '@/core/CacheService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { InternalEventService } from '@/core/InternalEventService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { IdService } from '@/core/IdService.js';
export function noOpRedis() {
return {
set: () => Promise.resolve(),
get: () => Promise.resolve(null),
del: () => Promise.resolve(),
on: () => {},
off: () => {},
} as unknown as Redis.Redis;
}
export class NoOpCacheService extends CacheService {
public readonly fakeRedis: {
[K in keyof Redis.Redis]: Redis.Redis[K];
};
public readonly fakeInternalEventService: FakeInternalEventService;
constructor(
@Inject(DI.usersRepository)
usersRepository: UsersRepository,
@Inject(DI.userProfilesRepository)
userProfilesRepository: UserProfilesRepository,
@Inject(DI.mutingsRepository)
mutingsRepository: MutingsRepository,
@Inject(DI.blockingsRepository)
blockingsRepository: BlockingsRepository,
@Inject(DI.renoteMutingsRepository)
renoteMutingsRepository: RenoteMutingsRepository,
@Inject(DI.followingsRepository)
followingsRepository: FollowingsRepository,
@Inject(DI.noteThreadMutingsRepository)
noteThreadMutingsRepository: NoteThreadMutingsRepository,
@Inject(DI.channelFollowingsRepository)
channelFollowingsRepository: ChannelFollowingsRepository,
@Inject(DI.instancesRepository)
instancesRepository: InstancesRepository,
@Inject(UserEntityService)
userEntityService: UserEntityService,
@Inject(UtilityService)
utilityService: UtilityService,
@Inject(IdService)
idService: IdService,
) {
const fakeRedis = noOpRedis();
const fakeInternalEventService = new FakeInternalEventService();
super(
fakeRedis,
fakeRedis,
usersRepository,
userProfilesRepository,
mutingsRepository,
blockingsRepository,
renoteMutingsRepository,
followingsRepository,
noteThreadMutingsRepository,
channelFollowingsRepository,
instancesRepository,
userEntityService,
fakeInternalEventService,
utilityService,
idService,
);
this.fakeRedis = fakeRedis;
this.fakeInternalEventService = fakeInternalEventService;
// Override caches
this.userByIdCache = new NoOpMemoryKVCache<MiUser>();
this.localUserByNativeTokenCache = new NoOpMemoryKVCache<MiLocalUser | null>();
this.localUserByIdCache = new NoOpMemoryKVCache<MiLocalUser>();
this.uriPersonCache = new NoOpMemoryKVCache<MiUser | null>();
this.userProfileCache = NoOpQuantumKVCache.copy(this.userProfileCache, fakeInternalEventService);
this.userMutingsCache = NoOpQuantumKVCache.copy(this.userMutingsCache, fakeInternalEventService);
this.userBlockingCache = NoOpQuantumKVCache.copy(this.userBlockingCache, fakeInternalEventService);
this.userBlockedCache = NoOpQuantumKVCache.copy(this.userBlockedCache, fakeInternalEventService);
this.renoteMutingsCache = NoOpQuantumKVCache.copy(this.renoteMutingsCache, fakeInternalEventService);
this.threadMutingsCache = NoOpQuantumKVCache.copy(this.threadMutingsCache, fakeInternalEventService);
this.noteMutingsCache = NoOpQuantumKVCache.copy(this.noteMutingsCache, fakeInternalEventService);
this.userFollowingsCache = NoOpQuantumKVCache.copy(this.userFollowingsCache, fakeInternalEventService);
this.userFollowersCache = NoOpQuantumKVCache.copy(this.userFollowersCache, fakeInternalEventService);
this.hibernatedUserCache = NoOpQuantumKVCache.copy(this.hibernatedUserCache, fakeInternalEventService);
this.userFollowStatsCache = new NoOpMemoryKVCache<FollowStats>();
this.translationsCache = NoOpRedisKVCache.copy(this.translationsCache, fakeRedis);
this.userFollowingChannelsCache = NoOpQuantumKVCache.copy(this.userFollowingChannelsCache, fakeInternalEventService);
}
}
export class NoOpMemoryKVCache<T> extends MemoryKVCache<T> {
constructor() {
super(-1);
}
}
export class NoOpMemorySingleCache<T> extends MemorySingleCache<T> {
constructor() {
super(-1);
}
}
export class NoOpRedisKVCache<T> extends RedisKVCache<T> {
constructor(opts?: {
redis?: Redis.Redis;
fetcher?: RedisKVCache<T>['fetcher'];
toRedisConverter?: RedisKVCache<T>['toRedisConverter'];
fromRedisConverter?: RedisKVCache<T>['fromRedisConverter'];
}) {
super(
opts?.redis ?? noOpRedis(),
'no-op',
{
lifetime: -1,
memoryCacheLifetime: -1,
fetcher: opts?.fetcher,
toRedisConverter: opts?.toRedisConverter,
fromRedisConverter: opts?.fromRedisConverter,
},
);
}
public static copy<T>(cache: RedisKVCache<T>, redis?: Redis.Redis): NoOpRedisKVCache<T> {
return new NoOpRedisKVCache<T>({
redis,
fetcher: cache.fetcher,
toRedisConverter: cache.toRedisConverter,
fromRedisConverter: cache.fromRedisConverter,
});
}
}
export class NoOpRedisSingleCache<T> extends RedisSingleCache<T> {
constructor(opts?: {
redis?: Redis.Redis;
fetcher?: RedisSingleCache<T>['fetcher'];
toRedisConverter?: RedisSingleCache<T>['toRedisConverter'];
fromRedisConverter?: RedisSingleCache<T>['fromRedisConverter'];
}) {
super(
opts?.redis ?? noOpRedis(),
'no-op',
{
lifetime: -1,
memoryCacheLifetime: -1,
fetcher: opts?.fetcher,
toRedisConverter: opts?.toRedisConverter,
fromRedisConverter: opts?.fromRedisConverter,
},
);
}
public static copy<T>(cache: RedisSingleCache<T>, redis?: Redis.Redis): NoOpRedisSingleCache<T> {
return new NoOpRedisSingleCache<T>({
redis,
fetcher: cache.fetcher,
toRedisConverter: cache.toRedisConverter,
fromRedisConverter: cache.fromRedisConverter,
});
}
}
export class NoOpQuantumKVCache<T> extends QuantumKVCache<T> {
constructor(opts: Omit<QuantumKVOpts<T>, 'lifetime'> & {
internalEventService?: InternalEventService,
}) {
super(
opts.internalEventService ?? new FakeInternalEventService(),
'no-op',
{
...opts,
lifetime: -1,
},
);
}
public static copy<T>(cache: QuantumKVCache<T>, internalEventService?: InternalEventService): NoOpQuantumKVCache<T> {
return new NoOpQuantumKVCache<T>({
internalEventService,
fetcher: cache.fetcher,
bulkFetcher: cache.bulkFetcher,
onChanged: cache.onChanged,
});
}
}

View file

@ -8,8 +8,9 @@ process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals';
import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing';
import { NoOpCacheService } from '../misc/noOpCaches.js';
import { FakeInternalEventService } from '../misc/FakeInternalEventService.js';
import { FakeCacheManagementService } from '../misc/FakeCacheManagementService.js';
import { MockInternalEventService } from '../misc/MockInternalEventService.js';
import { CacheManagementService } from '@/core/CacheManagementService.js';
import { GlobalModule } from '@/GlobalModule.js';
import { AnnouncementService } from '@/core/AnnouncementService.js';
import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
@ -23,11 +24,11 @@ import type {
} from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { genAidx } from '@/misc/id/aidx.js';
import { CacheService } from '@/core/CacheService.js';
import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { RoleService } from '@/core/RoleService.js';
import { CoreModule } from '@/core/CoreModule.js';
import { secureRndstr } from '@/misc/secure-rndstr.js';
import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock';
@ -42,7 +43,7 @@ describe('AnnouncementService', () => {
let announcementReadsRepository: AnnouncementReadsRepository;
let globalEventService: jest.Mocked<GlobalEventService>;
let moderationLogService: jest.Mocked<ModerationLogService>;
let roleService: jest.Mocked<RoleService>;
let cacheManagementService: CacheManagementService;
function createUser(data: Partial<MiUser> = {}) {
const un = secureRndstr(16);
@ -66,20 +67,11 @@ describe('AnnouncementService', () => {
.then(x => announcementsRepository.findOneByOrFail(x.identifiers[0]));
}
beforeEach(async () => {
beforeAll(async () => {
app = await Test.createTestingModule({
imports: [
GlobalModule,
],
providers: [
AnnouncementService,
AnnouncementEntityService,
CacheService,
IdService,
InternalEventService,
GlobalEventService,
ModerationLogService,
RoleService,
CoreModule,
],
})
.useMocker((token) => {
@ -99,19 +91,27 @@ describe('AnnouncementService', () => {
.overrideProvider(RoleService).useValue({
getUserRoles: jest.fn((_) => []),
})
.overrideProvider(InternalEventService).useClass(FakeInternalEventService)
.overrideProvider(CacheService).useClass(NoOpCacheService)
// TODO should we remove this now that cache is cleared?
.overrideProvider(InternalEventService).useClass(MockInternalEventService)
.overrideProvider(CacheManagementService).useClass(FakeCacheManagementService)
.compile();
await app.init();
app.enableShutdownHooks();
});
afterAll(async () => {
await app.close();
});
beforeEach(() => {
announcementService = app.get<AnnouncementService>(AnnouncementService);
usersRepository = app.get<UsersRepository>(DI.usersRepository);
announcementsRepository = app.get<AnnouncementsRepository>(DI.announcementsRepository);
announcementReadsRepository = app.get<AnnouncementReadsRepository>(DI.announcementReadsRepository);
globalEventService = app.get<GlobalEventService>(GlobalEventService) as jest.Mocked<GlobalEventService>;
moderationLogService = app.get<ModerationLogService>(ModerationLogService) as jest.Mocked<ModerationLogService>;
roleService = app.get<RoleService>(RoleService) as jest.Mocked<RoleService>;
cacheManagementService = app.get(CacheManagementService);
});
afterEach(async () => {
@ -121,8 +121,10 @@ describe('AnnouncementService', () => {
announcementsRepository.delete({}),
announcementReadsRepository.delete({}),
]);
await app.close();
moderationLogService.log.mockReset();
globalEventService.publishMainStream.mockReset();
globalEventService.publishBroadcastStream.mockReset();
cacheManagementService.clear();
});
describe('getUnreadAnnouncements', () => {

View file

@ -7,8 +7,10 @@ process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals';
import { Test } from '@nestjs/testing';
import { Redis } from 'ioredis';
import { GodOfTimeService } from '../misc/GodOfTimeService.js';
import { MockRedis } from '../misc/MockRedis.js';
import type { TestingModule } from '@nestjs/testing';
import type { InstancesRepository } from '@/models/_.js';
import { GlobalModule } from '@/GlobalModule.js';
import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
@ -16,67 +18,69 @@ import { HttpRequestService } from '@/core/HttpRequestService.js';
import { LoggerService } from '@/core/LoggerService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { IdService } from '@/core/IdService.js';
import { EnvService } from '@/core/EnvService.js';
import { CacheManagementService } from '@/core/CacheManagementService.js';
import { CoreModule } from '@/core/CoreModule.js';
import { DI } from '@/di-symbols.js';
function mockRedis() {
const hash = {} as any;
const set = jest.fn((key: string, value) => {
const ret = hash[key];
hash[key] = value;
return ret;
});
return set;
}
import { TimeService } from '@/core/TimeService.js';
describe('FetchInstanceMetadataService', () => {
let app: TestingModule;
let fetchInstanceMetadataService: jest.Mocked<FetchInstanceMetadataService>;
let fetchInstanceMetadataService: FetchInstanceMetadataService;
let federatedInstanceService: jest.Mocked<FederatedInstanceService>;
let httpRequestService: jest.Mocked<HttpRequestService>;
let redisClient: jest.Mocked<Redis>;
let redisClient: MockRedis;
let instancesRepository: InstancesRepository;
let cacheManagementService: CacheManagementService;
let timeService: GodOfTimeService;
beforeEach(async () => {
beforeAll(async () => {
app = await Test
.createTestingModule({
imports: [
GlobalModule,
],
providers: [
FetchInstanceMetadataService,
LoggerService,
UtilityService,
IdService,
EnvService,
CoreModule,
],
})
.useMocker((token) => {
if (token === HttpRequestService) {
return { getJson: jest.fn(), getHtml: jest.fn(), send: jest.fn() };
} else if (token === FederatedInstanceService) {
return { fetchOrRegister: jest.fn() };
} else if (token === DI.redis) {
return mockRedis;
}
return null;
})
.overrideProvider(TimeService).useClass(GodOfTimeService)
.overrideProvider(HttpRequestService).useValue({ getJson: jest.fn(), getHtml: jest.fn(), send: jest.fn() })
.overrideProvider(FederatedInstanceService).useValue({ fetchOrRegister: jest.fn() })
.overrideProvider(DI.redis).useClass(MockRedis)
.overrideProvider(DI.redisForSub).useClass(MockRedis)
.overrideProvider(DI.redisForPub).useClass(MockRedis)
.compile();
await app.init();
app.enableShutdownHooks();
fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService) as jest.Mocked<FetchInstanceMetadataService>;
fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService);
federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService) as jest.Mocked<FederatedInstanceService>;
redisClient = app.get<Redis>(DI.redis) as jest.Mocked<Redis>;
httpRequestService = app.get<HttpRequestService>(HttpRequestService) as jest.Mocked<HttpRequestService>;
instancesRepository = app.get<InstancesRepository>(DI.instancesRepository);
cacheManagementService = app.get(CacheManagementService);
timeService = app.get(TimeService);
redisClient = app.get(DI.redis);
});
afterEach(async () => {
afterAll(async () => {
await app.close();
});
beforeEach(() => {
federatedInstanceService.fetchOrRegister.mockReset();
httpRequestService.getJson.mockReset();
httpRequestService.getHtml.mockReset();
httpRequestService.send.mockReset();
redisClient.mockReset();
timeService.resetToNow();
});
afterEach(async () => {
await instancesRepository.delete({});
cacheManagementService.clear();
});
test('Lock and update', async () => {
redisClient.set = mockRedis();
const now = Date.now();
const now = timeService.now;
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
@ -90,8 +94,7 @@ describe('FetchInstanceMetadataService', () => {
});
test('Lock and don\'t update', async () => {
redisClient.set = mockRedis();
const now = Date.now();
const now = timeService.now;
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
@ -105,8 +108,7 @@ describe('FetchInstanceMetadataService', () => {
});
test('Do nothing when lock not acquired', async () => {
redisClient.set = mockRedis();
const now = Date.now();
const now = timeService.now;
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
await fetchInstanceMetadataService.tryLock('example.com');
@ -121,8 +123,7 @@ describe('FetchInstanceMetadataService', () => {
});
test('Do when lock not acquired but forced', async () => {
redisClient.set = mockRedis();
const now = Date.now();
const now = timeService.now;
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
await fetchInstanceMetadataService.tryLock('example.com');

View file

@ -5,17 +5,16 @@
process.env.NODE_ENV = 'test';
import { setTimeout } from 'node:timers/promises';
import { jest } from '@jest/globals';
import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing';
import * as lolex from '@sinonjs/fake-timers';
import { NoOpCacheService } from '../misc/noOpCaches.js';
import { FakeInternalEventService } from '../misc/FakeInternalEventService.js';
import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock';
import { GodOfTimeService } from '../misc/GodOfTimeService.js';
import { CacheManagementService } from '@/core/CacheManagementService.js';
import { GlobalModule } from '@/GlobalModule.js';
import { RoleService } from '@/core/RoleService.js';
import { CoreModule } from '@/core/CoreModule.js';
import {
InstancesRepository,
MetasRepository,
@ -38,6 +37,7 @@ import { NotificationService } from '@/core/NotificationService.js';
import { RoleCondFormulaValue } from '@/models/Role.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { InternalEventService } from '@/core/InternalEventService.js';
import { TimeService } from '@/core/TimeService.js';
const moduleMocker = new ModuleMocker(global);
@ -51,7 +51,8 @@ describe('RoleService', () => {
let meta: jest.Mocked<MiMeta>;
let metasRepository: MetasRepository;
let notificationService: jest.Mocked<NotificationService>;
let clock: lolex.InstalledClock;
let cacheManagementService: CacheManagementService;
let timeService: GodOfTimeService;
async function createUser(data: Partial<MiUser> = {}) {
if (data.host != null) {
@ -59,8 +60,8 @@ describe('RoleService', () => {
.createQueryBuilder('instance')
.insert()
.values({
id: genAidx(Date.now()),
firstRetrievedAt: new Date(),
id: genAidx(timeService.now),
firstRetrievedAt: timeService.date,
host: data.host,
})
.orIgnore()
@ -69,7 +70,7 @@ describe('RoleService', () => {
const un = secureRndstr(16);
const x = await usersRepository.insert({
id: genAidx(Date.now()),
id: genAidx(timeService.now),
username: un,
usernameLower: un,
...data,
@ -85,9 +86,9 @@ describe('RoleService', () => {
async function createRole(data: Partial<MiRole> = {}) {
const x = await rolesRepository.insert({
id: genAidx(Date.now()),
updatedAt: new Date(),
lastUsedAt: new Date(),
id: genAidx(timeService.now),
updatedAt: timeService.date,
lastUsedAt: timeService.date,
name: '',
description: '',
...data,
@ -105,8 +106,8 @@ describe('RoleService', () => {
}
async function assignRole(args: Partial<MiRoleAssignment>) {
const id = genAidx(Date.now());
const expiresAt = new Date();
const id = genAidx(timeService.now);
const expiresAt = timeService.date;
expiresAt.setDate(expiresAt.getDate() + 1);
await roleAssignmentsRepository.insert({
@ -119,37 +120,14 @@ describe('RoleService', () => {
}
function aidx() {
return genAidx(Date.now());
return genAidx(timeService.now);
}
beforeEach(async () => {
clock = lolex.install({
now: new Date(),
shouldClearNativeTimers: true,
});
beforeAll(async () => {
app = await Test.createTestingModule({
imports: [
GlobalModule,
],
providers: [
RoleService,
CacheService,
IdService,
GlobalEventService,
UserEntityService,
{
provide: NotificationService,
useFactory: () => ({
createNotification: jest.fn(),
}),
},
{
provide: NotificationService.name,
useExisting: NotificationService,
},
MetaService,
InternalEventService,
CoreModule,
],
})
.useMocker((token) => {
@ -160,10 +138,11 @@ describe('RoleService', () => {
}
})
.overrideProvider(MetaService).useValue({ fetch: jest.fn() })
.overrideProvider(InternalEventService).useClass(FakeInternalEventService)
.overrideProvider(CacheService).useClass(NoOpCacheService)
.overrideProvider(TimeService).useClass(GodOfTimeService)
.overrideProvider(NotificationService).useValue({ createNotification: jest.fn() })
.compile();
await app.init();
app.enableShutdownHooks();
roleService = app.get<RoleService>(RoleService);
@ -172,16 +151,25 @@ describe('RoleService', () => {
rolesRepository = app.get<RolesRepository>(DI.rolesRepository);
roleAssignmentsRepository = app.get<RoleAssignmentsRepository>(DI.roleAssignmentsRepository);
metasRepository = app.get<MetasRepository>(DI.metasRepository);
cacheManagementService = app.get(CacheManagementService);
timeService = app.get<GodOfTimeService>(TimeService);
timeService.resetToNow();
meta = app.get<MiMeta>(DI.meta) as jest.Mocked<MiMeta>;
notificationService = app.get<NotificationService>(NotificationService) as jest.Mocked<NotificationService>;
});
await roleService.onModuleInit();
afterAll(async () => {
await app.close();
});
beforeEach(() => {
notificationService.createNotification.mockReset();
timeService.resetToNow();
});
afterEach(async () => {
clock.uninstall();
await Promise.all([
metasRepository.delete({}),
usersRepository.delete({}),
@ -189,7 +177,7 @@ describe('RoleService', () => {
roleAssignmentsRepository.delete({}),
]);
await app.close();
cacheManagementService.clear();
});
describe('getUserPolicies', () => {
@ -282,25 +270,28 @@ describe('RoleService', () => {
},
},
});
await roleService.assign(user.id, role.id, new Date(Date.now() + (1000 * 60 * 60 * 24)));
await roleService.assign(user.id, role.id, new Date(timeService.now + (1000 * 60 * 60 * 24)));
meta.policies = {
canManageCustomEmojis: false,
};
// Condition 1: user has role immediately after assigning
const result = await roleService.getUserPolicies(user.id);
expect(result.canManageCustomEmojis).toBe(true);
clock.tick('25:00:00');
timeService.now += 1000 * 60 * 60 * 25; // 25h
// Condition 2: user loses role within 1hr after expiration
const resultAfter25h = await roleService.getUserPolicies(user.id);
expect(resultAfter25h.canManageCustomEmojis).toBe(false);
await roleService.assign(user.id, role.id);
// ストリーミング経由で反映されるまでちょっと待つ
clock.uninstall();
await setTimeout(100);
// Wait 100ms for the task queue to complete.
await new Promise(r => setTimeout(r, 100));
// Condition 3: user regains role within 100ms after assigning again
const resultAfter25hAgain = await roleService.getUserPolicies(user.id);
expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
});
@ -318,11 +309,11 @@ describe('RoleService', () => {
await Promise.all([
assignRole({ userId: adminUser1.id, roleId: role1.id }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: modeUser1.id, roleId: role2.id }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: normalUser1.id, roleId: role3.id }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(timeService.now - 1000) }),
]);
const result = await roleService.getModeratorIds({
@ -344,11 +335,11 @@ describe('RoleService', () => {
await Promise.all([
assignRole({ userId: adminUser1.id, roleId: role1.id }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: modeUser1.id, roleId: role2.id }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: normalUser1.id, roleId: role3.id }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(timeService.now - 1000) }),
]);
const result = await roleService.getModeratorIds({
@ -370,11 +361,11 @@ describe('RoleService', () => {
await Promise.all([
assignRole({ userId: adminUser1.id, roleId: role1.id }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: modeUser1.id, roleId: role2.id }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: normalUser1.id, roleId: role3.id }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(timeService.now - 1000) }),
]);
const result = await roleService.getModeratorIds({
@ -396,11 +387,11 @@ describe('RoleService', () => {
await Promise.all([
assignRole({ userId: adminUser1.id, roleId: role1.id }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: modeUser1.id, roleId: role2.id }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: normalUser1.id, roleId: role3.id }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(timeService.now - 1000) }),
]);
const result = await roleService.getModeratorIds({
@ -422,11 +413,11 @@ describe('RoleService', () => {
await Promise.all([
assignRole({ userId: adminUser1.id, roleId: role1.id }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: modeUser1.id, roleId: role2.id }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: normalUser1.id, roleId: role3.id }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(timeService.now - 1000) }),
]);
const result = await roleService.getModeratorIds({
@ -496,8 +487,8 @@ describe('RoleService', () => {
await Promise.all([
assignRole({ userId: adminUser1.id, roleId: role1.id }),
assignRole({ userId: modeUser1.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: rootUser.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
assignRole({ userId: modeUser1.id, roleId: role2.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: rootUser.id, roleId: role2.id, expiresAt: new Date(timeService.now - 1000) }),
assignRole({ userId: normalUser1.id, roleId: role3.id }),
]);
@ -745,7 +736,7 @@ describe('RoleService', () => {
});
test('ユーザが作成されてから指定期間経過した', async () => {
const base = new Date();
const base = timeService.date;
base.setMinutes(base.getMinutes() - 5);
const d1 = new Date(base);
@ -778,7 +769,7 @@ describe('RoleService', () => {
});
test('ユーザが作成されてから指定期間経っていない', async () => {
const base = new Date();
const base = timeService.date;
base.setMinutes(base.getMinutes() - 5);
const d1 = new Date(base);
@ -941,8 +932,7 @@ describe('RoleService', () => {
await roleService.assign(user.id, role.id);
clock.uninstall();
await setTimeout(100);
timeService.now += 100;
const assignments = await roleAssignmentsRepository.find({
where: {
@ -969,8 +959,7 @@ describe('RoleService', () => {
await roleService.assign(user.id, role.id);
clock.uninstall();
await setTimeout(100);
timeService.now += 100;
const assignments = await roleAssignmentsRepository.find({
where: {

View file

@ -19,6 +19,8 @@ import { DI } from '@/di-symbols.js';
import { QueueService } from '@/core/QueueService.js';
import { LoggerService } from '@/core/LoggerService.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
import { CacheManagementService } from '@/core/CacheManagementService.js';
import { CoreModule } from '@/core/CoreModule.js';
describe('SystemWebhookService', () => {
let app: TestingModule;
@ -29,6 +31,7 @@ describe('SystemWebhookService', () => {
let usersRepository: UsersRepository;
let systemWebhooksRepository: SystemWebhooksRepository;
let idService: IdService;
let cacheManagementService: CacheManagementService;
let queueService: jest.Mocked<QueueService>;
// --------------------------------------------------------------------------------------
@ -61,58 +64,48 @@ describe('SystemWebhookService', () => {
// --------------------------------------------------------------------------------------
async function beforeAllImpl() {
beforeAll(async () => {
app = await Test
.createTestingModule({
imports: [
GlobalModule,
],
providers: [
SystemWebhookService,
IdService,
LoggerService,
GlobalEventService,
{
provide: QueueService, useFactory: () => ({ systemWebhookDeliver: jest.fn() }),
},
{
provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }),
},
CoreModule,
],
})
.overrideProvider(QueueService).useValue({ systemWebhookDeliver: jest.fn() })
.overrideProvider(ModerationLogService).useValue({ log: () => Promise.resolve() })
.compile();
await app.init();
app.enableShutdownHooks();
usersRepository = app.get(DI.usersRepository);
systemWebhooksRepository = app.get(DI.systemWebhooksRepository);
service = app.get(SystemWebhookService);
idService = app.get(IdService);
cacheManagementService = app.get(CacheManagementService);
queueService = app.get(QueueService) as jest.Mocked<QueueService>;
});
app.enableShutdownHooks();
}
async function afterAllImpl() {
afterAll(async () => {
await app.close();
}
});
async function beforeEachImpl() {
beforeEach(async () => {
root = await createUser({ username: 'root', usernameLower: 'root' });
}
});
async function afterEachImpl() {
afterEach(async () => {
await usersRepository.delete({});
await systemWebhooksRepository.delete({});
}
queueService.systemWebhookDeliver.mockReset();
cacheManagementService.clear();
});
// --------------------------------------------------------------------------------------
describe('アプリを毎回作り直す必要のないグループ', () => {
beforeAll(beforeAllImpl);
afterAll(afterAllImpl);
beforeEach(beforeEachImpl);
afterEach(afterEachImpl);
describe('fetchSystemWebhooks', () => {
test('フィルタなし', async () => {
const webhook1 = await createWebhook({
@ -298,16 +291,6 @@ describe('SystemWebhookService', () => {
});
describe('アプリを毎回作り直す必要があるグループ', () => {
beforeEach(async () => {
await beforeAllImpl();
await beforeEachImpl();
});
afterEach(async () => {
await afterEachImpl();
await afterAllImpl();
});
describe('enqueueSystemWebhook', () => {
test('キューに追加成功', async () => {
const webhook = await createWebhook({

View file

@ -11,8 +11,10 @@ import { FollowingsRepository, InstancesRepository, MiUser, UserProfilesReposito
import { IdService } from '@/core/IdService.js';
import { GlobalModule } from '@/GlobalModule.js';
import { DI } from '@/di-symbols.js';
import { CacheManagementService } from '@/core/CacheManagementService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { genAidx } from '@/misc/id/aidx.js';
import { CoreModule } from '@/core/CoreModule.js';
describe('UserSearchService', () => {
let app: TestingModule;
@ -22,6 +24,7 @@ describe('UserSearchService', () => {
let usersRepository: UsersRepository;
let followingsRepository: FollowingsRepository;
let idService: IdService;
let cacheManagementService: CacheManagementService;
let userProfilesRepository: UserProfilesRepository;
let root: MiUser;
@ -103,17 +106,12 @@ describe('UserSearchService', () => {
.createTestingModule({
imports: [
GlobalModule,
CoreModule,
],
providers: [
UserSearchService,
{
provide: UserEntityService, useFactory: jest.fn(() => ({
// とりあえずIDが返れば確認が出来るので
packMany: (value: any) => value,
})),
},
IdService,
],
})
.overrideProvider(UserEntityService).useValue({
// とりあえずIDが返れば確認が出来るので
packMany: (value: any) => value,
})
.compile();
@ -127,6 +125,7 @@ describe('UserSearchService', () => {
service = app.get(UserSearchService);
idService = app.get(IdService);
cacheManagementService = app.get(CacheManagementService);
});
beforeEach(async () => {
@ -145,6 +144,7 @@ describe('UserSearchService', () => {
afterEach(async () => {
await usersRepository.delete({});
cacheManagementService.clear();
});
afterAll(async () => {

View file

@ -15,6 +15,8 @@ import { DI } from '@/di-symbols.js';
import { QueueService } from '@/core/QueueService.js';
import { LoggerService } from '@/core/LoggerService.js';
import { UserWebhookService } from '@/core/UserWebhookService.js';
import { CacheManagementService } from '@/core/CacheManagementService.js';
import { CoreModule } from '@/core/CoreModule.js';
describe('UserWebhookService', () => {
let app: TestingModule;
@ -25,6 +27,7 @@ describe('UserWebhookService', () => {
let usersRepository: UsersRepository;
let userWebhooksRepository: WebhooksRepository;
let idService: IdService;
let cacheManagementService: CacheManagementService;
let queueService: jest.Mocked<QueueService>;
// --------------------------------------------------------------------------------------
@ -58,55 +61,47 @@ describe('UserWebhookService', () => {
// --------------------------------------------------------------------------------------
async function beforeAllImpl() {
beforeAll(async () => {
app = await Test
.createTestingModule({
imports: [
GlobalModule,
],
providers: [
UserWebhookService,
IdService,
LoggerService,
GlobalEventService,
{
provide: QueueService, useFactory: () => ({ userWebhookDeliver: jest.fn() }),
},
CoreModule,
],
})
.overrideProvider(QueueService).useValue({ userWebhookDeliver: jest.fn() })
.compile();
await app.init();
app.enableShutdownHooks();
usersRepository = app.get(DI.usersRepository);
userWebhooksRepository = app.get(DI.webhooksRepository);
service = app.get(UserWebhookService);
idService = app.get(IdService);
cacheManagementService = app.get(CacheManagementService);
queueService = app.get(QueueService) as jest.Mocked<QueueService>;
});
app.enableShutdownHooks();
}
async function afterAllImpl() {
afterAll(async () => {
await app.close();
}
});
async function beforeEachImpl() {
beforeEach(async () => {
root = await createUser({ username: 'root', usernameLower: 'root' });
}
});
async function afterEachImpl() {
afterEach(async () => {
await usersRepository.delete({});
await userWebhooksRepository.delete({});
}
queueService.userWebhookDeliver.mockReset();
cacheManagementService.clear();
});
// --------------------------------------------------------------------------------------
describe('アプリを毎回作り直す必要のないグループ', () => {
beforeAll(beforeAllImpl);
afterAll(afterAllImpl);
beforeEach(beforeEachImpl);
afterEach(afterEachImpl);
describe('fetchSystemWebhooks', () => {
test('フィルタなし', async () => {
const webhook1 = await createWebhook({
@ -243,16 +238,6 @@ describe('UserWebhookService', () => {
});
describe('アプリを毎回作り直す必要があるグループ', () => {
beforeEach(async () => {
await beforeAllImpl();
await beforeEachImpl();
});
afterEach(async () => {
await afterEachImpl();
await afterAllImpl();
});
describe('enqueueUserWebhook', () => {
test('キューに追加成功', async () => {
const webhook = await createWebhook({

View file

@ -15,6 +15,8 @@ import { IdService } from '@/core/IdService.js';
import { DI } from '@/di-symbols.js';
import { QueueService } from '@/core/QueueService.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { CacheManagementService } from '@/core/CacheManagementService.js';
import { CoreModule } from '@/core/CoreModule.js';
describe('WebhookTestService', () => {
let app: TestingModule;
@ -28,6 +30,7 @@ describe('WebhookTestService', () => {
let userWebhookService: jest.Mocked<UserWebhookService>;
let systemWebhookService: jest.Mocked<SystemWebhookService>;
let idService: IdService;
let cacheManagementService: CacheManagementService;
let root: MiUser;
let alice: MiUser;
@ -53,44 +56,40 @@ describe('WebhookTestService', () => {
app = await Test.createTestingModule({
imports: [
GlobalModule,
CoreModule,
],
providers: [
WebhookTestService,
IdService,
{
provide: CustomEmojiService, useFactory: () => ({
populateEmojis: jest.fn(),
}),
},
{
provide: QueueService, useFactory: () => ({
systemWebhookDeliver: jest.fn(),
userWebhookDeliver: jest.fn(),
}),
},
{
provide: UserWebhookService, useFactory: () => ({
fetchWebhooks: jest.fn(),
}),
},
{
provide: SystemWebhookService, useFactory: () => ({
fetchSystemWebhooks: jest.fn(),
}),
},
],
}).compile();
})
.overrideProvider(CustomEmojiService).useValue({
populateEmojis: jest.fn(),
})
.overrideProvider(QueueService).useValue({
systemWebhookDeliver: jest.fn(),
userWebhookDeliver: jest.fn(),
})
.overrideProvider(UserWebhookService).useValue({
fetchWebhooks: jest.fn(),
})
.overrideProvider(SystemWebhookService).useValue({
fetchSystemWebhooks: jest.fn(),
})
.compile();
await app.init();
app.enableShutdownHooks();
usersRepository = app.get(DI.usersRepository);
userProfilesRepository = app.get(DI.userProfilesRepository);
service = app.get(WebhookTestService);
idService = app.get(IdService);
cacheManagementService = app.get(CacheManagementService);
queueService = app.get(QueueService) as jest.Mocked<QueueService>;
userWebhookService = app.get(UserWebhookService) as jest.Mocked<UserWebhookService>;
systemWebhookService = app.get(SystemWebhookService) as jest.Mocked<SystemWebhookService>;
});
app.enableShutdownHooks();
afterAll(async () => {
await app.close();
});
beforeEach(async () => {
@ -111,12 +110,9 @@ describe('WebhookTestService', () => {
userWebhookService.fetchWebhooks.mockClear();
systemWebhookService.fetchSystemWebhooks.mockClear();
await usersRepository.delete({});
await userProfilesRepository.delete({});
});
afterAll(async () => {
await app.close();
await usersRepository.delete({});
cacheManagementService.clear();
});
// --------------------------------------------------------------------------------------

View file

@ -2,19 +2,16 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import { generateKeyPair } from 'crypto';
import { Test, TestingModule } from '@nestjs/testing';
import { jest } from '@jest/globals';
import { NoOpCacheService } from '../misc/noOpCaches.js';
import { FakeInternalEventService } from '../misc/FakeInternalEventService.js';
import { MockLoggerService } from '../misc/MockLoggerService.js';
import type { Config } from '@/config.js';
import type { MiLocalUser, MiRemoteUser } from '@/models/User.js';
import { InternalEventService } from '@/core/InternalEventService.js';
import { CacheService } from '@/core/CacheService.js';
import { ApImageService } from '@/core/activitypub/models/ApImageService.js';
import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js';
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
@ -25,6 +22,8 @@ import { GlobalModule } from '@/GlobalModule.js';
import { CoreModule } from '@/core/CoreModule.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { LoggerService } from '@/core/LoggerService.js';
import { CacheManagementService } from '@/core/CacheManagementService.js';
import { ApResolverService } from '@/core/activitypub/ApResolverService.js';
import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js';
import { MiMeta, MiNote, MiUser, MiUserKeypair, UserProfilesRepository, UserPublickeysRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
@ -106,6 +105,8 @@ describe('ActivityPub', () => {
let userPublickeysRepository: UserPublickeysRepository;
let userKeypairService: UserKeypairService;
let config: Config;
let cacheManagementService: CacheManagementService;
let mockLoggerService: MockLoggerService;
const metaInitial = {
id: 'x',
@ -158,9 +159,8 @@ describe('ActivityPub', () => {
};
},
})
.overrideProvider(DI.meta).useFactory({ factory: () => meta })
.overrideProvider(CacheService).useClass(NoOpCacheService)
.overrideProvider(InternalEventService).useClass(FakeInternalEventService)
.overrideProvider(DI.meta).useValue(meta)
.overrideProvider(LoggerService).useClass(MockLoggerService)
.compile();
await app.init();
@ -178,20 +178,24 @@ describe('ActivityPub', () => {
userPublickeysRepository = app.get<UserPublickeysRepository>(DI.userPublickeysRepository);
userKeypairService = app.get<UserKeypairService>(UserKeypairService);
config = app.get<Config>(DI.config);
// Prevent ApPersonService from fetching instance, as it causes Jest import-after-test error
const federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService);
jest.spyOn(federatedInstanceService, 'fetch').mockImplementation(() => new Promise(() => { }));
});
beforeEach(() => {
resolver.clear();
cacheManagementService = app.get(CacheManagementService);
mockLoggerService = app.get<MockLoggerService>(LoggerService);
notesRepository = app.get<NotesRepository>(DI.notesRepository);
});
afterAll(async () => {
await app.close();
});
beforeEach(async () => {
// Clear all caches app-wide
cacheManagementService.clear();
// Reset mocks
mockLoggerService.reset();
resolver.clear();
});
describe('Parse minimum object', () => {
const actor = createRandomActor();

View file

@ -4,9 +4,8 @@
*/
import { Test, TestingModule } from '@nestjs/testing';
import { FakeInternalEventService } from '../../misc/FakeInternalEventService.js';
import { NoOpCacheService } from '../../misc/noOpCaches.js';
import type { MiUser } from '@/models/User.js';
import { CacheManagementService } from '@/core/CacheManagementService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { GlobalModule } from '@/GlobalModule.js';
import { CoreModule } from '@/core/CoreModule.js';
@ -21,39 +20,6 @@ import {
UsersRepository,
} from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { PageEntityService } from '@/core/entities/PageEntityService.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { AnnouncementService } from '@/core/AnnouncementService.js';
import { RoleService } from '@/core/RoleService.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { IdService } from '@/core/IdService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import { MetaService } from '@/core/MetaService.js';
import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js';
import { CacheService } from '@/core/CacheService.js';
import { ApResolverService } from '@/core/activitypub/ApResolverService.js';
import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js';
import { ApImageService } from '@/core/activitypub/models/ApImageService.js';
import { ApMfmService } from '@/core/activitypub/ApMfmService.js';
import { MfmService } from '@/core/MfmService.js';
import { HashtagService } from '@/core/HashtagService.js';
import UsersChart from '@/core/chart/charts/users.js';
import { ChartLoggerService } from '@/core/chart/ChartLoggerService.js';
import InstanceChart from '@/core/chart/charts/instance.js';
import { ApLoggerService } from '@/core/activitypub/ApLoggerService.js';
import { AccountMoveService } from '@/core/AccountMoveService.js';
import { ReactionService } from '@/core/ReactionService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
import { ChatService } from '@/core/ChatService.js';
import { InternalEventService } from '@/core/InternalEventService.js';
process.env.NODE_ENV = 'test';
@ -69,6 +35,7 @@ describe('UserEntityService', () => {
let blockingRepository: BlockingsRepository;
let mutingRepository: MutingsRepository;
let renoteMutingsRepository: RenoteMutingsRepository;
let cacheManagementService: CacheManagementService;
async function createUser(userData: Partial<MiUser> = {}, profileData: Partial<MiUserProfile> = {}) {
const un = secureRndstr(16);
@ -143,53 +110,11 @@ describe('UserEntityService', () => {
}
beforeAll(async () => {
const services = [
UserEntityService,
ApPersonService,
NoteEntityService,
PageEntityService,
CustomEmojiService,
AnnouncementService,
RoleService,
FederatedInstanceService,
IdService,
AvatarDecorationService,
UtilityService,
EmojiEntityService,
ModerationLogService,
GlobalEventService,
DriveFileEntityService,
MetaService,
FetchInstanceMetadataService,
CacheService,
ApResolverService,
ApNoteService,
ApImageService,
ApMfmService,
MfmService,
HashtagService,
UsersChart,
ChartLoggerService,
InstanceChart,
ApLoggerService,
AccountMoveService,
ReactionService,
ReactionsBufferingService,
NotificationService,
ChatService,
InternalEventService,
];
app = await Test.createTestingModule({
imports: [GlobalModule, CoreModule],
providers: [
...services,
...services.map(x => ({ provide: x.name, useExisting: x })),
],
})
.overrideProvider(InternalEventService).useClass(FakeInternalEventService)
.overrideProvider(CacheService).useClass(NoOpCacheService)
.compile();
await app.init();
app.enableShutdownHooks();
@ -202,12 +127,25 @@ describe('UserEntityService', () => {
blockingRepository = app.get<BlockingsRepository>(DI.blockingsRepository);
mutingRepository = app.get<MutingsRepository>(DI.mutingsRepository);
renoteMutingsRepository = app.get<RenoteMutingsRepository>(DI.renoteMutingsRepository);
cacheManagementService = app.get(CacheManagementService);
});
afterAll(async () => {
await app.close();
});
afterEach(async () => {
await userProfileRepository.delete({});
await userMemosRepository.delete({});
await followingRepository.delete({});
await followingRequestRepository.delete({});
await blockingRepository.delete({});
await mutingRepository.delete({});
await renoteMutingsRepository.delete({});
await usersRepository.delete({});
cacheManagementService.clear();
});
test('UserLite', async() => {
const me = await createUser();
const who = await createUser();