remove NoOpCaches in favor of CacheManagementService
This commit is contained in:
parent
c8976fef94
commit
3f950e398b
10 changed files with 240 additions and 554 deletions
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue