/* * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import { setTimeout } from 'node:timers/promises'; import { describe, jest } from '@jest/globals'; import { ModuleMocker } from 'jest-mock'; import { Test } from '@nestjs/testing'; import * as lolex from '@sinonjs/fake-timers'; import type { TestingModule } from '@nestjs/testing'; import type { MockFunctionMetadata } from 'jest-mock'; import { GlobalModule } from '@/GlobalModule.js'; import { RoleService } from '@/core/RoleService.js'; import { MiMeta, MiRole, MiRoleAssignment, MiUser, RoleAssignmentsRepository, RolesRepository, UsersRepository, } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.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 { secureRndstr } from '@/misc/secure-rndstr.js'; import { NotificationService } from '@/core/NotificationService.js'; import { RoleCondFormulaValue } from '@/models/Role.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; const moduleMocker = new ModuleMocker(global); describe('RoleService', () => { let app: TestingModule; let roleService: RoleService; let usersRepository: UsersRepository; let rolesRepository: RolesRepository; let roleAssignmentsRepository: RoleAssignmentsRepository; let meta: jest.Mocked; let notificationService: jest.Mocked; let clock: lolex.InstalledClock; async function createUser(data: Partial = {}) { const un = secureRndstr(16); const x = await usersRepository.insert({ id: genAidx(Date.now()), username: un, usernameLower: un, ...data, }); return await usersRepository.findOneByOrFail(x.identifiers[0]); } async function createRoot(data: Partial = {}) { const user = await createUser(data); meta.rootUserId = user.id; return user; } async function createRole(data: Partial = {}) { const x = await rolesRepository.insert({ id: genAidx(Date.now()), updatedAt: new Date(), lastUsedAt: new Date(), name: '', description: '', ...data, }); return await rolesRepository.findOneByOrFail(x.identifiers[0]); } function createConditionalRole(condFormula: RoleCondFormulaValue, data: Partial = {}) { return createRole({ name: `[conditional] ${condFormula.type}`, target: 'conditional', condFormula: condFormula, ...data, }); } async function assignRole(args: Partial) { const id = genAidx(Date.now()); const expiresAt = new Date(); expiresAt.setDate(expiresAt.getDate() + 1); await roleAssignmentsRepository.insert({ id, expiresAt, ...args, }); return await roleAssignmentsRepository.findOneByOrFail({ id }); } function aidx() { return genAidx(Date.now()); } beforeEach(async () => { clock = lolex.install({ now: new Date(), shouldClearNativeTimers: true, }); app = await Test.createTestingModule({ imports: [ GlobalModule, ], providers: [ RoleService, CacheService, IdService, GlobalEventService, UserEntityService, { provide: NotificationService, useFactory: () => ({ createNotification: jest.fn(), }), }, { provide: NotificationService.name, useExisting: NotificationService, }, ], }) .useMocker((token) => { if (token === MetaService) { return { fetch: jest.fn() }; } if (typeof token === 'function') { const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata; const Mock = moduleMocker.generateFromMetadata(mockMetadata); return new Mock(); } }) .compile(); app.enableShutdownHooks(); roleService = app.get(RoleService); usersRepository = app.get(DI.usersRepository); rolesRepository = app.get(DI.rolesRepository); roleAssignmentsRepository = app.get(DI.roleAssignmentsRepository); meta = app.get(DI.meta) as jest.Mocked; notificationService = app.get(NotificationService) as jest.Mocked; await roleService.onModuleInit(); }); afterEach(async () => { clock.uninstall(); /** * Delete meta and roleAssignment first to avoid deadlock due to schema dependencies * https://github.com/misskey-dev/misskey/issues/16783 */ await app.get(DI.metasRepository).createQueryBuilder().delete().execute(); await roleAssignmentsRepository.createQueryBuilder().delete().execute(); await Promise.all([ usersRepository.createQueryBuilder().delete().execute(), rolesRepository.createQueryBuilder().delete().execute(), ]); await app.close(); }); describe('getUserAssigns', () => { test('アサインされたロールを取得できる', async () => { const user = await createUser(); const role1 = await createRole({ name: 'a' }); const role2 = await createRole({ name: 'b' }); await roleService.assign(user.id, role1.id); await roleService.assign(user.id, role2.id); const assigns = await roleService.getUserAssigns(user.id); expect(assigns).toHaveLength(2); expect(assigns.some(a => a.roleId === role1.id)).toBe(true); expect(assigns.some(a => a.roleId === role2.id)).toBe(true); }); test('アサインされたロールの有効/期限切れパターンを取得できる', async () => { const user = await createUser(); const roleNoExpiry = await createRole({ name: 'no-expires' }); const roleNotExpired = await createRole({ name: 'not-expired' }); const roleExpired = await createRole({ name: 'expired' }); // expiresAtなし await roleService.assign(user.id, roleNoExpiry.id); // expiresAtあり(期限切れでない) const future = new Date(Date.now() + 1000 * 60 * 60); // +1 hour await roleService.assign(user.id, roleNotExpired.id, future); // expiresAtあり(期限切れ) await assignRole({ userId: user.id, roleId: roleExpired.id, expiresAt: new Date(Date.now() - 1000) }); const assigns = await roleService.getUserAssigns(user.id); expect(assigns.some(a => a.roleId === roleNoExpiry.id)).toBe(true); expect(assigns.some(a => a.roleId === roleNotExpired.id)).toBe(true); expect(assigns.some(a => a.roleId === roleExpired.id)).toBe(false); }); }); describe('getUserRoles', () => { test('アサインされたロールとコンディショナルロールの両方が取得できる', async () => { const user = await createUser(); const manualRole = await createRole({ name: 'manual role' }); const conditionalRole = await createConditionalRole({ id: aidx(), type: 'isBot', }); await roleService.assign(user.id, manualRole.id); await roleService.assign(user.id, conditionalRole.id); const roles = await roleService.getUserRoles(user.id); expect(roles.some(r => r.id === manualRole.id)).toBe(true); expect(roles.some(r => r.id === conditionalRole.id)).toBe(true); }); }); describe('getUserPolicies', () => { test('instance default policies', async () => { const user = await createUser(); meta.policies = { canManageCustomEmojis: false, }; const result = await roleService.getUserPolicies(user.id); expect(result.canManageCustomEmojis).toBe(false); }); test('instance default policies 2', async () => { const user = await createUser(); meta.policies = { canManageCustomEmojis: true, }; const result = await roleService.getUserPolicies(user.id); expect(result.canManageCustomEmojis).toBe(true); }); test('with role', async () => { const user = await createUser(); const role = await createRole({ name: 'a', policies: { canManageCustomEmojis: { useDefault: false, priority: 0, value: true, }, }, }); await roleService.assign(user.id, role.id); meta.policies = { canManageCustomEmojis: false, }; const result = await roleService.getUserPolicies(user.id); expect(result.canManageCustomEmojis).toBe(true); }); test('priority', async () => { const user = await createUser(); const role1 = await createRole({ name: 'role1', policies: { driveCapacityMb: { useDefault: false, priority: 0, value: 200, }, }, }); const role2 = await createRole({ name: 'role2', policies: { driveCapacityMb: { useDefault: false, priority: 1, value: 100, }, }, }); await roleService.assign(user.id, role1.id); await roleService.assign(user.id, role2.id); meta.policies = { driveCapacityMb: 50, }; const result = await roleService.getUserPolicies(user.id); expect(result.driveCapacityMb).toBe(100); }); test('expired role', async () => { const user = await createUser(); const role = await createRole({ name: 'a', policies: { canManageCustomEmojis: { useDefault: false, priority: 0, value: true, }, }, }); await roleService.assign(user.id, role.id, new Date(Date.now() + (1000 * 60 * 60 * 24))); meta.policies = { canManageCustomEmojis: false, }; const result = await roleService.getUserPolicies(user.id); expect(result.canManageCustomEmojis).toBe(true); clock.tick('25:00:00'); const resultAfter25h = await roleService.getUserPolicies(user.id); expect(resultAfter25h.canManageCustomEmojis).toBe(false); await roleService.assign(user.id, role.id); // ストリーミング経由で反映されるまでちょっと待つ clock.uninstall(); await setTimeout(100); const resultAfter25hAgain = await roleService.getUserPolicies(user.id); expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true); }); test('role with no policy set', async () => { const user = await createUser(); const roleWithPolicy = await createRole({ name: 'roleWithPolicy', policies: { pinLimit: { useDefault: false, priority: 0, value: 10, }, }, }); const roleWithoutPolicy = await createRole({ name: 'roleWithoutPolicy', policies: {}, // ポリシーが空 }); await roleService.assign(user.id, roleWithPolicy.id); await roleService.assign(user.id, roleWithoutPolicy.id); meta.policies = { pinLimit: 5, }; const result = await roleService.getUserPolicies(user.id); // roleWithoutPolicy は default 値 (5) を使い、roleWithPolicy の 10 と比較して大きい方が採用される expect(result.pinLimit).toBe(10); }); }); describe('getUserBadgeRoles', () => { test('手動アサイン済みのバッジロールのみが返る', async () => { const user = await createUser(); const badgeRole = await createRole({ name: 'badge', asBadge: true }); const normalRole = await createRole({ name: 'normal', asBadge: false }); await roleService.assign(user.id, badgeRole.id); await roleService.assign(user.id, normalRole.id); const roles = await roleService.getUserBadgeRoles(user.id); expect(roles.some(r => r.id === badgeRole.id)).toBe(true); expect(roles.some(r => r.id === normalRole.id)).toBe(false); }); test('コンディショナルなバッジロールが条件一致で返る', async () => { const user = await createUser({ isBot: true }); const condBadgeRole = await createConditionalRole({ id: aidx(), type: 'isBot', }, { asBadge: true, name: 'cond-badge' }); const condNonBadgeRole = await createConditionalRole({ id: aidx(), type: 'isBot', }, { asBadge: false, name: 'cond-non-badge' }); const roles = await roleService.getUserBadgeRoles(user.id); expect(roles.some(r => r.id === condBadgeRole.id)).toBe(true); expect(roles.some(r => r.id === condNonBadgeRole.id)).toBe(false); }); test('roleAssignedTo 条件のバッジロール: アサイン有無で変化する', async () => { const [user1, user2] = await Promise.all([createUser(), createUser()]); const manualRole = await createRole({ name: 'manual' }); const condBadgeRole = await createConditionalRole({ id: aidx(), type: 'roleAssignedTo', roleId: manualRole.id, }, { asBadge: true, name: 'assigned-badge' }); await roleService.assign(user2.id, manualRole.id); const [roles1, roles2] = await Promise.all([ roleService.getUserBadgeRoles(user1.id), roleService.getUserBadgeRoles(user2.id), ]); expect(roles1.some(r => r.id === condBadgeRole.id)).toBe(false); expect(roles2.some(r => r.id === condBadgeRole.id)).toBe(true); }); test('期限切れのバッジロールは除外される', async () => { const user = await createUser(); const roleNoExpiry = await createRole({ name: 'no-exp', asBadge: true }); const roleNotExpired = await createRole({ name: 'not-expired', asBadge: true }); const roleExpired = await createRole({ name: 'expired', asBadge: true }); // expiresAt なし await roleService.assign(user.id, roleNoExpiry.id); // expiresAt あり(期限切れでない) const future = new Date(Date.now() + 1000 * 60 * 60); // +1 hour await roleService.assign(user.id, roleNotExpired.id, future); // expiresAt あり(期限切れ) await assignRole({ userId: user.id, roleId: roleExpired.id, expiresAt: new Date(Date.now() - 1000) }); const rolesBefore = await roleService.getUserBadgeRoles(user.id); expect(rolesBefore.some(r => r.id === roleNoExpiry.id)).toBe(true); expect(rolesBefore.some(r => r.id === roleNotExpired.id)).toBe(true); expect(rolesBefore.some(r => r.id === roleExpired.id)).toBe(false); // 時間経過で roleNotExpired を失効させる clock.tick('02:00:00'); const rolesAfter = await roleService.getUserBadgeRoles(user.id); expect(rolesAfter.some(r => r.id === roleNoExpiry.id)).toBe(true); expect(rolesAfter.some(r => r.id === roleNotExpired.id)).toBe(false); }); }); describe('getModeratorIds', () => { test('includeAdmins = false, includeRoot = false, excludeExpire = false', async () => { const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([ createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createRoot(), ]); const role1 = await createRole({ name: 'admin', isAdministrator: true }); const role2 = await createRole({ name: 'moderator', isModerator: true }); const role3 = await createRole({ name: 'normal' }); 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: modeUser1.id, roleId: role2.id }), assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }), assignRole({ userId: normalUser1.id, roleId: role3.id }), assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }), ]); const result = await roleService.getModeratorIds({ includeAdmins: false, includeRoot: false, excludeExpire: false, }); expect(result).toEqual([modeUser1.id, modeUser2.id]); }); test('includeAdmins = false, includeRoot = false, excludeExpire = true', async () => { const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([ createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createRoot(), ]); const role1 = await createRole({ name: 'admin', isAdministrator: true }); const role2 = await createRole({ name: 'moderator', isModerator: true }); const role3 = await createRole({ name: 'normal' }); 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: modeUser1.id, roleId: role2.id }), assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }), assignRole({ userId: normalUser1.id, roleId: role3.id }), assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }), ]); const result = await roleService.getModeratorIds({ includeAdmins: false, includeRoot: false, excludeExpire: true, }); expect(result).toEqual([modeUser1.id]); }); test('includeAdmins = true, includeRoot = false, excludeExpire = false', async () => { const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([ createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createRoot(), ]); const role1 = await createRole({ name: 'admin', isAdministrator: true }); const role2 = await createRole({ name: 'moderator', isModerator: true }); const role3 = await createRole({ name: 'normal' }); 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: modeUser1.id, roleId: role2.id }), assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }), assignRole({ userId: normalUser1.id, roleId: role3.id }), assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }), ]); const result = await roleService.getModeratorIds({ includeAdmins: true, includeRoot: false, excludeExpire: false, }); expect(result).toEqual([adminUser1.id, adminUser2.id, modeUser1.id, modeUser2.id]); }); test('includeAdmins = true, includeRoot = false, excludeExpire = true', async () => { const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([ createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createRoot(), ]); const role1 = await createRole({ name: 'admin', isAdministrator: true }); const role2 = await createRole({ name: 'moderator', isModerator: true }); const role3 = await createRole({ name: 'normal' }); 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: modeUser1.id, roleId: role2.id }), assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }), assignRole({ userId: normalUser1.id, roleId: role3.id }), assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }), ]); const result = await roleService.getModeratorIds({ includeAdmins: true, includeRoot: false, excludeExpire: true, }); expect(result).toEqual([adminUser1.id, modeUser1.id]); }); test('includeAdmins = false, includeRoot = true, excludeExpire = false', async () => { const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([ createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createRoot(), ]); const role1 = await createRole({ name: 'admin', isAdministrator: true }); const role2 = await createRole({ name: 'moderator', isModerator: true }); const role3 = await createRole({ name: 'normal' }); 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: modeUser1.id, roleId: role2.id }), assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }), assignRole({ userId: normalUser1.id, roleId: role3.id }), assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }), ]); const result = await roleService.getModeratorIds({ includeAdmins: false, includeRoot: true, excludeExpire: false, }); expect(result).toEqual([modeUser1.id, modeUser2.id, rootUser.id]); }); test('includeAdmins = false, includeRoot = true, excludeExpire = true', async () => { const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([ createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createRoot(), ]); const role1 = await createRole({ name: 'admin', isAdministrator: true }); const role2 = await createRole({ name: 'moderator', isModerator: true }); const role3 = await createRole({ name: 'normal' }); 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: modeUser1.id, roleId: role2.id }), assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }), assignRole({ userId: normalUser1.id, roleId: role3.id }), assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }), ]); const result = await roleService.getModeratorIds({ includeAdmins: false, includeRoot: true, excludeExpire: false, }); expect(result).toEqual([modeUser1.id, modeUser2.id, rootUser.id]); }); test('includeAdmins = true, includeRoot = true, excludeExpire = false', async () => { const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([ createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createRoot(), ]); const role1 = await createRole({ name: 'admin', isAdministrator: true }); const role2 = await createRole({ name: 'moderator', isModerator: true }); const role3 = await createRole({ name: 'normal' }); 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: modeUser1.id, roleId: role2.id }), assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }), assignRole({ userId: normalUser1.id, roleId: role3.id }), assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }), ]); const result = await roleService.getModeratorIds({ includeAdmins: true, includeRoot: true, excludeExpire: false, }); expect(result).toEqual([adminUser1.id, adminUser2.id, modeUser1.id, modeUser2.id, rootUser.id]); }); test('includeAdmins = true, includeRoot = true, excludeExpire = true', async () => { const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([ createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createRoot(), ]); const role1 = await createRole({ name: 'admin', isAdministrator: true }); const role2 = await createRole({ name: 'moderator', isModerator: true }); const role3 = await createRole({ name: 'normal' }); 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: modeUser1.id, roleId: role2.id }), assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }), assignRole({ userId: normalUser1.id, roleId: role3.id }), assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }), ]); const result = await roleService.getModeratorIds({ includeAdmins: true, includeRoot: true, excludeExpire: true, }); expect(result).toEqual([adminUser1.id, modeUser1.id, rootUser.id]); }); }); describe('getAdministratorIds', () => { test('should return only user IDs with administrator roles', async () => { const adminUser1 = await createUser(); const adminUser2 = await createUser(); const normalUser = await createUser(); const moderatorUser = await createUser(); const adminRole = await createRole({ name: 'admin', isAdministrator: true, isModerator: false }); const moderatorRole = await createRole({ name: 'moderator', isModerator: true, isAdministrator: false }); const normalRole = await createRole({ name: 'normal', isAdministrator: false, isModerator: false }); await roleService.assign(adminUser1.id, adminRole.id); await roleService.assign(adminUser2.id, adminRole.id); await roleService.assign(moderatorUser.id, moderatorRole.id); await roleService.assign(normalUser.id, normalRole.id); const adminIds = await roleService.getAdministratorIds(); // sort for deterministic order adminIds.sort(); const expectedIds = [adminUser1.id, adminUser2.id].sort(); expect(adminIds).toEqual(expectedIds); }); test('should return an empty array if no users have administrator roles', async () => { const normalUser = await createUser(); const normalRole = await createRole({ name: 'normal', isAdministrator: false }); await roleService.assign(normalUser.id, normalRole.id); const adminIds = await roleService.getAdministratorIds(); expect(adminIds).toHaveLength(0); }); test('should return an empty array if there are no administrator roles defined', async () => { await createUser(); // create user to ensure not empty db const adminIds = await roleService.getAdministratorIds(); expect(adminIds).toHaveLength(0); }); // TODO: rootユーザーは現在実装に含まれていないため、テストもそれに倣う test('should not include the root user', async () => { const rootUser = await createUser(); meta.rootUserId = rootUser.id; const adminIds = await roleService.getAdministratorIds(); expect(adminIds).not.toContain(rootUser.id); }); }); describe('conditional role', () => { test('~かつ~', async () => { const [user1, user2, user3, user4] = await Promise.all([ createUser({ isBot: true, isCat: false, isSuspended: false }), createUser({ isBot: false, isCat: true, isSuspended: false }), createUser({ isBot: true, isCat: true, isSuspended: false }), createUser({ isBot: false, isCat: false, isSuspended: true }), ]); const role1 = await createConditionalRole({ id: aidx(), type: 'isBot', }); const role2 = await createConditionalRole({ id: aidx(), type: 'isCat', }); const role3 = await createConditionalRole({ id: aidx(), type: 'isSuspended', }); const role4 = await createConditionalRole({ id: aidx(), type: 'and', values: [role1.condFormula, role2.condFormula], }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); const actual4 = await roleService.getUserRoles(user4.id); expect(actual1.some(r => r.id === role4.id)).toBe(false); expect(actual2.some(r => r.id === role4.id)).toBe(false); expect(actual3.some(r => r.id === role4.id)).toBe(true); expect(actual4.some(r => r.id === role4.id)).toBe(false); }); test('~または~', async () => { const [user1, user2, user3, user4] = await Promise.all([ createUser({ isBot: true, isCat: false, isSuspended: false }), createUser({ isBot: false, isCat: true, isSuspended: false }), createUser({ isBot: true, isCat: true, isSuspended: false }), createUser({ isBot: false, isCat: false, isSuspended: true }), ]); const role1 = await createConditionalRole({ id: aidx(), type: 'isBot', }); const role2 = await createConditionalRole({ id: aidx(), type: 'isCat', }); const role3 = await createConditionalRole({ id: aidx(), type: 'isSuspended', }); const role4 = await createConditionalRole({ id: aidx(), type: 'or', values: [role1.condFormula, role2.condFormula], }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); const actual4 = await roleService.getUserRoles(user4.id); expect(actual1.some(r => r.id === role4.id)).toBe(true); expect(actual2.some(r => r.id === role4.id)).toBe(true); expect(actual3.some(r => r.id === role4.id)).toBe(true); expect(actual4.some(r => r.id === role4.id)).toBe(false); }); test('~ではない', async () => { const [user1, user2, user3] = await Promise.all([ createUser({ isBot: true, isCat: false, isSuspended: false }), createUser({ isBot: false, isCat: true, isSuspended: false }), createUser({ isBot: true, isCat: true, isSuspended: false }), ]); const role1 = await createConditionalRole({ id: aidx(), type: 'isBot', }); const role2 = await createConditionalRole({ id: aidx(), type: 'isCat', }); const role4 = await createConditionalRole({ id: aidx(), type: 'not', value: role1.condFormula, }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); expect(actual1.some(r => r.id === role4.id)).toBe(false); expect(actual2.some(r => r.id === role4.id)).toBe(true); expect(actual3.some(r => r.id === role4.id)).toBe(false); }); test('マニュアルロールにアサイン済み', async () => { const [user1, user2, role1] = await Promise.all([ createUser(), createUser(), createRole({ name: 'manual role', }), ]); const role2 = await createConditionalRole({ id: aidx(), type: 'roleAssignedTo', roleId: role1.id, }); await roleService.assign(user2.id, role1.id); const [u1role, u2role] = await Promise.all([ roleService.getUserRoles(user1.id), roleService.getUserRoles(user2.id), ]); expect(u1role.some(r => r.id === role2.id)).toBe(false); expect(u2role.some(r => r.id === role2.id)).toBe(true); }); test('ローカルユーザのみ', async () => { const [user1, user2] = await Promise.all([ createUser({ host: null }), createUser({ host: 'example.com' }), ]); const role = await createConditionalRole({ id: aidx(), type: 'isLocal', }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); expect(actual1.some(r => r.id === role.id)).toBe(true); expect(actual2.some(r => r.id === role.id)).toBe(false); }); test('リモートユーザのみ', async () => { const [user1, user2] = await Promise.all([ createUser({ host: null }), createUser({ host: 'example.com' }), ]); const role = await createConditionalRole({ id: aidx(), type: 'isRemote', }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(true); }); test('サスペンド済みユーザである', async () => { const [user1, user2] = await Promise.all([ createUser({ isSuspended: false }), createUser({ isSuspended: true }), ]); const role = await createConditionalRole({ id: aidx(), type: 'isSuspended', }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(true); }); test('鍵アカウントユーザである', async () => { const [user1, user2] = await Promise.all([ createUser({ isLocked: false }), createUser({ isLocked: true }), ]); const role = await createConditionalRole({ id: aidx(), type: 'isLocked', }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(true); }); test('botユーザである', async () => { const [user1, user2] = await Promise.all([ createUser({ isBot: false }), createUser({ isBot: true }), ]); const role = await createConditionalRole({ id: aidx(), type: 'isBot', }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(true); }); test('猫である', async () => { const [user1, user2] = await Promise.all([ createUser({ isCat: false }), createUser({ isCat: true }), ]); const role = await createConditionalRole({ id: aidx(), type: 'isCat', }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(true); }); test('「ユーザを見つけやすくする」が有効なアカウント', async () => { const [user1, user2] = await Promise.all([ createUser({ isExplorable: false }), createUser({ isExplorable: true }), ]); const role = await createConditionalRole({ id: aidx(), type: 'isExplorable', }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(true); }); test('ユーザが作成されてから指定期間経過した', async () => { const base = new Date(); base.setMinutes(base.getMinutes() - 5); const d1 = new Date(base); const d2 = new Date(base); const d3 = new Date(base); d1.setSeconds(d1.getSeconds() - 1); d3.setSeconds(d3.getSeconds() + 1); const [user1, user2, user3] = await Promise.all([ // 4:59 createUser({ id: genAidx(d1.getTime()) }), // 5:00 createUser({ id: genAidx(d2.getTime()) }), // 5:01 createUser({ id: genAidx(d3.getTime()) }), ]); const role = await createConditionalRole({ id: aidx(), type: 'createdLessThan', // 5 minutes sec: 300, }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(false); expect(actual3.some(r => r.id === role.id)).toBe(true); }); test('ユーザが作成されてから指定期間経っていない', async () => { const base = new Date(); base.setMinutes(base.getMinutes() - 5); const d1 = new Date(base); const d2 = new Date(base); const d3 = new Date(base); d1.setSeconds(d1.getSeconds() - 1); d3.setSeconds(d3.getSeconds() + 1); const [user1, user2, user3] = await Promise.all([ // 4:59 createUser({ id: genAidx(d1.getTime()) }), // 5:00 createUser({ id: genAidx(d2.getTime()) }), // 5:01 createUser({ id: genAidx(d3.getTime()) }), ]); const role = await createConditionalRole({ id: aidx(), type: 'createdMoreThan', // 5 minutes sec: 300, }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); expect(actual1.some(r => r.id === role.id)).toBe(true); expect(actual2.some(r => r.id === role.id)).toBe(false); expect(actual3.some(r => r.id === role.id)).toBe(false); }); test('フォロワー数が指定値以下', async () => { const [user1, user2, user3] = await Promise.all([ createUser({ followersCount: 99 }), createUser({ followersCount: 100 }), createUser({ followersCount: 101 }), ]); const role = await createConditionalRole({ id: aidx(), type: 'followersLessThanOrEq', value: 100, }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); expect(actual1.some(r => r.id === role.id)).toBe(true); expect(actual2.some(r => r.id === role.id)).toBe(true); expect(actual3.some(r => r.id === role.id)).toBe(false); }); test('フォロワー数が指定値以下', async () => { const [user1, user2, user3] = await Promise.all([ createUser({ followersCount: 99 }), createUser({ followersCount: 100 }), createUser({ followersCount: 101 }), ]); const role = await createConditionalRole({ id: aidx(), type: 'followersMoreThanOrEq', value: 100, }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(true); expect(actual3.some(r => r.id === role.id)).toBe(true); }); test('フォロー数が指定値以下', async () => { const [user1, user2, user3] = await Promise.all([ createUser({ followingCount: 99 }), createUser({ followingCount: 100 }), createUser({ followingCount: 101 }), ]); const role = await createConditionalRole({ id: aidx(), type: 'followingLessThanOrEq', value: 100, }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); expect(actual1.some(r => r.id === role.id)).toBe(true); expect(actual2.some(r => r.id === role.id)).toBe(true); expect(actual3.some(r => r.id === role.id)).toBe(false); }); test('フォロー数が指定値以上', async () => { const [user1, user2, user3] = await Promise.all([ createUser({ followingCount: 99 }), createUser({ followingCount: 100 }), createUser({ followingCount: 101 }), ]); const role = await createConditionalRole({ id: aidx(), type: 'followingMoreThanOrEq', value: 100, }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(true); expect(actual3.some(r => r.id === role.id)).toBe(true); }); test('ノート数が指定値以下', async () => { const [user1, user2, user3] = await Promise.all([ createUser({ notesCount: 9 }), createUser({ notesCount: 10 }), createUser({ notesCount: 11 }), ]); const role = await createConditionalRole({ id: aidx(), type: 'notesLessThanOrEq', value: 10, }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); expect(actual1.some(r => r.id === role.id)).toBe(true); expect(actual2.some(r => r.id === role.id)).toBe(true); expect(actual3.some(r => r.id === role.id)).toBe(false); }); test('ノート数が指定値以上', async () => { const [user1, user2, user3] = await Promise.all([ createUser({ notesCount: 9 }), createUser({ notesCount: 10 }), createUser({ notesCount: 11 }), ]); const role = await createConditionalRole({ id: aidx(), type: 'notesMoreThanOrEq', value: 10, }); const actual1 = await roleService.getUserRoles(user1.id); const actual2 = await roleService.getUserRoles(user2.id); const actual3 = await roleService.getUserRoles(user3.id); expect(actual1.some(r => r.id === role.id)).toBe(false); expect(actual2.some(r => r.id === role.id)).toBe(true); expect(actual3.some(r => r.id === role.id)).toBe(true); }); }); describe('assign', () => { test('公開ロールの場合は通知される', async () => { const user = await createUser(); const role = await createRole({ isPublic: true, name: 'a', }); await roleService.assign(user.id, role.id); clock.uninstall(); await setTimeout(100); const assignments = await roleAssignmentsRepository.find({ where: { userId: user.id, roleId: role.id, }, }); expect(assignments).toHaveLength(1); expect(notificationService.createNotification).toHaveBeenCalled(); expect(notificationService.createNotification.mock.lastCall![0]).toBe(user.id); expect(notificationService.createNotification.mock.lastCall![1]).toBe('roleAssigned'); expect(notificationService.createNotification.mock.lastCall![2]).toEqual({ roleId: role.id, }); }); test('非公開ロールの場合は通知されない', async () => { const user = await createUser(); const role = await createRole({ isPublic: false, name: 'a', }); await roleService.assign(user.id, role.id); clock.uninstall(); await setTimeout(100); const assignments = await roleAssignmentsRepository.find({ where: { userId: user.id, roleId: role.id, }, }); expect(assignments).toHaveLength(1); expect(notificationService.createNotification).not.toHaveBeenCalled(); }); }); });