1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-05 03:05:52 +02:00

enhance(backend/test): Migrate tests to vitest (#16935)

* wip

* update fake-timers and migrate

* fix

* remove jest-mock

* fix

* fix

* fix

* fix

* attempt to fix unit tests

* attempt to fix e2e tests

* fix federation test [ci skip]

* attempt to fix e2e tests

* fix typecheck

* fix unit tests

* fix

* attempt to fix e2e

* fix

* Revert "attempt to fix e2e"

This reverts commit b7b7b05d85.

* attempt to fix e2e

* revert attempt to fix e2e

* update deps

* update vitest

* migrate

* attempt to fix e2e

* update

* fix

* remove vite swc plugin as oxc parser can handle decorators

* attempt to fix drive/files/create test

* Revert "attempt to fix drive/files/create test"

This reverts commit 4715153375.

* fix: エンドポイントにまつわるテストをunitからe2eに移動

* attempt to fix e2e

* remove swc

* attempt to fix e2e

* Revert "attempt to fix e2e"

This reverts commit 9fb86a4076.

* add logs for debug

* attempt to fix e2e

* Partially revert "attempt to fix e2e"

This reverts commit fb0008c85a.

* attempt to fix test

* fix: attempt to fix test

* Revert "fix: attempt to fix test"

This reverts commit ed2f5c40e8.

* Revert "attempt to fix test"

This reverts commit d7329c46f1.

* attempt to fix e2e

* fix: surpass eventemitter warning by increasing defaultMaxListeners

* attempt to fix e2e

* fix

* fix e2e not ending properly

* exp: add hanging-process reporter for investigation

* Revert "exp: add hanging-process reporter for investigation"

This reverts commit 26851f8282.

* update changelog
This commit is contained in:
かっこかり
2026-04-20 14:57:29 +09:00
committed by GitHub
parent 1391269a67
commit 6d15fe32d0
112 changed files with 755 additions and 3068 deletions

View File

@@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { describe, jest } from '@jest/globals';
import { describe, expect, beforeAll, afterAll, beforeEach, afterEach, test, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import { randomString } from '../utils.js';
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
@@ -42,9 +43,9 @@ describe('AbuseReportNotificationService', () => {
let systemWebhooksRepository: SystemWebhooksRepository;
let abuseReportNotificationRecipientRepository: AbuseReportNotificationRecipientRepository;
let idService: IdService;
let roleService: jest.Mocked<RoleService>;
let emailService: jest.Mocked<EmailService>;
let webhookService: jest.Mocked<SystemWebhookService>;
let roleService: Mocked<RoleService>;
let emailService: Mocked<EmailService>;
let webhookService: Mocked<SystemWebhookService>;
// --------------------------------------------------------------------------------------
@@ -107,10 +108,10 @@ describe('AbuseReportNotificationService', () => {
AbuseReportNotificationService,
IdService,
{
provide: RoleService, useFactory: () => ({ getModeratorIds: jest.fn() }),
provide: RoleService, useFactory: () => ({ getModeratorIds: vi.fn() }),
},
{
provide: SystemWebhookService, useFactory: () => ({ enqueueSystemWebhook: jest.fn() }),
provide: SystemWebhookService, useFactory: () => ({ enqueueSystemWebhook: vi.fn() }),
},
{
provide: UserEntityService, useFactory: () => ({
@@ -119,16 +120,16 @@ describe('AbuseReportNotificationService', () => {
}),
},
{
provide: EmailService, useFactory: () => ({ sendEmail: jest.fn() }),
provide: EmailService, useFactory: () => ({ sendEmail: vi.fn() }),
},
{
provide: MetaService, useFactory: () => ({ fetch: jest.fn() }),
provide: MetaService, useFactory: () => ({ fetch: vi.fn() }),
},
{
provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }),
},
{
provide: GlobalEventService, useFactory: () => ({ publishAdminStream: jest.fn() }),
provide: GlobalEventService, useFactory: () => ({ publishAdminStream: vi.fn() }),
},
],
})
@@ -141,9 +142,9 @@ describe('AbuseReportNotificationService', () => {
service = app.get(AbuseReportNotificationService);
idService = app.get(IdService);
roleService = app.get(RoleService) as jest.Mocked<RoleService>;
emailService = app.get<EmailService>(EmailService) as jest.Mocked<EmailService>;
webhookService = app.get<SystemWebhookService>(SystemWebhookService) as jest.Mocked<SystemWebhookService>;
roleService = app.get(RoleService) as Mocked<RoleService>;
emailService = app.get<EmailService>(EmailService) as Mocked<EmailService>;
webhookService = app.get<SystemWebhookService>(SystemWebhookService) as Mocked<SystemWebhookService>;
app.enableShutdownHooks();
});

View File

@@ -5,8 +5,9 @@
process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals';
import { ModuleMocker } from 'jest-mock';
import { describe, expect, beforeEach, afterEach, test, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { mockDeep } from 'vitest-mock-extended';
import { Test } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js';
import { AnnouncementService } from '@/core/AnnouncementService.js';
@@ -26,9 +27,6 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { secureRndstr } from '@/misc/secure-rndstr.js';
import type { TestingModule } from '@nestjs/testing';
import type { MockMetadata } from 'jest-mock';
const moduleMocker = new ModuleMocker(global);
describe('AnnouncementService', () => {
let app: TestingModule;
@@ -36,8 +34,8 @@ describe('AnnouncementService', () => {
let usersRepository: UsersRepository;
let announcementsRepository: AnnouncementsRepository;
let announcementReadsRepository: AnnouncementReadsRepository;
let globalEventService: jest.Mocked<GlobalEventService>;
let moderationLogService: jest.Mocked<ModerationLogService>;
let globalEventService: Mocked<GlobalEventService>;
let moderationLogService: Mocked<ModerationLogService>;
function createUser(data: Partial<MiUser> = {}) {
const un = secureRndstr(16);
@@ -76,17 +74,15 @@ describe('AnnouncementService', () => {
.useMocker((token) => {
if (token === GlobalEventService) {
return {
publishMainStream: jest.fn(),
publishBroadcastStream: jest.fn(),
publishMainStream: vi.fn(),
publishBroadcastStream: vi.fn(),
};
} else if (token === ModerationLogService) {
return {
log: jest.fn(),
log: vi.fn(),
};
} else if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
return mockDeep<typeof token>();
}
})
.compile();
@@ -97,8 +93,8 @@ describe('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>;
globalEventService = app.get<GlobalEventService>(GlobalEventService) as Mocked<GlobalEventService>;
moderationLogService = app.get<ModerationLogService>(ModerationLogService) as Mocked<ModerationLogService>;
});
afterEach(async () => {
@@ -203,7 +199,7 @@ describe('AnnouncementService', () => {
});
});
describe('read', () => {
describe.todo('read', () => {
// TODO
});
});

View File

@@ -4,6 +4,7 @@
*/
import * as assert from 'assert';
import { describe, test, beforeAll } from 'vitest';
import { Test } from '@nestjs/testing';
import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { afterAll, beforeAll, beforeEach, describe, expect, jest } from '@jest/globals';
import { afterAll, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import { Response } from 'node-fetch';
import {
@@ -22,8 +23,8 @@ import { LoggerService } from '@/core/LoggerService.js';
describe('CaptchaService', () => {
let app: TestingModule;
let service: CaptchaService;
let httpRequestService: jest.Mocked<HttpRequestService>;
let metaService: jest.Mocked<MetaService>;
let httpRequestService: Mocked<HttpRequestService>;
let metaService: Mocked<MetaService>;
beforeAll(async () => {
app = await Test.createTestingModule({
@@ -34,12 +35,12 @@ describe('CaptchaService', () => {
CaptchaService,
LoggerService,
{
provide: HttpRequestService, useFactory: () => ({ send: jest.fn() }),
provide: HttpRequestService, useFactory: () => ({ send: vi.fn() }),
},
{
provide: MetaService, useFactory: () => ({
fetch: jest.fn(),
update: jest.fn(),
fetch: vi.fn(),
update: vi.fn(),
}),
},
],
@@ -48,8 +49,8 @@ describe('CaptchaService', () => {
app.enableShutdownHooks();
service = app.get(CaptchaService);
httpRequestService = app.get(HttpRequestService) as jest.Mocked<HttpRequestService>;
metaService = app.get(MetaService) as jest.Mocked<MetaService>;
httpRequestService = app.get(HttpRequestService) as Mocked<HttpRequestService>;
metaService = app.get(MetaService) as Mocked<MetaService>;
});
beforeEach(() => {

View File

@@ -5,7 +5,7 @@
/* eslint-disable */
import { afterEach, beforeEach, describe, expect } from '@jest/globals';
import { afterEach, beforeEach, describe, expect, beforeAll, afterAll, test } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js';
import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -5,7 +5,7 @@
/* eslint-disable */
import { afterEach, beforeEach, describe, expect } from '@jest/globals';
import { afterEach, beforeEach, describe, expect, beforeAll, afterAll, test } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js';
import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { afterEach, beforeAll, describe, test } from '@jest/globals';
import { afterEach, beforeAll, describe, test, expect } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';

View File

@@ -5,6 +5,7 @@
process.env.NODE_ENV = 'test';
import { afterAll, beforeAll, beforeEach, describe, test, expect } from 'vitest';
import { Test } from '@nestjs/testing';
import {
DeleteObjectCommand,

View File

@@ -5,7 +5,8 @@
process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test } from '@nestjs/testing';
import { Redis } from 'ioredis';
import type { TestingModule } from '@nestjs/testing';
@@ -18,22 +19,32 @@ import { UtilityService } from '@/core/UtilityService.js';
import { IdService } from '@/core/IdService.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;
function createMockRedis() {
const store = new Map<string, string>();
const del = vi.fn((key: string) => {
const existed = store.delete(key);
return Promise.resolve(existed ? 1 : 0);
});
return set;
const set = vi.fn((key: string, value: string, ...args: any[]) => {
const prev = store.get(key) ?? null;
store.set(key, value);
// ioredis: SET key value ... GET => returns old value or null
const hasGet = args.some(a => typeof a === 'string' && a.toUpperCase() === 'GET');
return Promise.resolve(hasGet ? prev : 'OK');
});
return { set, del };
}
describe('FetchInstanceMetadataService', () => {
let app: TestingModule;
let fetchInstanceMetadataService: jest.Mocked<FetchInstanceMetadataService>;
let federatedInstanceService: jest.Mocked<FederatedInstanceService>;
let httpRequestService: jest.Mocked<HttpRequestService>;
let redisClient: jest.Mocked<Redis>;
let fetchInstanceMetadataService: Mocked<FetchInstanceMetadataService>;
let federatedInstanceService: Mocked<FederatedInstanceService>;
let httpRequestService: Mocked<HttpRequestService>;
let redisClient: Mocked<Redis>;
beforeEach(async () => {
app = await Test
@@ -50,11 +61,11 @@ describe('FetchInstanceMetadataService', () => {
})
.useMocker((token) => {
if (token === HttpRequestService) {
return { getJson: jest.fn(), getHtml: jest.fn(), send: jest.fn() };
return { getJson: vi.fn(), getHtml: vi.fn(), send: vi.fn() };
} else if (token === FederatedInstanceService) {
return { fetchOrRegister: jest.fn() };
return { fetchOrRegister: vi.fn() };
} else if (token === DI.redis) {
return mockRedis;
return createMockRedis();
}
return null;
})
@@ -62,23 +73,24 @@ describe('FetchInstanceMetadataService', () => {
app.enableShutdownHooks();
fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService) as jest.Mocked<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>;
fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService) as Mocked<FetchInstanceMetadataService>;
federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService) as Mocked<FederatedInstanceService>;
redisClient = app.get<Redis>(DI.redis) as Mocked<Redis>;
httpRequestService = app.get<HttpRequestService>(HttpRequestService) as Mocked<HttpRequestService>;
});
afterEach(async () => {
await app.close();
vi.resetAllMocks();
vi.clearAllMocks();
});
test('Lock and update', async () => {
redisClient.set = mockRedis();
const now = Date.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');
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
const tryLockSpy = vi.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = vi.spyOn(fetchInstanceMetadataService, 'unlock');
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
expect(tryLockSpy).toHaveBeenCalledTimes(1);
@@ -88,12 +100,11 @@ describe('FetchInstanceMetadataService', () => {
});
test('Lock and don\'t update', async () => {
redisClient.set = mockRedis();
const now = Date.now();
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
const tryLockSpy = vi.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = vi.spyOn(fetchInstanceMetadataService, 'unlock');
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
expect(tryLockSpy).toHaveBeenCalledTimes(1);
@@ -103,13 +114,12 @@ describe('FetchInstanceMetadataService', () => {
});
test('Do nothing when lock not acquired', async () => {
redisClient.set = mockRedis();
const now = Date.now();
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
await fetchInstanceMetadataService.tryLock('example.com');
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
const tryLockSpy = vi.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = vi.spyOn(fetchInstanceMetadataService, 'unlock');
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
expect(tryLockSpy).toHaveBeenCalledTimes(1);
@@ -119,13 +129,12 @@ describe('FetchInstanceMetadataService', () => {
});
test('Do when lock not acquired but forced', async () => {
redisClient.set = mockRedis();
const now = Date.now();
federatedInstanceService.fetchOrRegister.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
await fetchInstanceMetadataService.tryLock('example.com');
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
const tryLockSpy = vi.spyOn(fetchInstanceMetadataService, 'tryLock');
const unlockSpy = vi.spyOn(fetchInstanceMetadataService, 'unlock');
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any, true);
expect(tryLockSpy).toHaveBeenCalledTimes(0);

View File

@@ -8,23 +8,20 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing';
import { afterAll, beforeAll, describe, test } from '@jest/globals';
import { afterAll, beforeAll, describe, test } from 'vitest';
import { mockDeep } from 'vitest-mock-extended';
import { GlobalModule } from '@/GlobalModule.js';
import { FileInfo, FileInfoService } from '@/core/FileInfoService.js';
//import { DI } from '@/di-symbols.js';
import { AiService } from '@/core/AiService.js';
import { LoggerService } from '@/core/LoggerService.js';
import type { TestingModule } from '@nestjs/testing';
import type { MockMetadata } from 'jest-mock';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const resources = `${_dirname}/../resources`;
const moduleMocker = new ModuleMocker(global);
describe('FileInfoService', () => {
let app: TestingModule;
let fileInfoService: FileInfoService;
@@ -54,9 +51,7 @@ describe('FileInfoService', () => {
// return { };
//}
if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
return mockDeep<typeof token>();
}
})
.compile();

View File

@@ -5,6 +5,7 @@
*/
import { Test, TestingModule } from '@nestjs/testing';
import { afterAll, afterEach, beforeEach, describe, expect, test } from 'vitest';
import { FlashService } from '@/core/FlashService.js';
import { IdService } from '@/core/IdService.js';
import { FlashLikesRepository, FlashsRepository, MiFlash, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js';

View File

@@ -5,7 +5,7 @@
process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals';
import { afterAll, beforeAll, describe, test, expect, vi } from 'vitest';
import { Test } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js';
import { DI } from '@/di-symbols.js';
@@ -40,7 +40,7 @@ describe('MetaService', () => {
test('fetch (cache)', async () => {
const db = app.get<DataSource>(DI.db);
const spy = jest.spyOn(db, 'transaction');
const spy = vi.spyOn(db, 'transaction');
const result = await metaService.fetch();
@@ -50,7 +50,7 @@ describe('MetaService', () => {
test('fetch (force)', async () => {
const db = app.get<DataSource>(DI.db);
const spy = jest.spyOn(db, 'transaction');
const spy = vi.spyOn(db, 'transaction');
const result = await metaService.fetch(true);

View File

@@ -5,6 +5,7 @@
import * as assert from 'assert';
import * as mfm from 'mfm-js';
import { beforeAll, describe, test } from 'vitest';
import { Test } from '@nestjs/testing';
import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { beforeAll, describe, test, expect } from 'vitest';
import { Test } from '@nestjs/testing';
import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -4,6 +4,7 @@
*/
import * as assert from 'assert';
import { beforeAll, describe, test } from 'vitest';
import { Test } from '@nestjs/testing';
import { CoreModule } from '@/core/CoreModule.js';

View File

@@ -5,11 +5,11 @@
process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals';
import { afterAll, beforeAll, describe, test, expect, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test } from '@nestjs/testing';
import { ModuleMocker } from 'jest-mock';
import { mockDeep } from 'vitest-mock-extended';
import type { TestingModule } from '@nestjs/testing';
import type { MockMetadata } from 'jest-mock';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { IdService } from '@/core/IdService.js';
@@ -19,12 +19,10 @@ import { SystemAccountService } from '@/core/SystemAccountService.js';
import { GlobalModule } from '@/GlobalModule.js';
import { UtilityService } from '@/core/UtilityService.js';
const moduleMocker = new ModuleMocker(global);
describe('RelayService', () => {
let app: TestingModule;
let relayService: RelayService;
let queueService: jest.Mocked<QueueService>;
let queueService: Mocked<QueueService>;
beforeAll(async () => {
app = await Test.createTestingModule({
@@ -42,12 +40,10 @@ describe('RelayService', () => {
})
.useMocker((token) => {
if (token === QueueService) {
return { deliver: jest.fn() };
return { deliver: vi.fn() };
}
if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
return mockDeep<typeof token>();
}
})
.compile();
@@ -55,7 +51,7 @@ describe('RelayService', () => {
app.enableShutdownHooks();
relayService = app.get<RelayService>(RelayService);
queueService = app.get<QueueService>(QueueService) as jest.Mocked<QueueService>;
queueService = app.get<QueueService>(QueueService) as Mocked<QueueService>;
});
afterAll(async () => {

View File

@@ -6,12 +6,12 @@
process.env.NODE_ENV = 'test';
import { setTimeout } from 'node:timers/promises';
import { describe, jest } from '@jest/globals';
import { ModuleMocker } from 'jest-mock';
import { describe, beforeEach, afterEach, test, expect, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { mockDeep } from 'vitest-mock-extended';
import { Test } from '@nestjs/testing';
import * as lolex from '@sinonjs/fake-timers';
import type { TestingModule } from '@nestjs/testing';
import type { MockMetadata } from 'jest-mock';
import { GlobalModule } from '@/GlobalModule.js';
import { RoleService } from '@/core/RoleService.js';
import {
@@ -34,16 +34,14 @@ 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<MiMeta>;
let notificationService: jest.Mocked<NotificationService>;
let meta: Mocked<MiMeta>;
let notificationService: Mocked<NotificationService>;
let clock: lolex.Clock;
async function createUser(data: Partial<MiUser> = {}) {
@@ -123,7 +121,7 @@ describe('RoleService', () => {
{
provide: NotificationService,
useFactory: () => ({
createNotification: jest.fn(),
createNotification: vi.fn(),
}),
},
{
@@ -134,12 +132,10 @@ describe('RoleService', () => {
})
.useMocker((token) => {
if (token === MetaService) {
return { fetch: jest.fn() };
return { fetch: vi.fn() };
}
if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
return mockDeep<typeof token>();
}
})
.compile();
@@ -151,8 +147,8 @@ describe('RoleService', () => {
rolesRepository = app.get<RolesRepository>(DI.rolesRepository);
roleAssignmentsRepository = app.get<RoleAssignmentsRepository>(DI.roleAssignmentsRepository);
meta = app.get<MiMeta>(DI.meta) as jest.Mocked<MiMeta>;
notificationService = app.get<NotificationService>(NotificationService) as jest.Mocked<NotificationService>;
meta = app.get<MiMeta>(DI.meta) as Mocked<MiMeta>;
notificationService = app.get<NotificationService>(NotificationService) as Mocked<NotificationService>;
await roleService.onModuleInit();
});

View File

@@ -5,6 +5,7 @@
process.env.NODE_ENV = 'test';
import { afterAll, beforeAll, beforeEach, describe, test, expect } from 'vitest';
import { Test } from '@nestjs/testing';
import {
CompleteMultipartUploadCommand,

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { afterAll, afterEach, beforeAll, describe, expect, test } from '@jest/globals';
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import type { Index, Meilisearch } from 'meilisearch';
import { type Config, loadConfig } from '@/config.js';

View File

@@ -4,12 +4,12 @@
*/
import { IncomingHttpHeaders } from 'node:http';
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, jest, test } from '@jest/globals';
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { mockDeep } from 'vitest-mock-extended';
import { Test, TestingModule } from '@nestjs/testing';
import { FastifyReply, FastifyRequest } from 'fastify';
import { AuthenticationResponseJSON } from '@simplewebauthn/types';
import { HttpHeader } from 'fastify/types/utils.js';
import { MockMetadata, ModuleMocker } from 'jest-mock';
import { MiUser } from '@/models/User.js';
import { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
@@ -22,8 +22,6 @@ import { WebAuthnService } from '@/core/WebAuthnService.js';
import { SigninService } from '@/server/api/SigninService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
const moduleMocker = new ModuleMocker(global);
class FakeLimiter {
public async limit() {
return;
@@ -95,9 +93,7 @@ describe('SigninWithPasskeyApiService', () => {
],
}).useMocker((token) => {
if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
return mockDeep<typeof token>();
}
}).compile();
passkeyApiService = app.get<SigninWithPasskeyApiService>(SigninWithPasskeyApiService);
@@ -112,7 +108,7 @@ describe('SigninWithPasskeyApiService', () => {
FakeWebauthnVerify = async () => {
return uid;
};
jest.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication').mockImplementation(FakeWebauthnVerify);
vi.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication').mockImplementation(FakeWebauthnVerify);
const dummyUser = {
id: uid, username: uid, usernameLower: uid.toLowerCase(), uri: null, host: null,
@@ -159,7 +155,7 @@ describe('SigninWithPasskeyApiService', () => {
it('Should return 403 When Challenge Verify fail', async () => {
const req = new DummyFastifyRequest({ context: 'misskey-1234', credential: { dummy: [] } }) as ApiFastifyRequestType;
const res = new DummyFastifyReply() as FastifyReply;
jest.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication')
vi.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication')
.mockImplementation(async () => {
throw new IdentifiableError('THIS_ERROR_CODE_SHOULD_BE_FORWARDED');
});

View File

@@ -5,7 +5,8 @@
*/
import { setTimeout } from 'node:timers/promises';
import { afterEach, beforeEach, describe, expect, jest } from '@jest/globals';
import { afterEach, beforeEach, afterAll, beforeAll, describe, test, expect, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import { randomString } from '../utils.js';
import { MiUser } from '@/models/User.js';
@@ -29,7 +30,7 @@ describe('SystemWebhookService', () => {
let usersRepository: UsersRepository;
let systemWebhooksRepository: SystemWebhooksRepository;
let idService: IdService;
let queueService: jest.Mocked<QueueService>;
let queueService: Mocked<QueueService>;
// --------------------------------------------------------------------------------------
@@ -73,7 +74,7 @@ describe('SystemWebhookService', () => {
LoggerService,
GlobalEventService,
{
provide: QueueService, useFactory: () => ({ systemWebhookDeliver: jest.fn() }),
provide: QueueService, useFactory: () => ({ systemWebhookDeliver: vi.fn() }),
},
{
provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }),
@@ -87,7 +88,7 @@ describe('SystemWebhookService', () => {
service = app.get(SystemWebhookService);
idService = app.get(IdService);
queueService = app.get(QueueService) as jest.Mocked<QueueService>;
queueService = app.get(QueueService) as Mocked<QueueService>;
app.enableShutdownHooks();
}

View File

@@ -4,7 +4,7 @@
*/
import { Test, TestingModule } from '@nestjs/testing';
import { describe, jest, test } from '@jest/globals';
import { describe, beforeEach, beforeAll, afterEach, afterAll, vi, test, expect } from 'vitest';
import { In } from 'typeorm';
import { UserSearchService } from '@/core/UserSearchService.js';
import { FollowingsRepository, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js';
@@ -92,7 +92,7 @@ describe('UserSearchService', () => {
providers: [
UserSearchService,
{
provide: UserEntityService, useFactory: jest.fn(() => ({
provide: UserEntityService, useFactory: vi.fn(() => ({
// とりあえずIDが返れば確認が出来るので
packMany: (value: any) => value,
})),

View File

@@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { afterEach, beforeEach, describe, expect, jest } from '@jest/globals';
import { afterEach, beforeEach, describe, expect, test, beforeAll, afterAll, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import { randomString } from '../utils.js';
import { MiUser } from '@/models/User.js';
@@ -25,7 +26,7 @@ describe('UserWebhookService', () => {
let usersRepository: UsersRepository;
let userWebhooksRepository: WebhooksRepository;
let idService: IdService;
let queueService: jest.Mocked<QueueService>;
let queueService: Mocked<QueueService>;
// --------------------------------------------------------------------------------------
@@ -70,7 +71,7 @@ describe('UserWebhookService', () => {
LoggerService,
GlobalEventService,
{
provide: QueueService, useFactory: () => ({ userWebhookDeliver: jest.fn() }),
provide: QueueService, useFactory: () => ({ userWebhookDeliver: vi.fn() }),
},
],
})
@@ -81,7 +82,7 @@ describe('UserWebhookService', () => {
service = app.get(UserWebhookService);
idService = app.get(IdService);
queueService = app.get(QueueService) as jest.Mocked<QueueService>;
queueService = app.get(QueueService) as Mocked<QueueService>;
app.enableShutdownHooks();
}

View File

@@ -5,7 +5,8 @@
*/
import { Test, TestingModule } from '@nestjs/testing';
import { beforeAll, describe, jest } from '@jest/globals';
import { beforeAll, afterAll, beforeEach, afterEach, test, expect, describe, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { WebhookTestService } from '@/core/WebhookTestService.js';
import { UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
@@ -24,9 +25,9 @@ describe('WebhookTestService', () => {
let usersRepository: UsersRepository;
let userProfilesRepository: UserProfilesRepository;
let queueService: jest.Mocked<QueueService>;
let userWebhookService: jest.Mocked<UserWebhookService>;
let systemWebhookService: jest.Mocked<SystemWebhookService>;
let queueService: Mocked<QueueService>;
let userWebhookService: Mocked<UserWebhookService>;
let systemWebhookService: Mocked<SystemWebhookService>;
let idService: IdService;
let root: MiUser;
@@ -59,23 +60,23 @@ describe('WebhookTestService', () => {
IdService,
{
provide: CustomEmojiService, useFactory: () => ({
populateEmojis: jest.fn(),
populateEmojis: vi.fn(),
}),
},
{
provide: QueueService, useFactory: () => ({
systemWebhookDeliver: jest.fn(),
userWebhookDeliver: jest.fn(),
systemWebhookDeliver: vi.fn(),
userWebhookDeliver: vi.fn(),
}),
},
{
provide: UserWebhookService, useFactory: () => ({
fetchWebhooks: jest.fn(),
fetchWebhooks: vi.fn(),
}),
},
{
provide: SystemWebhookService, useFactory: () => ({
fetchSystemWebhooks: jest.fn(),
fetchSystemWebhooks: vi.fn(),
}),
},
],
@@ -86,9 +87,9 @@ describe('WebhookTestService', () => {
service = app.get(WebhookTestService);
idService = app.get(IdService);
queueService = app.get(QueueService) as jest.Mocked<QueueService>;
userWebhookService = app.get(UserWebhookService) as jest.Mocked<UserWebhookService>;
systemWebhookService = app.get(SystemWebhookService) as jest.Mocked<SystemWebhookService>;
queueService = app.get(QueueService) as Mocked<QueueService>;
userWebhookService = app.get(UserWebhookService) as Mocked<UserWebhookService>;
systemWebhookService = app.get(SystemWebhookService) as Mocked<SystemWebhookService>;
app.enableShutdownHooks();
});

View File

@@ -9,8 +9,8 @@ import * as assert from 'assert';
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { describe, beforeAll, beforeEach, test, vi } from 'vitest';
import { Test } from '@nestjs/testing';
import { jest } from '@jest/globals';
import { MockResolver } from '../misc/mock-resolver.js';
import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js';
@@ -155,7 +155,7 @@ describe('ActivityPub', () => {
// 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(() => { }));
vi.spyOn(federatedInstanceService, 'fetch').mockImplementation(() => new Promise(() => { }));
});
beforeEach(() => {

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { describe, test } from 'vitest';
import * as assert from 'assert';
import httpSignature from '@peertube/http-signature';
@@ -78,7 +79,7 @@ describe('ap-request', () => {
'https://alice.example.com/abc',
FetchAllowSoftFailMask.Any,
), 'validation should fail no matter what if the response URL is inconsistent with the object ID');
assert.doesNotThrow(() => assertActivityMatchesUrl(
'https://alice.example.com/abc#test',
{ id: 'https://alice.example.com/abc' } as IObject,

View File

@@ -6,7 +6,8 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import { jest } from '@jest/globals';
import { describe, beforeEach, afterEach, afterAll, test } from 'vitest';
import type { Mocked } from 'vitest';
import * as lolex from '@sinonjs/fake-timers';
import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
@@ -28,7 +29,7 @@ describe('Chart', () => {
let redisClient = {
set: () => Promise.resolve('OK'),
get: () => Promise.resolve(null),
} as unknown as jest.Mocked<Redis.Redis>;
} as unknown as Mocked<Redis.Redis>;
let testChart: TestChart;
let testGroupedChart: TestGroupedChart;

View File

@@ -5,7 +5,7 @@
process.env.NODE_ENV = 'test';
import { afterAll, beforeAll, beforeEach, describe, expect, jest, test } from '@jest/globals';
import { afterAll, beforeAll, beforeEach, describe, expect, vi, test } from 'vitest';
import { Test } from '@nestjs/testing';
import type { TestingModule } from '@nestjs/testing';
import type { DriveFilesRepository, DriveFoldersRepository, UsersRepository } from '@/models/_.js';
@@ -30,13 +30,13 @@ describe('DriveFileEntityService', () => {
let idCounter = 0;
const userEntityServiceMock = {
packMany: jest.fn(async (users: Array<string | { id: string }>) => {
packMany: vi.fn(async (users: Array<string | { id: string }>) => {
return users.map(u => ({
id: typeof u === 'string' ? u : u.id,
username: 'user',
}));
}),
pack: jest.fn(async (user: string | { id: string }) => {
pack: vi.fn(async (user: string | { id: string }) => {
return {
id: typeof user === 'string' ? user : user.id,
username: 'user',
@@ -195,7 +195,7 @@ describe('DriveFileEntityService', () => {
test('detail: true uses DriveFolderEntityService pack', async () => {
const folder = await createFolder('packmany-folder', null);
const file = await createFile(folder.id, null);
const packSpy = jest.spyOn(driveFolderEntityService, 'pack');
const packSpy = vi.spyOn(driveFolderEntityService, 'pack');
await service.packMany([file], { detail: true, self: true });
expect(packSpy).toHaveBeenCalled();

View File

@@ -5,7 +5,7 @@
process.env.NODE_ENV = 'test';
import { afterAll, beforeAll, describe, expect, test } from '@jest/globals';
import { afterAll, beforeAll, describe, expect, test } from 'vitest';
import { Test } from '@nestjs/testing';
import type { TestingModule } from '@nestjs/testing';
import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/_.js';

View File

@@ -4,6 +4,7 @@
*/
import { Test, TestingModule } from '@nestjs/testing';
import { describe, expect, beforeAll, afterAll, test } from 'vitest';
import type { MiUser } from '@/models/User.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { GlobalModule } from '@/GlobalModule.js';

View File

@@ -4,6 +4,7 @@
*/
import * as assert from 'assert';
import { describe, test } from 'vitest';
import { parse } from 'mfm-js';
import { extractMentions } from '@/misc/extract-mentions.js';

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { describe, expect, it } from 'vitest';
import { checkWordMute } from '@/misc/check-word-mute.js';
describe(checkWordMute, () => {

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { describe, expect, it, test } from 'vitest';
import { correctFilename } from '@/misc/correct-filename.js';
describe(correctFilename, () => {

View File

@@ -4,7 +4,7 @@
*/
import { ulid } from 'ulid';
import { describe, expect, test } from '@jest/globals';
import { describe, expect, test } from 'vitest';
import { aidRegExp, genAid, parseAid } from '@/misc/id/aid.js';
import { aidxRegExp, genAidx, parseAidx } from '@/misc/id/aidx.js';
import { genMeid, meidRegExp, parseMeid } from '@/misc/id/meid.js';

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { describe, expect, test } from 'vitest';
import { isQuote, isRenote } from '@/misc/is-renote.js';
import { MiNote } from '@/models/Note.js';

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { expect, describe, it } from 'vitest';
import { DebounceLoader } from '@/misc/loader.js';
class Mock {

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { describe, expect, test } from '@jest/globals';
import { describe, expect, test } from 'vitest';
import { contentDisposition } from '@/misc/content-disposition.js';
describe('misc:content-disposition', () => {

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { describe, expect, test, beforeEach, afterEach } from '@jest/globals';
import { describe, expect, test, beforeEach, afterEach } from 'vitest';
import * as lolex from '@sinonjs/fake-timers';
import { shouldHideNoteByTime } from '@/misc/should-hide-note-by-time.js';

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { describe, expect, test } from '@jest/globals';
import { describe, expect, test } from 'vitest';
import { parseUlidFull } from '@/misc/id/ulid.js';
// Timestamp part "01KPS7S300" encodes 1776816000000ms (2026-04-22T00:00:00.000Z)

View File

@@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { jest } from '@jest/globals';
import { describe, expect, test, beforeAll, beforeEach, afterEach, afterAll, vi } from 'vitest';
import type { Mocked } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import * as lolex from '@sinonjs/fake-timers';
import { addHours, addSeconds, subDays, subHours, subSeconds } from 'date-fns';
@@ -32,10 +33,10 @@ describe('CheckModeratorsActivityProcessorService', () => {
let usersRepository: UsersRepository;
let userProfilesRepository: UserProfilesRepository;
let idService: IdService;
let roleService: jest.Mocked<RoleService>;
let announcementService: jest.Mocked<AnnouncementService>;
let emailService: jest.Mocked<EmailService>;
let systemWebhookService: jest.Mocked<SystemWebhookService>;
let roleService: Mocked<RoleService>;
let announcementService: Mocked<AnnouncementService>;
let emailService: Mocked<EmailService>;
let systemWebhookService: Mocked<SystemWebhookService>;
let systemWebhook1: MiSystemWebhook;
let systemWebhook2: MiSystemWebhook;
@@ -94,30 +95,30 @@ describe('CheckModeratorsActivityProcessorService', () => {
CheckModeratorsActivityProcessorService,
IdService,
{
provide: RoleService, useFactory: () => ({ getModerators: jest.fn() }),
provide: RoleService, useFactory: () => ({ getModerators: vi.fn() }),
},
{
provide: MetaService, useFactory: () => ({ fetch: jest.fn() }),
provide: MetaService, useFactory: () => ({ fetch: vi.fn() }),
},
{
provide: AnnouncementService, useFactory: () => ({ create: jest.fn() }),
provide: AnnouncementService, useFactory: () => ({ create: vi.fn() }),
},
{
provide: EmailService, useFactory: () => ({ sendEmail: jest.fn() }),
provide: EmailService, useFactory: () => ({ sendEmail: vi.fn() }),
},
{
provide: SystemWebhookService, useFactory: () => ({
fetchActiveSystemWebhooks: jest.fn(),
enqueueSystemWebhook: jest.fn(),
fetchActiveSystemWebhooks: vi.fn(),
enqueueSystemWebhook: vi.fn(),
}),
},
{
provide: QueueLoggerService, useFactory: () => ({
logger: ({
createSubLogger: () => ({
info: jest.fn(),
warn: jest.fn(),
succ: jest.fn(),
info: vi.fn(),
warn: vi.fn(),
succ: vi.fn(),
}),
}),
}),
@@ -131,10 +132,10 @@ describe('CheckModeratorsActivityProcessorService', () => {
service = app.get(CheckModeratorsActivityProcessorService);
idService = app.get(IdService);
roleService = app.get(RoleService) as jest.Mocked<RoleService>;
announcementService = app.get(AnnouncementService) as jest.Mocked<AnnouncementService>;
emailService = app.get(EmailService) as jest.Mocked<EmailService>;
systemWebhookService = app.get(SystemWebhookService) as jest.Mocked<SystemWebhookService>;
roleService = app.get(RoleService) as Mocked<RoleService>;
announcementService = app.get(AnnouncementService) as Mocked<AnnouncementService>;
emailService = app.get(EmailService) as Mocked<EmailService>;
systemWebhookService = app.get(SystemWebhookService) as Mocked<SystemWebhookService>;
app.enableShutdownHooks();
});

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { jest } from '@jest/globals';
import { describe, expect, test, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
import { Test, TestingModule } from '@nestjs/testing';
import ms from 'ms';
import {
@@ -44,8 +44,8 @@ describe('CleanRemoteNotesProcessorService', () => {
// Mock job object
const createMockJob = () => ({
log: jest.fn(),
updateProgress: jest.fn(),
log: vi.fn(),
updateProgress: vi.fn(),
});
async function createUser(data: Partial<MiUser> = {}) {
@@ -96,9 +96,9 @@ describe('CleanRemoteNotesProcessorService', () => {
useFactory: () => ({
logger: {
createSubLogger: () => ({
info: jest.fn(),
warn: jest.fn(),
succ: jest.fn(),
info: vi.fn(),
warn: vi.fn(),
succ: vi.fn(),
}),
},
}),
@@ -125,7 +125,7 @@ describe('CleanRemoteNotesProcessorService', () => {
beforeEach(() => {
// Reset mocks
jest.clearAllMocks();
vi.clearAllMocks();
// Set default meta values
meta.enableRemoteNotesCleaning = true;

View File

@@ -7,7 +7,7 @@ import * as fs from 'node:fs';
import * as path from 'node:path';
import fastifyStatic from '@fastify/static';
import Fastify, { type FastifyInstance } from 'fastify';
import { describe, expect, test } from '@jest/globals';
import { describe, expect, test, beforeAll, afterAll, afterEach } from 'vitest';
import sharp from 'sharp';
import { DataSource, type Repository } from 'typeorm';
import { initTestDb, randomString } from '../../utils.js';

View File

@@ -1,216 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Test, TestingModule } from '@nestjs/testing';
import { FastifyInstance } from 'fastify';
import request from 'supertest';
import { randomString } from '../../../../../utils.js';
import { CoreModule } from '@/core/CoreModule.js';
import { RoleService } from '@/core/RoleService.js';
import { DI } from '@/di-symbols.js';
import { GlobalModule } from '@/GlobalModule.js';
import { DriveFoldersRepository, MiDriveFolder, MiRole, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import { MiUser } from '@/models/User.js';
import { ServerModule } from '@/server/ServerModule.js';
import { ServerService } from '@/server/ServerService.js';
import { IdService } from '@/core/IdService.js';
// TODO: uploadableFileTypes で許可されていないファイルが弾かれるかのテスト
describe('/drive/files/create', () => {
let module: TestingModule;
let server: FastifyInstance;
let roleService: RoleService;
let idService: IdService;
let root: MiUser;
let role_tinyAttachment: MiRole;
let role_imageOnly: MiRole;
let role_allowAllTypes: MiRole;
let folder: MiDriveFolder;
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [GlobalModule, CoreModule, ServerModule],
}).compile();
module.enableShutdownHooks();
const serverService = module.get<ServerService>(ServerService);
await serverService.launch();
server = serverService.fastify;
idService = module.get(IdService);
const usersRepository = module.get<UsersRepository>(DI.usersRepository);
await usersRepository.createQueryBuilder().delete().execute();
root = await usersRepository.insert({
id: idService.gen(),
username: 'root',
usernameLower: 'root',
token: '1234567890123456',
}).then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
const userProfilesRepository = module.get<UserProfilesRepository>(DI.userProfilesRepository);
await userProfilesRepository.createQueryBuilder().delete().execute();
await userProfilesRepository.insert({
userId: root.id,
});
const driveFoldersRepository = module.get<DriveFoldersRepository>(DI.driveFoldersRepository);
folder = await driveFoldersRepository.insertOne({
id: idService.gen(),
name: 'root-folder',
parentId: null,
userId: root.id,
});
roleService = module.get<RoleService>(RoleService);
role_imageOnly = await roleService.create({
name: 'test-role001',
description: 'Test role001 description',
target: 'manual',
policies: {
uploadableFileTypes: {
useDefault: false,
priority: 1,
value: ['image/png'],
},
},
});
role_allowAllTypes = await roleService.create({
name: 'test-role002',
description: 'Test role002 description',
target: 'manual',
policies: {
uploadableFileTypes: {
useDefault: false,
priority: 1,
value: ['*/*'],
},
},
});
role_tinyAttachment = await roleService.create({
name: 'test-role003',
description: 'Test role003 description',
target: 'manual',
policies: {
maxFileSizeMb: {
useDefault: false,
priority: 1,
// 10byte
value: 10 / 1024 / 1024,
},
},
});
});
beforeEach(async () => {
await roleService.unassign(root.id, role_tinyAttachment.id).catch(() => {
});
await roleService.unassign(root.id, role_imageOnly.id).catch(() => {
});
await roleService.unassign(root.id, role_allowAllTypes.id).catch(() => {
});
});
afterAll(async () => {
await server.close();
await module.close();
});
async function postFile(props: {
name: string,
comment: string,
isSensitive: boolean,
force: boolean,
fileContent: Buffer | string,
}) {
const { name, comment, isSensitive, force, fileContent } = props;
return await request(server.server)
.post('/api/drive/files/create')
.set('Content-Type', 'multipart/form-data')
.attach('file', fileContent)
.field('name', name)
.field('comment', comment)
.field('isSensitive', isSensitive)
.field('force', force)
.field('folderId', folder.id)
.field('i', root.token ?? '');
}
test('200 ok (all types allowed)', async () => {
await roleService.assign(root.id, role_allowAllTypes.id);
const name = randomString();
const comment = randomString();
const result = await postFile({
name: name,
comment: comment,
isSensitive: true,
force: true,
fileContent: Buffer.from('a'.repeat(1000 * 1000)),
});
expect(result.statusCode).toBe(200);
expect(result.body.name).toBe(name + '.unknown');
expect(result.body.comment).toBe(comment);
expect(result.body.isSensitive).toBe(true);
expect(result.body.folderId).toBe(folder.id);
});
test('400 when not allowed type', async () => {
await roleService.assign(root.id, role_imageOnly.id);
const name = randomString();
const comment = randomString();
const result = await postFile({
name: name,
comment: comment,
isSensitive: true,
force: true,
fileContent: Buffer.from('a'.repeat(10)),
});
expect(result.statusCode).toBe(400);
expect(result.body.error.code).toBe('UNALLOWED_FILE_TYPE');
});
test('200 ok (with size limited role)', async () => {
await roleService.assign(root.id, role_allowAllTypes.id);
await roleService.assign(root.id, role_tinyAttachment.id);
const name = randomString();
const comment = randomString();
const result = await postFile({
name: name,
comment: comment,
isSensitive: true,
force: true,
fileContent: Buffer.from('a'.repeat(10)),
});
expect(result.statusCode).toBe(200);
expect(result.body.name).toBe(name + '.unknown');
expect(result.body.comment).toBe(comment);
expect(result.body.isSensitive).toBe(true);
expect(result.body.folderId).toBe(folder.id);
});
test('413 too large', async () => {
await roleService.assign(root.id, role_allowAllTypes.id);
await roleService.assign(root.id, role_tinyAttachment.id);
const name = randomString();
const comment = randomString();
const result = await postFile({
name: name,
comment: comment,
isSensitive: true,
force: true,
fileContent: Buffer.from('a'.repeat(11)),
});
expect(result.statusCode).toBe(413);
expect(result.body.error.code).toBe('MAX_FILE_SIZE_EXCEEDED');
});
});