Merge branch 'develop' into ed25519

This commit is contained in:
tamaina
2024-06-29 09:24:02 +09:00
118 changed files with 916 additions and 669 deletions

View File

@@ -1,5 +1,3 @@
version: "3"
services:
redistest:
image: redis:7

View File

@@ -163,8 +163,7 @@ describe('アンテナ', () => {
});
test('が上限いっぱいまで作成できること', async () => {
// antennaLimit + 1まで作れるのがキモ
const response = await Promise.all([...Array(DEFAULT_POLICIES.antennaLimit + 1)].map(() => successfulApiCall({
const response = await Promise.all([...Array(DEFAULT_POLICIES.antennaLimit)].map(() => successfulApiCall({
endpoint: 'antennas/create',
parameters: { ...defaultParam },
user: alice,

View File

@@ -153,8 +153,7 @@ describe('クリップ', () => {
});
test('の作成はポリシーで定められた数以上はできない。', async () => {
// ポリシー + 1まで作れるという所がミソ
const clipLimit = DEFAULT_POLICIES.clipLimit + 1;
const clipLimit = DEFAULT_POLICIES.clipLimit;
for (let i = 0; i < clipLimit; i++) {
await create();
}
@@ -327,7 +326,7 @@ describe('クリップ', () => {
});
test('の一覧(clips/list)が取得できる(上限いっぱい)', async () => {
const clipLimit = DEFAULT_POLICIES.clipLimit + 1;
const clipLimit = DEFAULT_POLICIES.clipLimit;
const clips = await createMany({}, clipLimit);
const res = await list({
parameters: { limit: 1 }, // FIXME: 無視されて11全部返ってくる
@@ -705,7 +704,7 @@ describe('クリップ', () => {
// TODO: 17000msくらいかかる...
test('をポリシーで定められた上限いっぱい(200)を超えて追加はできない。', async () => {
const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit + 1;
const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit;
const noteList = await Promise.all([...Array(noteLimit)].map((_, i) => post(alice, {
text: `test ${i}`,
}) as unknown)) as Misskey.entities.Note[];

View File

@@ -266,6 +266,67 @@ describe('Endpoints', () => {
assert.strictEqual(res.status, 400);
});
test('リノートにリアクションできない', async () => {
const bobNote = await post(bob, { text: 'hi' });
const bobRenote = await post(bob, { renoteId: bobNote.id });
const res = await api('notes/reactions/create', {
noteId: bobRenote.id,
reaction: '🚀',
}, alice);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'CANNOT_REACT_TO_RENOTE');
});
test('引用にリアクションできる', async () => {
const bobNote = await post(bob, { text: 'hi' });
const bobRenote = await post(bob, { text: 'hi again', renoteId: bobNote.id });
const res = await api('notes/reactions/create', {
noteId: bobRenote.id,
reaction: '🚀',
}, alice);
assert.strictEqual(res.status, 204);
});
test('空文字列のリアクションは\u2764にフォールバックされる', async () => {
const bobNote = await post(bob, { text: 'hi' });
const res = await api('notes/reactions/create', {
noteId: bobNote.id,
reaction: '',
}, alice);
assert.strictEqual(res.status, 204);
const reaction = await api('notes/reactions', {
noteId: bobNote.id,
});
assert.strictEqual(reaction.body.length, 1);
assert.strictEqual(reaction.body[0].type, '\u2764');
});
test('絵文字ではない文字列のリアクションは\u2764にフォールバックされる', async () => {
const bobNote = await post(bob, { text: 'hi' });
const res = await api('notes/reactions/create', {
noteId: bobNote.id,
reaction: 'Hello!',
}, alice);
assert.strictEqual(res.status, 204);
const reaction = await api('notes/reactions', {
noteId: bobNote.id,
});
assert.strictEqual(reaction.body.length, 1);
assert.strictEqual(reaction.body[0].type, '\u2764');
});
test('空のパラメータで怒られる', async () => {
// @ts-expect-error param must not be empty
const res = await api('notes/reactions/create', {}, alice);

View File

@@ -153,6 +153,23 @@ describe('Webリソース', () => {
path: path('nonexisting'),
status: 404,
}));
describe(' has entry such ', () => {
beforeEach(() => {
post(alice, { text: "**a**" })
});
test('MFMを含まない。', async () => {
const content = await simpleGet(path(alice.username), "*/*", undefined, res => res.text());
const _body: unknown = content.body;
// JSONフィードのときは改めて文字列化する
const body: string = typeof (_body) === "object" ? JSON.stringify(_body) : _body as string;
if (body.includes("**a**")) {
throw new Error("MFM shouldn't be included");
}
});
})
});
describe.each([{ path: '/api/foo' }])('$path', ({ path }) => {

View File

@@ -7,6 +7,8 @@
// pnpm jest -- e2e/timelines.ts
import * as assert from 'assert';
import { Redis } from 'ioredis';
import { loadConfig } from '@/config.js';
import { api, post, randomString, sendEnvUpdateRequest, signup, sleep, uploadUrl } from '../utils.js';
function genHost() {
@@ -17,7 +19,13 @@ function waitForPushToTl() {
return sleep(500);
}
let redisForTimelines: Redis;
describe('Timelines', () => {
beforeAll(() => {
redisForTimelines = new Redis(loadConfig().redisForTimelines);
});
describe('Home TL', () => {
test.concurrent('自分の visibility: followers なノートが含まれる', async () => {
const [alice] = await Promise.all([signup()]);
@@ -1272,6 +1280,33 @@ describe('Timelines', () => {
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
/** @see https://github.com/misskey-dev/misskey/issues/14000 */
test.concurrent('FTT: sinceId にキャッシュより古いートを指定しても、sinceId による絞り込みが正しく動作する', async () => {
const alice = await signup();
const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
const note1 = await post(alice, { text: '1' });
const note2 = await post(alice, { text: '2' });
await redisForTimelines.del('list:userTimeline:' + alice.id);
const note3 = await post(alice, { text: '3' });
const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id });
assert.deepStrictEqual(res.body, [note1, note2, note3]);
});
test.concurrent('FTT: sinceId にキャッシュより古いートを指定しても、sinceId と untilId による絞り込みが正しく動作する', async () => {
const alice = await signup();
const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
const note1 = await post(alice, { text: '1' });
const note2 = await post(alice, { text: '2' });
await redisForTimelines.del('list:userTimeline:' + alice.id);
const note3 = await post(alice, { text: '3' });
const noteUntil = await post(alice, { text: 'Note where id will be `untilId`.' });
await post(alice, { text: '4' });
const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id, untilId: noteUntil.id });
assert.deepStrictEqual(res.body, [note3, note2, note1]);
});
});
// TODO: リノートミュート済みユーザーのテスト

View File

@@ -1,23 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as assert from 'assert';
import { just, nothing } from '../../src/misc/prelude/maybe.js';
describe('just', () => {
test('has a value', () => {
assert.deepStrictEqual(just(3).isJust(), true);
});
test('has the inverse called get', () => {
assert.deepStrictEqual(just(3).get(), 3);
});
});
describe('nothing', () => {
test('has no value', () => {
assert.deepStrictEqual(nothing().isJust(), false);
});
});

View File

@@ -17,6 +17,7 @@ import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/val
import { entities } from '../src/postgres.js';
import { loadConfig } from '../src/config.js';
import type * as misskey from 'misskey-js';
import { type Response } from 'node-fetch';
export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
@@ -454,7 +455,7 @@ export type SimpleGetResponse = {
type: string | null,
location: string | null
};
export const simpleGet = async (path: string, accept = '*/*', cookie: any = undefined): Promise<SimpleGetResponse> => {
export const simpleGet = async (path: string, accept = '*/*', cookie: any = undefined, bodyExtractor: (res: Response) => Promise<string | null> = _ => Promise.resolve(null)): Promise<SimpleGetResponse> => {
const res = await relativeFetch(path, {
headers: {
Accept: accept,
@@ -482,7 +483,7 @@ export const simpleGet = async (path: string, accept = '*/*', cookie: any = unde
const body =
jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() :
htmlTypes.includes(res.headers.get('content-type') ?? '') ? new JSDOM(await res.text()) :
null;
await bodyExtractor(res);
return {
status: res.status,