Merge branch 'develop' into fetch-outbox

This commit is contained in:
tamaina
2023-08-24 06:23:05 +00:00
675 changed files with 6717 additions and 3481 deletions

View File

@@ -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);

View File

@@ -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({

View File

@@ -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 () => {

View File

@@ -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);

View File

@@ -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' });

View File

@@ -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, []);

View 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
});
});

View File

@@ -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', () => {

View 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');
});
});

View File

@@ -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');
});
});