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);
|
const inactivePeriod = new Date(today);
|
||||||
inactivePeriod.setDate(today.getDate() - MODERATOR_INACTIVITY_LIMIT_DAYS);
|
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
|
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 {
|
return {
|
||||||
isModeratorsInactive: inactiveModerators.length !== moderators.length,
|
isModeratorsInactive: inactiveModerators.length === moderators.length,
|
||||||
inactiveModerators,
|
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