Merge branch 'develop' into fetch-outbox
This commit is contained in:
@@ -9,7 +9,7 @@ import * as assert from 'assert';
|
||||
// node-fetch only supports it's own Blob yet
|
||||
// https://github.com/node-fetch/node-fetch/pull/1664
|
||||
import { Blob } from 'node-fetch';
|
||||
import { User } from '@/models/index.js';
|
||||
import { MiUser } from '@/models/index.js';
|
||||
import { startServer, signup, post, api, uploadFile, simpleGet, initTestDb } from '../utils.js';
|
||||
import type { INestApplicationContext } from '@nestjs/common';
|
||||
import type * as misskey from 'misskey-js';
|
||||
@@ -298,7 +298,7 @@ describe('Endpoints', () => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
|
||||
const connection = await initTestDb(true);
|
||||
const Users = connection.getRepository(User);
|
||||
const Users = connection.getRepository(MiUser);
|
||||
const newBob = await Users.findOneByOrFail({ id: bob.id });
|
||||
assert.strictEqual(newBob.followersCount, 0);
|
||||
assert.strictEqual(newBob.followingCount, 1);
|
||||
@@ -360,7 +360,7 @@ describe('Endpoints', () => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
|
||||
const connection = await initTestDb(true);
|
||||
const Users = connection.getRepository(User);
|
||||
const Users = connection.getRepository(MiUser);
|
||||
const newBob = await Users.findOneByOrFail({ id: bob.id });
|
||||
assert.strictEqual(newBob.followersCount, 0);
|
||||
assert.strictEqual(newBob.followingCount, 0);
|
||||
|
||||
@@ -34,6 +34,8 @@ describe('Webリソース', () => {
|
||||
let aliceGalleryPost: any;
|
||||
let aliceChannel: any;
|
||||
|
||||
let bob: misskey.entities.MeSignup;
|
||||
|
||||
type Request = {
|
||||
path: string,
|
||||
accept?: string,
|
||||
@@ -90,6 +92,8 @@ describe('Webリソース', () => {
|
||||
fileIds: [aliceUploadedFile.body.id],
|
||||
});
|
||||
aliceChannel = await channel(alice, {});
|
||||
|
||||
bob = await signup({ username: 'alice' });
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -163,9 +167,15 @@ describe('Webリソース', () => {
|
||||
});
|
||||
|
||||
describe.each([{ path: '/queue' }])('$path', ({ path }) => {
|
||||
test('はログインしないとGETできない。', async () => await notOk({
|
||||
path,
|
||||
status: 401,
|
||||
}));
|
||||
|
||||
test('はadminでなければGETできない。', async () => await notOk({
|
||||
path,
|
||||
status: 500, // FIXME? 403ではない。
|
||||
cookie: cookie(bob),
|
||||
status: 403,
|
||||
}));
|
||||
|
||||
test('はadminならGETできる。', async () => await ok({
|
||||
|
||||
@@ -7,7 +7,7 @@ process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { loadConfig } from '@/config.js';
|
||||
import { User, UsersRepository } from '@/models/index.js';
|
||||
import { MiUser, UsersRepository } from '@/models/index.js';
|
||||
import { jobQueue } from '@/boot/common.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { uploadFile, signup, startServer, initTestDb, api, sleep, successfulApiCall } from '../utils.js';
|
||||
@@ -42,7 +42,7 @@ describe('Account Move', () => {
|
||||
dave = await signup({ username: 'dave' });
|
||||
eve = await signup({ username: 'eve' });
|
||||
frank = await signup({ username: 'frank' });
|
||||
Users = connection.getRepository(User);
|
||||
Users = connection.getRepository(MiUser);
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Note } from '@/models/entities/Note.js';
|
||||
import { MiNote } from '@/models/entities/Note.js';
|
||||
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
||||
import { signup, post, uploadUrl, startServer, initTestDb, api, uploadFile } from '../utils.js';
|
||||
import type { INestApplicationContext } from '@nestjs/common';
|
||||
@@ -22,7 +22,7 @@ describe('Note', () => {
|
||||
beforeAll(async () => {
|
||||
app = await startServer();
|
||||
const connection = await initTestDb(true);
|
||||
Notes = connection.getRepository(Note);
|
||||
Notes = connection.getRepository(MiNote);
|
||||
alice = await signup({ username: 'alice' });
|
||||
bob = await signup({ username: 'bob' });
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Following } from '@/models/entities/Following.js';
|
||||
import { MiFollowing } from '@/models/entities/Following.js';
|
||||
import { connectStream, signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
|
||||
import type { INestApplicationContext } from '@nestjs/common';
|
||||
import type * as misskey from 'misskey-js';
|
||||
@@ -46,7 +46,7 @@ describe('Streaming', () => {
|
||||
beforeAll(async () => {
|
||||
app = await startServer();
|
||||
const connection = await initTestDb(true);
|
||||
Followings = connection.getRepository(Following);
|
||||
Followings = connection.getRepository(MiFollowing);
|
||||
|
||||
ayano = await signup({ username: 'ayano' });
|
||||
kyoko = await signup({ username: 'kyoko' });
|
||||
|
||||
@@ -160,6 +160,7 @@ describe('ユーザー', () => {
|
||||
hasUnreadChannel: user.hasUnreadChannel,
|
||||
hasUnreadNotification: user.hasUnreadNotification,
|
||||
hasPendingReceivedFollowRequest: user.hasPendingReceivedFollowRequest,
|
||||
unreadAnnouncements: user.unreadAnnouncements,
|
||||
mutedWords: user.mutedWords,
|
||||
mutedInstances: user.mutedInstances,
|
||||
mutingNotificationTypes: user.mutingNotificationTypes,
|
||||
@@ -405,6 +406,7 @@ describe('ユーザー', () => {
|
||||
assert.strictEqual(response.hasUnreadChannel, false);
|
||||
assert.strictEqual(response.hasUnreadNotification, false);
|
||||
assert.strictEqual(response.hasPendingReceivedFollowRequest, false);
|
||||
assert.deepStrictEqual(response.unreadAnnouncements, []);
|
||||
assert.deepStrictEqual(response.mutedWords, []);
|
||||
assert.deepStrictEqual(response.mutedInstances, []);
|
||||
assert.deepStrictEqual(response.mutingNotificationTypes, []);
|
||||
|
||||
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