Merge branch 'develop' into fetch-outbox
This commit is contained in:
194
packages/backend/test/unit/AnnouncementService.ts
Normal file
194
packages/backend/test/unit/AnnouncementService.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import { jest } from '@jest/globals';
|
||||
import { ModuleMocker } from 'jest-mock';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { AnnouncementService } from '@/core/AnnouncementService.js';
|
||||
import type { Announcement, AnnouncementsRepository, AnnouncementReadsRepository, UsersRepository, User } from '@/models/index.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { genAid } from '@/misc/id/aid.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 type { TestingModule } from '@nestjs/testing';
|
||||
import type { MockFunctionMetadata } from 'jest-mock';
|
||||
|
||||
const moduleMocker = new ModuleMocker(global);
|
||||
|
||||
describe('AnnouncementService', () => {
|
||||
let app: TestingModule;
|
||||
let announcementService: AnnouncementService;
|
||||
let usersRepository: UsersRepository;
|
||||
let announcementsRepository: AnnouncementsRepository;
|
||||
let announcementReadsRepository: AnnouncementReadsRepository;
|
||||
let globalEventService: jest.Mocked<GlobalEventService>;
|
||||
|
||||
function createUser(data: Partial<User> = {}) {
|
||||
const un = secureRndstr(16);
|
||||
return usersRepository.insert({
|
||||
id: genAid(new Date()),
|
||||
createdAt: new Date(),
|
||||
username: un,
|
||||
usernameLower: un,
|
||||
...data,
|
||||
})
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}
|
||||
|
||||
function createAnnouncement(data: Partial<Announcement> = {}) {
|
||||
return announcementsRepository.insert({
|
||||
id: genAid(new Date()),
|
||||
createdAt: new Date(),
|
||||
updatedAt: null,
|
||||
title: 'Title',
|
||||
text: 'Text',
|
||||
...data,
|
||||
})
|
||||
.then(x => announcementsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await Test.createTestingModule({
|
||||
imports: [
|
||||
GlobalModule,
|
||||
],
|
||||
providers: [
|
||||
AnnouncementService,
|
||||
CacheService,
|
||||
IdService,
|
||||
],
|
||||
})
|
||||
.useMocker((token) => {
|
||||
if (token === GlobalEventService) {
|
||||
return {
|
||||
publishMainStream: jest.fn(),
|
||||
publishBroadcastStream: jest.fn(),
|
||||
};
|
||||
}
|
||||
if (typeof token === 'function') {
|
||||
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
|
||||
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
|
||||
return new Mock();
|
||||
}
|
||||
})
|
||||
.compile();
|
||||
|
||||
app.enableShutdownHooks();
|
||||
|
||||
announcementService = app.get<AnnouncementService>(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>;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all([
|
||||
app.get(DI.metasRepository).delete({}),
|
||||
usersRepository.delete({}),
|
||||
announcementsRepository.delete({}),
|
||||
announcementReadsRepository.delete({}),
|
||||
]);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('getUnreadAnnouncements', () => {
|
||||
test('通常', async () => {
|
||||
const user = await createUser();
|
||||
const announcement = await createAnnouncement({
|
||||
title: '1',
|
||||
});
|
||||
|
||||
const result = await announcementService.getUnreadAnnouncements(user);
|
||||
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0].title).toBe(announcement.title);
|
||||
});
|
||||
|
||||
test('isActiveがfalseは除外', async () => {
|
||||
const user = await createUser();
|
||||
await createAnnouncement({
|
||||
isActive: false,
|
||||
});
|
||||
|
||||
const result = await announcementService.getUnreadAnnouncements(user);
|
||||
|
||||
expect(result.length).toBe(0);
|
||||
});
|
||||
|
||||
test('forExistingUsers', async () => {
|
||||
const user = await createUser();
|
||||
const [announcementAfter, announcementBefore, announcementBefore2] = await Promise.all([
|
||||
createAnnouncement({
|
||||
title: 'after',
|
||||
createdAt: new Date(),
|
||||
forExistingUsers: true,
|
||||
}),
|
||||
createAnnouncement({
|
||||
title: 'before',
|
||||
createdAt: new Date(Date.now() - 1000),
|
||||
forExistingUsers: true,
|
||||
}),
|
||||
createAnnouncement({
|
||||
title: 'before2',
|
||||
createdAt: new Date(Date.now() - 1000),
|
||||
forExistingUsers: false,
|
||||
}),
|
||||
]);
|
||||
|
||||
const result = await announcementService.getUnreadAnnouncements(user);
|
||||
|
||||
expect(result.length).toBe(2);
|
||||
expect(result.some(a => a.title === announcementAfter.title)).toBe(true);
|
||||
expect(result.some(a => a.title === announcementBefore.title)).toBe(false);
|
||||
expect(result.some(a => a.title === announcementBefore2.title)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
test('通常', async () => {
|
||||
const result = await announcementService.create({
|
||||
title: 'Title',
|
||||
text: 'Text',
|
||||
});
|
||||
|
||||
expect(result.raw.title).toBe('Title');
|
||||
expect(result.packed.title).toBe('Title');
|
||||
|
||||
expect(globalEventService.publishBroadcastStream).toHaveBeenCalled();
|
||||
expect(globalEventService.publishBroadcastStream.mock.lastCall![0]).toBe('announcementCreated');
|
||||
expect((globalEventService.publishBroadcastStream.mock.lastCall![1] as any).announcement).toBe(result.packed);
|
||||
});
|
||||
|
||||
test('ユーザー指定', async () => {
|
||||
const user = await createUser();
|
||||
const result = await announcementService.create({
|
||||
title: 'Title',
|
||||
text: 'Text',
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
expect(result.raw.title).toBe('Title');
|
||||
expect(result.packed.title).toBe('Title');
|
||||
|
||||
expect(globalEventService.publishBroadcastStream).not.toHaveBeenCalled();
|
||||
expect(globalEventService.publishMainStream).toHaveBeenCalled();
|
||||
expect(globalEventService.publishMainStream.mock.lastCall![0]).toBe(user.id);
|
||||
expect(globalEventService.publishMainStream.mock.lastCall![1]).toBe('announcementCreated');
|
||||
expect((globalEventService.publishMainStream.mock.lastCall![2] as any).announcement).toBe(result.packed);
|
||||
});
|
||||
});
|
||||
|
||||
describe('read', () => {
|
||||
// TODO
|
||||
});
|
||||
});
|
||||
|
||||
@@ -316,6 +316,21 @@ describe('ActivityPub', () => {
|
||||
assert.strictEqual(note.text, 'test test foo');
|
||||
assert.strictEqual(note.uri, actor2Note.id);
|
||||
});
|
||||
|
||||
test('Fetch a note that is a featured note of the attributed actor', async () => {
|
||||
const actor = createRandomActor();
|
||||
actor.featured = `${actor.id}/collections/featured`;
|
||||
|
||||
const featured = createRandomFeaturedCollection(actor, 5);
|
||||
const firstNote = (featured.items as NonTransientIPost[])[0];
|
||||
|
||||
resolver.register(actor.id, actor);
|
||||
resolver.register(actor.featured, featured);
|
||||
resolver.register(firstNote.id, firstNote);
|
||||
|
||||
const note = await noteService.createNote(firstNote.id as string, resolver);
|
||||
assert.strictEqual(note?.uri, firstNote.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Outbox', () => {
|
||||
|
||||
48
packages/backend/test/unit/misc/correct-filename.ts
Normal file
48
packages/backend/test/unit/misc/correct-filename.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { correctFilename } from '@/misc/correct-filename.js';
|
||||
|
||||
describe(correctFilename, () => {
|
||||
it('no ext to null', () => {
|
||||
expect(correctFilename('test', null)).toBe('test.unknown');
|
||||
});
|
||||
it('no ext to jpg', () => {
|
||||
expect(correctFilename('test', 'jpg')).toBe('test.jpg');
|
||||
});
|
||||
it('jpg to webp', () => {
|
||||
expect(correctFilename('test.jpg', 'webp')).toBe('test.jpg.webp');
|
||||
});
|
||||
it('jpg to .webp', () => {
|
||||
expect(correctFilename('test.jpg', '.webp')).toBe('test.jpg.webp');
|
||||
});
|
||||
it('jpeg to jpg', () => {
|
||||
expect(correctFilename('test.jpeg', 'jpg')).toBe('test.jpeg');
|
||||
});
|
||||
it('JPEG to jpg', () => {
|
||||
expect(correctFilename('test.JPEG', 'jpg')).toBe('test.JPEG');
|
||||
});
|
||||
it('jpg to jpg', () => {
|
||||
expect(correctFilename('test.jpg', 'jpg')).toBe('test.jpg');
|
||||
});
|
||||
it('JPG to jpg', () => {
|
||||
expect(correctFilename('test.JPG', 'jpg')).toBe('test.JPG');
|
||||
});
|
||||
it('tiff to tif', () => {
|
||||
expect(correctFilename('test.tiff', 'tif')).toBe('test.tiff');
|
||||
});
|
||||
it('skip gz', () => {
|
||||
expect(correctFilename('test.unitypackage', 'gz')).toBe('test.unitypackage');
|
||||
});
|
||||
it('skip text file', () => {
|
||||
expect(correctFilename('test.txt', null)).toBe('test.txt');
|
||||
});
|
||||
it('unknown', () => {
|
||||
expect(correctFilename('test.hoge', null)).toBe('test.hoge');
|
||||
});
|
||||
test('non ascii with space', () => {
|
||||
expect(correctFilename('ファイル 名前', 'jpg')).toBe('ファイル 名前.jpg');
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { describe, test, expect } from '@jest/globals';
|
||||
import { contentDisposition } from '@/misc/content-disposition.js';
|
||||
import { correctFilename } from '@/misc/correct-filename.js';
|
||||
|
||||
describe('misc:content-disposition', () => {
|
||||
test('inline', () => {
|
||||
@@ -18,30 +17,3 @@ describe('misc:content-disposition', () => {
|
||||
expect(contentDisposition('attachment', 'ファイル名')).toBe('attachment; filename=\"_____\"; filename*=UTF-8\'\'%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8D');
|
||||
});
|
||||
});
|
||||
|
||||
describe('misc:correct-filename', () => {
|
||||
test('simple', () => {
|
||||
expect(correctFilename('filename', 'jpg')).toBe('filename.jpg');
|
||||
});
|
||||
test('with same ext', () => {
|
||||
expect(correctFilename('filename.jpg', 'jpg')).toBe('filename.jpg');
|
||||
});
|
||||
test('.ext', () => {
|
||||
expect(correctFilename('filename.jpg', '.jpg')).toBe('filename.jpg');
|
||||
});
|
||||
test('with different ext', () => {
|
||||
expect(correctFilename('filename.webp', 'jpg')).toBe('filename.webp.jpg');
|
||||
});
|
||||
test('non ascii with space', () => {
|
||||
expect(correctFilename('ファイル 名前', 'jpg')).toBe('ファイル 名前.jpg');
|
||||
});
|
||||
test('jpeg', () => {
|
||||
expect(correctFilename('filename.jpeg', 'jpg')).toBe('filename.jpeg');
|
||||
});
|
||||
test('tiff', () => {
|
||||
expect(correctFilename('filename.tiff', 'tif')).toBe('filename.tiff');
|
||||
});
|
||||
test('null ext', () => {
|
||||
expect(correctFilename('filename', null)).toBe('filename.unknown');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user