summaryrefslogtreecommitdiff
path: root/packages/backend/test/unit/RoleService.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/backend/test/unit/RoleService.ts')
-rw-r--r--packages/backend/test/unit/RoleService.ts498
1 files changed, 432 insertions, 66 deletions
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index 19d03570e0..ec441735d7 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -3,6 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+
process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals';
@@ -20,6 +22,7 @@ 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 { sleep } from '../utils.js';
import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock';
@@ -52,12 +55,26 @@ describe('RoleService', () => {
id: genAidx(Date.now()),
updatedAt: new Date(),
lastUsedAt: new Date(),
+ name: '',
description: '',
...data,
})
.then(x => rolesRepository.findOneByOrFail(x.identifiers[0]));
}
+ function createConditionalRole(condFormula: RoleCondFormulaValue, data: Partial<MiRole> = {}) {
+ return createRole({
+ name: `[conditional] ${condFormula.type}`,
+ target: 'conditional',
+ condFormula: condFormula,
+ ...data,
+ });
+ }
+
+ function aidx() {
+ return genAidx(Date.now());
+ }
+
beforeEach(async () => {
clock = lolex.install({
now: new Date(),
@@ -73,6 +90,7 @@ describe('RoleService', () => {
CacheService,
IdService,
GlobalEventService,
+ UserEntityService,
{
provide: NotificationService,
useFactory: () => ({
@@ -209,15 +227,9 @@ describe('RoleService', () => {
expect(result.driveCapacityMb).toBe(100);
});
- test('conditional role', async () => {
- const user1 = await createUser({
- id: genAidx(Date.now() - (1000 * 60 * 60 * 24 * 365)),
- });
- const user2 = await createUser({
- id: genAidx(Date.now() - (1000 * 60 * 60 * 24 * 365)),
- followersCount: 10,
- });
- await createRole({
+ test('expired role', async () => {
+ const user = await createUser();
+ const role = await createRole({
name: 'a',
policies: {
canManageCustomEmojis: {
@@ -226,35 +238,133 @@ describe('RoleService', () => {
value: true,
},
},
- target: 'conditional',
- condFormula: {
- id: '232a4221-9816-49a6-a967-ae0fac52ec5e',
- type: 'and',
- values: [{
- id: '2a37ef43-2d93-4c4d-87f6-f2fdb7d9b530',
- type: 'followersMoreThanOrEq',
- value: 10,
- }, {
- id: '1bd67839-b126-4f92-bad0-4e285dab453b',
- type: 'createdMoreThan',
- sec: 60 * 60 * 24 * 7,
- }],
- },
});
-
+ await roleService.assign(user.id, role.id, new Date(Date.now() + (1000 * 60 * 60 * 24)));
metaService.fetch.mockResolvedValue({
policies: {
canManageCustomEmojis: false,
},
} as any);
- const user1Policies = await roleService.getUserPolicies(user1.id);
- const user2Policies = await roleService.getUserPolicies(user2.id);
- expect(user1Policies.canManageCustomEmojis).toBe(false);
- expect(user2Policies.canManageCustomEmojis).toBe(true);
+ 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 sleep(100);
+
+ const resultAfter25hAgain = await roleService.getUserPolicies(user.id);
+ expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
+ });
+ });
+
+ 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 () => {
+ test('マニュアルロールにアサイン済み', async () => {
const [user1, user2, role1] = await Promise.all([
createUser(),
createUser(),
@@ -262,15 +372,10 @@ describe('RoleService', () => {
name: 'manual role',
}),
]);
- const role2 = await createRole({
- name: 'conditional role',
- target: 'conditional',
- condFormula: {
- // idはバックエンドのロジックに必要ない?
- id: 'bdc612bd-9d54-4675-ae83-0499c82ea670',
- type: 'roleAssignedTo',
- roleId: role1.id,
- },
+ const role2 = await createConditionalRole({
+ id: aidx(),
+ type: 'roleAssignedTo',
+ roleId: role1.id,
});
await roleService.assign(user2.id, role1.id);
@@ -282,41 +387,302 @@ describe('RoleService', () => {
expect(u2role.some(r => r.id === role2.id)).toBe(true);
});
- test('expired role', async () => {
- const user = await createUser();
- const role = await createRole({
- name: 'a',
- policies: {
- canManageCustomEmojis: {
- useDefault: false,
- priority: 0,
- value: true,
- },
- },
+ test('ローカルユーザのみ', async () => {
+ const [user1, user2] = await Promise.all([
+ createUser({ host: null }),
+ createUser({ host: 'example.com' }),
+ ]);
+ const role = await createConditionalRole({
+ id: aidx(),
+ type: 'isLocal',
});
- await roleService.assign(user.id, role.id, new Date(Date.now() + (1000 * 60 * 60 * 24)));
- metaService.fetch.mockResolvedValue({
- policies: {
- canManageCustomEmojis: false,
- },
- } as any);
- const result = await roleService.getUserPolicies(user.id);
- expect(result.canManageCustomEmojis).toBe(true);
+ 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);
+ });
- clock.tick('25:00:00');
+ test('リモートユーザのみ', async () => {
+ const [user1, user2] = await Promise.all([
+ createUser({ host: null }),
+ createUser({ host: 'example.com' }),
+ ]);
+ const role = await createConditionalRole({
+ id: aidx(),
+ type: 'isRemote',
+ });
- const resultAfter25h = await roleService.getUserPolicies(user.id);
- expect(resultAfter25h.canManageCustomEmojis).toBe(false);
+ 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);
+ });
- await roleService.assign(user.id, role.id);
+ test('サスペンド済みユーザである', async () => {
+ const [user1, user2] = await Promise.all([
+ createUser({ isSuspended: false }),
+ createUser({ isSuspended: true }),
+ ]);
+ const role = await createConditionalRole({
+ id: aidx(),
+ type: 'isSuspended',
+ });
- // ストリーミング経由で反映されるまでちょっと待つ
- clock.uninstall();
- await sleep(100);
+ 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);
+ });
- const resultAfter25hAgain = await roleService.getUserPolicies(user.id);
- expect(resultAfter25hAgain.canManageCustomEmojis).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);
});
});