add test and fix
This commit is contained in:
parent
3e5b42ba0b
commit
f9ee6b54dd
|
@ -92,14 +92,21 @@ export class CheckModeratorsActivityProcessorService {
|
|||
const inactivePeriod = new Date(today);
|
||||
inactivePeriod.setDate(today.getDate() - MODERATOR_INACTIVITY_LIMIT_DAYS);
|
||||
|
||||
const moderators = await this.fetchModerators();
|
||||
const moderators = await this.fetchModerators()
|
||||
.then(it => it.filter(it => it.lastActiveDate != null));
|
||||
const inactiveModerators = moderators
|
||||
.filter(it => it.lastActiveDate != null && it.lastActiveDate.getTime() < inactivePeriod.getTime());
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
.filter(it => it.lastActiveDate!.getTime() < inactivePeriod.getTime());
|
||||
|
||||
// 残りの猶予を示したいので、最終アクティブ日時が一番若いモデレータの日数を基準に猶予を計算する
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const newestLastActiveDate = new Date(Math.max(...moderators.map(it => it.lastActiveDate!.getTime())));
|
||||
const inactivityLimitCountdown = Math.floor((newestLastActiveDate.getTime() - inactivePeriod.getTime()) / ONE_DAY_MILLI_SEC);
|
||||
|
||||
return {
|
||||
isModeratorsInactive: inactiveModerators.length !== moderators.length,
|
||||
isModeratorsInactive: inactiveModerators.length === moderators.length,
|
||||
inactiveModerators,
|
||||
inactivityLimitCountdown: MODERATOR_INACTIVITY_LIMIT_DAYS - Math.floor((today.getTime() - inactivePeriod.getTime()) / ONE_DAY_MILLI_SEC),
|
||||
inactivityLimitCountdown,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { jest } from '@jest/globals';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import * as lolex from '@sinonjs/fake-timers';
|
||||
import { addHours, addSeconds, subDays, subHours, subSeconds } from 'date-fns';
|
||||
import { CheckModeratorsActivityProcessorService } from '@/queue/processors/CheckModeratorsActivityProcessorService.js';
|
||||
import { MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { QueueLoggerService } from '@/queue/QueueLoggerService.js';
|
||||
|
||||
const baseDate = new Date(Date.UTC(2000, 11, 15, 12, 0, 0));
|
||||
|
||||
describe('CheckModeratorsActivityProcessorService', () => {
|
||||
let app: TestingModule;
|
||||
let clock: lolex.InstalledClock;
|
||||
let service: CheckModeratorsActivityProcessorService;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
let usersRepository: UsersRepository;
|
||||
let userProfilesRepository: UserProfilesRepository;
|
||||
let idService: IdService;
|
||||
let roleService: jest.Mocked<RoleService>;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
async function createUser(data: Partial<MiUser> = {}) {
|
||||
const id = idService.gen();
|
||||
const user = await usersRepository
|
||||
.insert({
|
||||
id: id,
|
||||
username: `user_${id}`,
|
||||
usernameLower: `user_${id}`.toLowerCase(),
|
||||
...data,
|
||||
})
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
await userProfilesRepository.insert({
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
function mockModeratorRole(users: MiUser[]) {
|
||||
roleService.getModerators.mockReset();
|
||||
roleService.getModerators.mockResolvedValue(users);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test
|
||||
.createTestingModule({
|
||||
imports: [
|
||||
GlobalModule,
|
||||
],
|
||||
providers: [
|
||||
CheckModeratorsActivityProcessorService,
|
||||
IdService,
|
||||
{
|
||||
provide: RoleService, useFactory: () => ({ getModerators: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: MetaService, useFactory: () => ({ fetch: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: QueueLoggerService, useFactory: () => ({
|
||||
logger: ({
|
||||
createSubLogger: () => ({
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
succ: jest.fn(),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
],
|
||||
})
|
||||
.compile();
|
||||
|
||||
usersRepository = app.get(DI.usersRepository);
|
||||
userProfilesRepository = app.get(DI.userProfilesRepository);
|
||||
|
||||
service = app.get(CheckModeratorsActivityProcessorService);
|
||||
idService = app.get(IdService);
|
||||
roleService = app.get(RoleService) as jest.Mocked<RoleService>;
|
||||
|
||||
app.enableShutdownHooks();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
clock = lolex.install({
|
||||
now: new Date(baseDate),
|
||||
shouldClearNativeTimers: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
clock.uninstall();
|
||||
await usersRepository.delete({});
|
||||
await userProfilesRepository.delete({});
|
||||
roleService.getModerators.mockReset();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
describe('evaluateModeratorsInactiveDays', () => {
|
||||
test('[isModeratorsInactive] inactiveなモデレーターがいても他のモデレーターがアクティブなら"運営が非アクティブ"としてみなされない', async () => {
|
||||
const [user1, user2, user3, user4] = await Promise.all([
|
||||
// 期限よりも1秒新しいタイミングでアクティブ化(セーフ)
|
||||
createUser({ lastActiveDate: subDays(addSeconds(baseDate, 1), 7) }),
|
||||
// 期限ちょうどにアクティブ化(セーフ)
|
||||
createUser({ lastActiveDate: subDays(baseDate, 7) }),
|
||||
// 期限よりも1秒古いタイミングでアクティブ化(アウト)
|
||||
createUser({ lastActiveDate: subDays(subSeconds(baseDate, 1), 7) }),
|
||||
// 対象外
|
||||
createUser({ lastActiveDate: null }),
|
||||
]);
|
||||
|
||||
mockModeratorRole([user1, user2, user3, user4]);
|
||||
|
||||
const result = await service.evaluateModeratorsInactiveDays();
|
||||
expect(result.isModeratorsInactive).toBe(false);
|
||||
expect(result.inactiveModerators).toEqual([user3]);
|
||||
});
|
||||
|
||||
test('[isModeratorsInactive] 全員非アクティブなら"運営が非アクティブ"としてみなされる', async () => {
|
||||
const [user1, user2] = await Promise.all([
|
||||
// 期限よりも1秒古いタイミングでアクティブ化(アウト)
|
||||
createUser({ lastActiveDate: subDays(subSeconds(baseDate, 1), 7) }),
|
||||
// 対象外
|
||||
createUser({ lastActiveDate: null }),
|
||||
]);
|
||||
|
||||
mockModeratorRole([user1, user2]);
|
||||
|
||||
const result = await service.evaluateModeratorsInactiveDays();
|
||||
expect(result.isModeratorsInactive).toBe(true);
|
||||
expect(result.inactiveModerators).toEqual([user1]);
|
||||
});
|
||||
|
||||
test('[countdown] 猶予まで24時間ある場合、猶予1日として計算される', async () => {
|
||||
const [user1, user2] = await Promise.all([
|
||||
createUser({ lastActiveDate: subDays(baseDate, 8) }),
|
||||
// 猶予はこのユーザ基準で計算される想定。
|
||||
// 期限まで残り24時間->猶予1日として計算されるはずである
|
||||
createUser({ lastActiveDate: subDays(baseDate, 6) }),
|
||||
]);
|
||||
|
||||
mockModeratorRole([user1, user2]);
|
||||
|
||||
const result = await service.evaluateModeratorsInactiveDays();
|
||||
expect(result.isModeratorsInactive).toBe(false);
|
||||
expect(result.inactiveModerators).toEqual([user1]);
|
||||
expect(result.inactivityLimitCountdown).toBe(1);
|
||||
});
|
||||
|
||||
test('[countdown] 猶予まで25時間ある場合、猶予1日として計算される', async () => {
|
||||
const [user1, user2] = await Promise.all([
|
||||
createUser({ lastActiveDate: subDays(baseDate, 8) }),
|
||||
// 猶予はこのユーザ基準で計算される想定。
|
||||
// 期限まで残り25時間->猶予1日として計算されるはずである
|
||||
createUser({ lastActiveDate: subDays(addHours(baseDate, 1), 6) }),
|
||||
]);
|
||||
|
||||
mockModeratorRole([user1, user2]);
|
||||
|
||||
const result = await service.evaluateModeratorsInactiveDays();
|
||||
expect(result.isModeratorsInactive).toBe(false);
|
||||
expect(result.inactiveModerators).toEqual([user1]);
|
||||
expect(result.inactivityLimitCountdown).toBe(1);
|
||||
});
|
||||
|
||||
test('[countdown] 猶予まで23時間ある場合、猶予0日として計算される', async () => {
|
||||
const [user1, user2] = await Promise.all([
|
||||
createUser({ lastActiveDate: subDays(baseDate, 8) }),
|
||||
// 猶予はこのユーザ基準で計算される想定。
|
||||
// 期限まで残り23時間->猶予0日として計算されるはずである
|
||||
createUser({ lastActiveDate: subDays(subHours(baseDate, 1), 6) }),
|
||||
]);
|
||||
|
||||
mockModeratorRole([user1, user2]);
|
||||
|
||||
const result = await service.evaluateModeratorsInactiveDays();
|
||||
expect(result.isModeratorsInactive).toBe(false);
|
||||
expect(result.inactiveModerators).toEqual([user1]);
|
||||
expect(result.inactivityLimitCountdown).toBe(0);
|
||||
});
|
||||
|
||||
test('[countdown] 期限ちょうどの場合、猶予0日として計算される', async () => {
|
||||
const [user1, user2] = await Promise.all([
|
||||
createUser({ lastActiveDate: subDays(baseDate, 8) }),
|
||||
// 猶予はこのユーザ基準で計算される想定。
|
||||
// 期限ちょうど->猶予0日として計算されるはずである
|
||||
createUser({ lastActiveDate: subDays(baseDate, 7) }),
|
||||
]);
|
||||
|
||||
mockModeratorRole([user1, user2]);
|
||||
|
||||
const result = await service.evaluateModeratorsInactiveDays();
|
||||
expect(result.isModeratorsInactive).toBe(false);
|
||||
expect(result.inactiveModerators).toEqual([user1]);
|
||||
expect(result.inactivityLimitCountdown).toBe(0);
|
||||
});
|
||||
|
||||
test('[countdown] 期限より1時間超過している場合、猶予-1日として計算される', async () => {
|
||||
const [user1, user2] = await Promise.all([
|
||||
createUser({ lastActiveDate: subDays(baseDate, 8) }),
|
||||
// 猶予はこのユーザ基準で計算される想定。
|
||||
// 期限より1時間超過->猶予-1日として計算されるはずである
|
||||
createUser({ lastActiveDate: subDays(subHours(baseDate, 1), 7) }),
|
||||
]);
|
||||
|
||||
mockModeratorRole([user1, user2]);
|
||||
|
||||
const result = await service.evaluateModeratorsInactiveDays();
|
||||
expect(result.isModeratorsInactive).toBe(true);
|
||||
expect(result.inactiveModerators).toEqual([user1, user2]);
|
||||
expect(result.inactivityLimitCountdown).toBe(-1);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue