なんかもうめっちゃ変えた

This commit is contained in:
syuilo 2022-09-18 03:27:08 +09:00 committed by GitHub
parent d9ab03f086
commit b75184ec8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
946 changed files with 41219 additions and 28839 deletions

View file

@ -0,0 +1,477 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { signup, request, post, startServer, shutdownServer } from '../utils.js';
describe('API visibility', () => {
let p: childProcess.ChildProcess;
beforeAll(async () => {
p = await startServer();
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
describe('Note visibility', () => {
//#region vars
/** ヒロイン */
let alice: any;
/** フォロワー */
let follower: any;
/** 非フォロワー */
let other: any;
/** 非フォロワーでもリプライやメンションをされた人 */
let target: any;
/** specified mentionでmentionを飛ばされる人 */
let target2: any;
/** public-post */
let pub: any;
/** home-post */
let home: any;
/** followers-post */
let fol: any;
/** specified-post */
let spe: any;
/** public-reply to target's post */
let pubR: any;
/** home-reply to target's post */
let homeR: any;
/** followers-reply to target's post */
let folR: any;
/** specified-reply to target's post */
let speR: any;
/** public-mention to target */
let pubM: any;
/** home-mention to target */
let homeM: any;
/** followers-mention to target */
let folM: any;
/** specified-mention to target */
let speM: any;
/** reply target post */
let tgt: any;
//#endregion
const show = async (noteId: any, by: any) => {
return await request('/notes/show', {
noteId,
}, by);
};
beforeAll(async () => {
//#region prepare
// signup
alice = await signup({ username: 'alice' });
follower = await signup({ username: 'follower' });
other = await signup({ username: 'other' });
target = await signup({ username: 'target' });
target2 = await signup({ username: 'target2' });
// follow alice <= follower
await request('/following/create', { userId: alice.id }, follower);
// normal posts
pub = await post(alice, { text: 'x', visibility: 'public' });
home = await post(alice, { text: 'x', visibility: 'home' });
fol = await post(alice, { text: 'x', visibility: 'followers' });
spe = await post(alice, { text: 'x', visibility: 'specified', visibleUserIds: [target.id] });
// replies
tgt = await post(target, { text: 'y', visibility: 'public' });
pubR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'public' });
homeR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'home' });
folR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'followers' });
speR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'specified' });
// mentions
pubM = await post(alice, { text: '@target x', replyId: tgt.id, visibility: 'public' });
homeM = await post(alice, { text: '@target x', replyId: tgt.id, visibility: 'home' });
folM = await post(alice, { text: '@target x', replyId: tgt.id, visibility: 'followers' });
speM = await post(alice, { text: '@target2 x', replyId: tgt.id, visibility: 'specified' });
//#endregion
});
//#region show post
// public
it('[show] public-postを自分が見れる', async () => {
const res = await show(pub.id, alice);
assert.strictEqual(res.body.text, 'x');
});
it('[show] public-postをフォロワーが見れる', async () => {
const res = await show(pub.id, follower);
assert.strictEqual(res.body.text, 'x');
});
it('[show] public-postを非フォロワーが見れる', async () => {
const res = await show(pub.id, other);
assert.strictEqual(res.body.text, 'x');
});
it('[show] public-postを未認証が見れる', async () => {
const res = await show(pub.id, null);
assert.strictEqual(res.body.text, 'x');
});
// home
it('[show] home-postを自分が見れる', async () => {
const res = await show(home.id, alice);
assert.strictEqual(res.body.text, 'x');
});
it('[show] home-postをフォロワーが見れる', async () => {
const res = await show(home.id, follower);
assert.strictEqual(res.body.text, 'x');
});
it('[show] home-postを非フォロワーが見れる', async () => {
const res = await show(home.id, other);
assert.strictEqual(res.body.text, 'x');
});
it('[show] home-postを未認証が見れる', async () => {
const res = await show(home.id, null);
assert.strictEqual(res.body.text, 'x');
});
// followers
it('[show] followers-postを自分が見れる', async () => {
const res = await show(fol.id, alice);
assert.strictEqual(res.body.text, 'x');
});
it('[show] followers-postをフォロワーが見れる', async () => {
const res = await show(fol.id, follower);
assert.strictEqual(res.body.text, 'x');
});
it('[show] followers-postを非フォロワーが見れない', async () => {
const res = await show(fol.id, other);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] followers-postを未認証が見れない', async () => {
const res = await show(fol.id, null);
assert.strictEqual(res.body.isHidden, true);
});
// specified
it('[show] specified-postを自分が見れる', async () => {
const res = await show(spe.id, alice);
assert.strictEqual(res.body.text, 'x');
});
it('[show] specified-postを指定ユーザーが見れる', async () => {
const res = await show(spe.id, target);
assert.strictEqual(res.body.text, 'x');
});
it('[show] specified-postをフォロワーが見れない', async () => {
const res = await show(spe.id, follower);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] specified-postを非フォロワーが見れない', async () => {
const res = await show(spe.id, other);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] specified-postを未認証が見れない', async () => {
const res = await show(spe.id, null);
assert.strictEqual(res.body.isHidden, true);
});
//#endregion
//#region show reply
// public
it('[show] public-replyを自分が見れる', async () => {
const res = await show(pubR.id, alice);
assert.strictEqual(res.body.text, 'x');
});
it('[show] public-replyをされた人が見れる', async () => {
const res = await show(pubR.id, target);
assert.strictEqual(res.body.text, 'x');
});
it('[show] public-replyをフォロワーが見れる', async () => {
const res = await show(pubR.id, follower);
assert.strictEqual(res.body.text, 'x');
});
it('[show] public-replyを非フォロワーが見れる', async () => {
const res = await show(pubR.id, other);
assert.strictEqual(res.body.text, 'x');
});
it('[show] public-replyを未認証が見れる', async () => {
const res = await show(pubR.id, null);
assert.strictEqual(res.body.text, 'x');
});
// home
it('[show] home-replyを自分が見れる', async () => {
const res = await show(homeR.id, alice);
assert.strictEqual(res.body.text, 'x');
});
it('[show] home-replyをされた人が見れる', async () => {
const res = await show(homeR.id, target);
assert.strictEqual(res.body.text, 'x');
});
it('[show] home-replyをフォロワーが見れる', async () => {
const res = await show(homeR.id, follower);
assert.strictEqual(res.body.text, 'x');
});
it('[show] home-replyを非フォロワーが見れる', async () => {
const res = await show(homeR.id, other);
assert.strictEqual(res.body.text, 'x');
});
it('[show] home-replyを未認証が見れる', async () => {
const res = await show(homeR.id, null);
assert.strictEqual(res.body.text, 'x');
});
// followers
it('[show] followers-replyを自分が見れる', async () => {
const res = await show(folR.id, alice);
assert.strictEqual(res.body.text, 'x');
});
it('[show] followers-replyを非フォロワーでもリプライされていれば見れる', async () => {
const res = await show(folR.id, target);
assert.strictEqual(res.body.text, 'x');
});
it('[show] followers-replyをフォロワーが見れる', async () => {
const res = await show(folR.id, follower);
assert.strictEqual(res.body.text, 'x');
});
it('[show] followers-replyを非フォロワーが見れない', async () => {
const res = await show(folR.id, other);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] followers-replyを未認証が見れない', async () => {
const res = await show(folR.id, null);
assert.strictEqual(res.body.isHidden, true);
});
// specified
it('[show] specified-replyを自分が見れる', async () => {
const res = await show(speR.id, alice);
assert.strictEqual(res.body.text, 'x');
});
it('[show] specified-replyを指定ユーザーが見れる', async () => {
const res = await show(speR.id, target);
assert.strictEqual(res.body.text, 'x');
});
it('[show] specified-replyをされた人が指定されてなくても見れる', async () => {
const res = await show(speR.id, target);
assert.strictEqual(res.body.text, 'x');
});
it('[show] specified-replyをフォロワーが見れない', async () => {
const res = await show(speR.id, follower);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] specified-replyを非フォロワーが見れない', async () => {
const res = await show(speR.id, other);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] specified-replyを未認証が見れない', async () => {
const res = await show(speR.id, null);
assert.strictEqual(res.body.isHidden, true);
});
//#endregion
//#region show mention
// public
it('[show] public-mentionを自分が見れる', async () => {
const res = await show(pubM.id, alice);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] public-mentionをされた人が見れる', async () => {
const res = await show(pubM.id, target);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] public-mentionをフォロワーが見れる', async () => {
const res = await show(pubM.id, follower);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] public-mentionを非フォロワーが見れる', async () => {
const res = await show(pubM.id, other);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] public-mentionを未認証が見れる', async () => {
const res = await show(pubM.id, null);
assert.strictEqual(res.body.text, '@target x');
});
// home
it('[show] home-mentionを自分が見れる', async () => {
const res = await show(homeM.id, alice);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] home-mentionをされた人が見れる', async () => {
const res = await show(homeM.id, target);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] home-mentionをフォロワーが見れる', async () => {
const res = await show(homeM.id, follower);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] home-mentionを非フォロワーが見れる', async () => {
const res = await show(homeM.id, other);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] home-mentionを未認証が見れる', async () => {
const res = await show(homeM.id, null);
assert.strictEqual(res.body.text, '@target x');
});
// followers
it('[show] followers-mentionを自分が見れる', async () => {
const res = await show(folM.id, alice);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] followers-mentionをメンションされていれば非フォロワーでも見れる', async () => {
const res = await show(folM.id, target);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] followers-mentionをフォロワーが見れる', async () => {
const res = await show(folM.id, follower);
assert.strictEqual(res.body.text, '@target x');
});
it('[show] followers-mentionを非フォロワーが見れない', async () => {
const res = await show(folM.id, other);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] followers-mentionを未認証が見れない', async () => {
const res = await show(folM.id, null);
assert.strictEqual(res.body.isHidden, true);
});
// specified
it('[show] specified-mentionを自分が見れる', async () => {
const res = await show(speM.id, alice);
assert.strictEqual(res.body.text, '@target2 x');
});
it('[show] specified-mentionを指定ユーザーが見れる', async () => {
const res = await show(speM.id, target);
assert.strictEqual(res.body.text, '@target2 x');
});
it('[show] specified-mentionをされた人が指定されてなかったら見れない', async () => {
const res = await show(speM.id, target2);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] specified-mentionをフォロワーが見れない', async () => {
const res = await show(speM.id, follower);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] specified-mentionを非フォロワーが見れない', async () => {
const res = await show(speM.id, other);
assert.strictEqual(res.body.isHidden, true);
});
it('[show] specified-mentionを未認証が見れない', async () => {
const res = await show(speM.id, null);
assert.strictEqual(res.body.isHidden, true);
});
//#endregion
//#region HTL
it('[HTL] public-post が 自分が見れる', async () => {
const res = await request('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === pub.id);
assert.strictEqual(notes[0].text, 'x');
});
it('[HTL] public-post が 非フォロワーから見れない', async () => {
const res = await request('/notes/timeline', { limit: 100 }, other);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === pub.id);
assert.strictEqual(notes.length, 0);
});
it('[HTL] followers-post が フォロワーから見れる', async () => {
const res = await request('/notes/timeline', { limit: 100 }, follower);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === fol.id);
assert.strictEqual(notes[0].text, 'x');
});
//#endregion
//#region RTL
it('[replies] followers-reply が フォロワーから見れる', async () => {
const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, follower);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folR.id);
assert.strictEqual(notes[0].text, 'x');
});
it('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => {
const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, other);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folR.id);
assert.strictEqual(notes.length, 0);
});
it('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, target);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folR.id);
assert.strictEqual(notes[0].text, 'x');
});
//#endregion
//#region MTL
it('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
const res = await request('/notes/mentions', { limit: 100 }, target);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folR.id);
assert.strictEqual(notes[0].text, 'x');
});
it('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => {
const res = await request('/notes/mentions', { limit: 100 }, target);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folM.id);
assert.strictEqual(notes[0].text, '@target x');
});
//#endregion
});
});
*/

View file

@ -0,0 +1,83 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { async, signup, request, post, react, uploadFile, startServer, shutdownServer } from '../utils.js';
describe('API', () => {
let p: childProcess.ChildProcess;
let alice: any;
let bob: any;
let carol: any;
beforeAll(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
describe('General validation', () => {
it('wrong type', async(async () => {
const res = await request('/test', {
required: true,
string: 42,
});
assert.strictEqual(res.status, 400);
}));
it('missing require param', async(async () => {
const res = await request('/test', {
string: 'a',
});
assert.strictEqual(res.status, 400);
}));
it('invalid misskey:id (empty string)', async(async () => {
const res = await request('/test', {
required: true,
id: '',
});
assert.strictEqual(res.status, 400);
}));
it('valid misskey:id', async(async () => {
const res = await request('/test', {
required: true,
id: '8wvhjghbxu',
});
assert.strictEqual(res.status, 200);
}));
it('default value', async(async () => {
const res = await request('/test', {
required: true,
string: 'a',
});
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.default, 'hello');
}));
it('can set null even if it has default value', async(async () => {
const res = await request('/test', {
required: true,
nullableDefault: null,
});
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.nullableDefault, null);
}));
it('cannot set undefined if it has default value', async(async () => {
const res = await request('/test', {
required: true,
nullableDefault: undefined,
});
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.nullableDefault, 'hello');
}));
});
});

View file

@ -0,0 +1,85 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { signup, request, post, startServer, shutdownServer } from '../utils.js';
describe('Block', () => {
let p: childProcess.ChildProcess;
// alice blocks bob
let alice: any;
let bob: any;
let carol: any;
beforeAll(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
it('Block作成', async () => {
const res = await request('/blocking/create', {
userId: bob.id,
}, alice);
assert.strictEqual(res.status, 200);
});
it('ブロックされているユーザーをフォローできない', async () => {
const res = await request('/following/create', { userId: alice.id }, bob);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
});
it('ブロックされているユーザーにリアクションできない', async () => {
const note = await post(alice, { text: 'hello' });
const res = await request('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
});
it('ブロックされているユーザーに返信できない', async () => {
const note = await post(alice, { text: 'hello' });
const res = await request('/notes/create', { replyId: note.id, text: 'yo' }, bob);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
});
it('ブロックされているユーザーのートをRenoteできない', async () => {
const note = await post(alice, { text: 'hello' });
const res = await request('/notes/create', { renoteId: note.id, text: 'yo' }, bob);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
});
// TODO: ユーザーリストに入れられないテスト
// TODO: ユーザーリストから除外されるテスト
it('タイムライン(LTL)にブロックされているユーザーの投稿が含まれない', async () => {
const aliceNote = await post(alice);
const bobNote = await post(bob);
const carolNote = await post(carol);
const res = await request('/notes/local-timeline', {}, bob);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
});
});

View file

@ -0,0 +1,881 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import * as openapi from '@redocly/openapi-core';
import { startServer, signup, post, request, simpleGet, port, shutdownServer, api } from '../utils.js';
describe('Endpoints', () => {
let p: childProcess.ChildProcess;
let alice: any;
let bob: any;
beforeAll(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
describe('signup', () => {
it('不正なユーザー名でアカウントが作成できない', async () => {
const res = await request('api/signup', {
username: 'test.',
password: 'test',
});
assert.strictEqual(res.status, 400);
});
it('空のパスワードでアカウントが作成できない', async () => {
const res = await request('api/signup', {
username: 'test',
password: '',
});
assert.strictEqual(res.status, 400);
});
it('正しくアカウントが作成できる', async () => {
const me = {
username: 'test1',
password: 'test1',
};
const res = await request('api/signup', me);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.username, me.username);
});
it('同じユーザー名のアカウントは作成できない', async () => {
const res = await request('api/signup', {
username: 'test1',
password: 'test1',
});
assert.strictEqual(res.status, 400);
});
});
describe('signin', () => {
it('間違ったパスワードでサインインできない', async () => {
const res = await request('api/signin', {
username: 'test1',
password: 'bar',
});
assert.strictEqual(res.status, 403);
});
it('クエリをインジェクションできない', async () => {
const res = await request('api/signin', {
username: 'test1',
password: {
$gt: '',
},
});
assert.strictEqual(res.status, 400);
});
it('正しい情報でサインインできる', async () => {
const res = await request('api/signin', {
username: 'test1',
password: 'test1',
});
assert.strictEqual(res.status, 200);
});
});
describe('i/update', () => {
it('アカウント設定を更新できる', async () => {
const myName = '大室櫻子';
const myLocation = '七森中';
const myBirthday = '2000-09-07';
const res = await api('/i/update', {
name: myName,
location: myLocation,
birthday: myBirthday,
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.name, myName);
assert.strictEqual(res.body.location, myLocation);
assert.strictEqual(res.body.birthday, myBirthday);
});
it('名前を空白にできない', async () => {
const res = await api('/i/update', {
name: ' ',
}, alice);
assert.strictEqual(res.status, 400);
});
it('誕生日の設定を削除できる', async () => {
await api('/i/update', {
birthday: '2000-09-07',
}, alice);
const res = await api('/i/update', {
birthday: null,
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.birthday, null);
});
it('不正な誕生日の形式で怒られる', async () => {
const res = await api('/i/update', {
birthday: '2000/09/07',
}, alice);
assert.strictEqual(res.status, 400);
});
});
describe('users/show', () => {
it('ユーザーが取得できる', async () => {
const res = await api('/users/show', {
userId: alice.id,
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.id, alice.id);
});
it('ユーザーが存在しなかったら怒る', async () => {
const res = await api('/users/show', {
userId: '000000000000000000000000',
});
assert.strictEqual(res.status, 400);
});
it('間違ったIDで怒られる', async () => {
const res = await api('/users/show', {
userId: 'kyoppie',
});
assert.strictEqual(res.status, 400);
});
});
describe('notes/show', () => {
it('投稿が取得できる', async () => {
const myPost = await post(alice, {
text: 'test',
});
const res = await api('/notes/show', {
noteId: myPost.id,
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.id, myPost.id);
assert.strictEqual(res.body.text, myPost.text);
});
it('投稿が存在しなかったら怒る', async () => {
const res = await api('/notes/show', {
noteId: '000000000000000000000000',
});
assert.strictEqual(res.status, 400);
});
it('間違ったIDで怒られる', async () => {
const res = await api('/notes/show', {
noteId: 'kyoppie',
});
assert.strictEqual(res.status, 400);
});
});
describe('notes/reactions/create', () => {
it('リアクションできる', async () => {
const bobPost = await post(bob);
const alice = await signup({ username: 'alice' });
const res = await api('/notes/reactions/create', {
noteId: bobPost.id,
reaction: '🚀',
}, alice);
assert.strictEqual(res.status, 204);
const resNote = await api('/notes/show', {
noteId: bobPost.id,
}, alice);
assert.strictEqual(resNote.status, 200);
assert.strictEqual(resNote.body.reactions['🚀'], [alice.id]);
});
it('自分の投稿にもリアクションできる', async () => {
const myPost = await post(alice);
const res = await api('/notes/reactions/create', {
noteId: myPost.id,
reaction: '🚀',
}, alice);
assert.strictEqual(res.status, 204);
});
it('二重にリアクションできない', async () => {
const bobPost = await post(bob);
await api('/notes/reactions/create', {
noteId: bobPost.id,
reaction: '🥰',
}, alice);
const res = await api('/notes/reactions/create', {
noteId: bobPost.id,
reaction: '🚀',
}, alice);
assert.strictEqual(res.status, 400);
});
it('存在しない投稿にはリアクションできない', async () => {
const res = await api('/notes/reactions/create', {
noteId: '000000000000000000000000',
reaction: '🚀',
}, alice);
assert.strictEqual(res.status, 400);
});
it('空のパラメータで怒られる', async () => {
const res = await api('/notes/reactions/create', {}, alice);
assert.strictEqual(res.status, 400);
});
it('間違ったIDで怒られる', async () => {
const res = await api('/notes/reactions/create', {
noteId: 'kyoppie',
reaction: '🚀',
}, alice);
assert.strictEqual(res.status, 400);
});
});
describe('following/create', () => {
it('フォローできる', async () => {
const res = await api('/following/create', {
userId: alice.id,
}, bob);
assert.strictEqual(res.status, 200);
});
it('既にフォローしている場合は怒る', async () => {
const res = await api('/following/create', {
userId: alice.id,
}, bob);
assert.strictEqual(res.status, 400);
});
it('存在しないユーザーはフォローできない', async () => {
const res = await api('/following/create', {
userId: '000000000000000000000000',
}, alice);
assert.strictEqual(res.status, 400);
});
it('自分自身はフォローできない', async () => {
const res = await api('/following/create', {
userId: alice.id,
}, alice);
assert.strictEqual(res.status, 400);
});
it('空のパラメータで怒られる', async () => {
const res = await api('/following/create', {}, alice);
assert.strictEqual(res.status, 400);
});
it('間違ったIDで怒られる', async () => {
const res = await api('/following/create', {
userId: 'foo',
}, alice);
assert.strictEqual(res.status, 400);
});
});
describe('following/delete', () => {
it('フォロー解除できる', async () => {
await api('/following/create', {
userId: alice.id,
}, bob);
const res = await api('/following/delete', {
userId: alice.id,
}, bob);
assert.strictEqual(res.status, 200);
});
it('フォローしていない場合は怒る', async () => {
const res = await api('/following/delete', {
userId: alice.id,
}, bob);
assert.strictEqual(res.status, 400);
});
it('存在しないユーザーはフォロー解除できない', async () => {
const res = await api('/following/delete', {
userId: '000000000000000000000000',
}, alice);
assert.strictEqual(res.status, 400);
});
it('自分自身はフォロー解除できない', async () => {
const res = await api('/following/delete', {
userId: alice.id,
}, alice);
assert.strictEqual(res.status, 400);
});
it('空のパラメータで怒られる', async () => {
const res = await api('/following/delete', {}, alice);
assert.strictEqual(res.status, 400);
});
it('間違ったIDで怒られる', async () => {
const res = await api('/following/delete', {
userId: 'kyoppie',
}, alice);
assert.strictEqual(res.status, 400);
});
});
/*
describe('/i', () => {
it('', async () => {
});
});
*/
});
/*
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { async, signup, request, post, react, uploadFile, startServer, shutdownServer } from './utils.js';
describe('API: Endpoints', () => {
let p: childProcess.ChildProcess;
let alice: any;
let bob: any;
let carol: any;
before(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
});
after(async () => {
await shutdownServer(p);
});
describe('drive', () => {
it('ドライブ情報を取得できる', async () => {
await uploadFile({
userId: alice.id,
size: 256
});
await uploadFile({
userId: alice.id,
size: 512
});
await uploadFile({
userId: alice.id,
size: 1024
});
const res = await api('/drive', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
expect(res.body).have.property('usage').eql(1792);
}));
});
describe('drive/files/create', () => {
it('ファイルを作成できる', async () => {
const res = await uploadFile(alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.name, 'Lenna.png');
}));
it('ファイルに名前を付けられる', async () => {
const res = await assert.request(server)
.post('/drive/files/create')
.field('i', alice.token)
.field('name', 'Belmond.png')
.attach('file', fs.readFileSync(__dirname + '/resources/Lenna.png'), 'Lenna.png');
expect(res).have.status(200);
expect(res.body).be.a('object');
expect(res.body).have.property('name').eql('Belmond.png');
}));
it('ファイル無しで怒られる', async () => {
const res = await api('/drive/files/create', {}, alice);
assert.strictEqual(res.status, 400);
}));
it('SVGファイルを作成できる', async () => {
const res = await uploadFile(alice, __dirname + '/resources/image.svg');
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.name, 'image.svg');
assert.strictEqual(res.body.type, 'image/svg+xml');
}));
});
describe('drive/files/update', () => {
it('名前を更新できる', async () => {
const file = await uploadFile(alice);
const newName = 'いちごパスタ.png';
const res = await api('/drive/files/update', {
fileId: file.id,
name: newName
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.name, newName);
}));
it('他人のファイルは更新できない', async () => {
const file = await uploadFile(bob);
const res = await api('/drive/files/update', {
fileId: file.id,
name: 'いちごパスタ.png'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('親フォルダを更新できる', async () => {
const file = await uploadFile(alice);
const folder = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: folder.id
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.folderId, folder.id);
}));
it('親フォルダを無しにできる', async () => {
const file = await uploadFile(alice);
const folder = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
await api('/drive/files/update', {
fileId: file.id,
folderId: folder.id
}, alice);
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: null
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.folderId, null);
}));
it('他人のフォルダには入れられない', async () => {
const file = await uploadFile(alice);
const folder = (await api('/drive/folders/create', {
name: 'test'
}, bob)).body;
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: folder.id
}, alice);
assert.strictEqual(res.status, 400);
}));
it('存在しないフォルダで怒られる', async () => {
const file = await uploadFile(alice);
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: '000000000000000000000000'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('不正なフォルダIDで怒られる', async () => {
const file = await uploadFile(alice);
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: 'foo'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('ファイルが存在しなかったら怒る', async () => {
const res = await api('/drive/files/update', {
fileId: '000000000000000000000000',
name: 'いちごパスタ.png'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('間違ったIDで怒られる', async () => {
const res = await api('/drive/files/update', {
fileId: 'kyoppie',
name: 'いちごパスタ.png'
}, alice);
assert.strictEqual(res.status, 400);
}));
});
describe('drive/folders/create', () => {
it('フォルダを作成できる', async () => {
const res = await api('/drive/folders/create', {
name: 'test'
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.name, 'test');
}));
});
describe('drive/folders/update', () => {
it('名前を更新できる', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
name: 'new name'
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.name, 'new name');
}));
it('他人のフォルダを更新できない', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
}, bob)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
name: 'new name'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('親フォルダを更新できる', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const parentFolder = (await api('/drive/folders/create', {
name: 'parent'
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: parentFolder.id
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.parentId, parentFolder.id);
}));
it('親フォルダを無しに更新できる', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const parentFolder = (await api('/drive/folders/create', {
name: 'parent'
}, alice)).body;
await api('/drive/folders/update', {
folderId: folder.id,
parentId: parentFolder.id
}, alice);
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: null
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.parentId, null);
}));
it('他人のフォルダを親フォルダに設定できない', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const parentFolder = (await api('/drive/folders/create', {
name: 'parent'
}, bob)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: parentFolder.id
}, alice);
assert.strictEqual(res.status, 400);
}));
it('フォルダが循環するような構造にできない', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const parentFolder = (await api('/drive/folders/create', {
name: 'parent'
}, alice)).body;
await api('/drive/folders/update', {
folderId: parentFolder.id,
parentId: folder.id
}, alice);
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: parentFolder.id
}, alice);
assert.strictEqual(res.status, 400);
}));
it('フォルダが循環するような構造にできない(再帰的)', async () => {
const folderA = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const folderB = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const folderC = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
await api('/drive/folders/update', {
folderId: folderB.id,
parentId: folderA.id
}, alice);
await api('/drive/folders/update', {
folderId: folderC.id,
parentId: folderB.id
}, alice);
const res = await api('/drive/folders/update', {
folderId: folderA.id,
parentId: folderC.id
}, alice);
assert.strictEqual(res.status, 400);
}));
it('フォルダが循環するような構造にできない(自身)', async () => {
const folderA = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folderA.id,
parentId: folderA.id
}, alice);
assert.strictEqual(res.status, 400);
}));
it('存在しない親フォルダを設定できない', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: '000000000000000000000000'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('不正な親フォルダIDで怒られる', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: 'foo'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('存在しないフォルダを更新できない', async () => {
const res = await api('/drive/folders/update', {
folderId: '000000000000000000000000'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('不正なフォルダIDで怒られる', async () => {
const res = await api('/drive/folders/update', {
folderId: 'foo'
}, alice);
assert.strictEqual(res.status, 400);
}));
});
describe('messaging/messages/create', () => {
it('メッセージを送信できる', async () => {
const res = await api('/messaging/messages/create', {
userId: bob.id,
text: 'test'
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.text, 'test');
}));
it('自分自身にはメッセージを送信できない', async () => {
const res = await api('/messaging/messages/create', {
userId: alice.id,
text: 'Yo'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('存在しないユーザーにはメッセージを送信できない', async () => {
const res = await api('/messaging/messages/create', {
userId: '000000000000000000000000',
text: 'test'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('不正なユーザーIDで怒られる', async () => {
const res = await api('/messaging/messages/create', {
userId: 'foo',
text: 'test'
}, alice);
assert.strictEqual(res.status, 400);
}));
it('テキストが無くて怒られる', async () => {
const res = await api('/messaging/messages/create', {
userId: bob.id
}, alice);
assert.strictEqual(res.status, 400);
}));
it('文字数オーバーで怒られる', async () => {
const res = await api('/messaging/messages/create', {
userId: bob.id,
text: '!'.repeat(1001)
}, alice);
assert.strictEqual(res.status, 400);
}));
});
describe('notes/replies', () => {
it('自分に閲覧権限のない投稿は含まれない', async () => {
const alicePost = await post(alice, {
text: 'foo'
});
await post(bob, {
replyId: alicePost.id,
text: 'bar',
visibility: 'specified',
visibleUserIds: [alice.id]
});
const res = await api('/notes/replies', {
noteId: alicePost.id
}, carol);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.length, 0);
}));
});
describe('notes/timeline', () => {
it('フォロワー限定投稿が含まれる', async () => {
await api('/following/create', {
userId: alice.id
}, bob);
const alicePost = await post(alice, {
text: 'foo',
visibility: 'followers'
});
const res = await api('/notes/timeline', {}, bob);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.length, 1);
assert.strictEqual(res.body[0].id, alicePost.id);
}));
});
});
*/

View file

@ -0,0 +1,205 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import * as openapi from '@redocly/openapi-core';
import { startServer, signup, post, request, simpleGet, port, shutdownServer } from '../utils.js';
// Request Accept
const ONLY_AP = 'application/activity+json';
const PREFER_AP = 'application/activity+json, */*';
const PREFER_HTML = 'text/html, */*';
const UNSPECIFIED = '*/*';
// Response Contet-Type
const AP = 'application/activity+json; charset=utf-8';
const JSON = 'application/json; charset=utf-8';
const HTML = 'text/html; charset=utf-8';
describe('Fetch resource', () => {
let p: childProcess.ChildProcess;
let alice: any;
let alicesPost: any;
beforeAll(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
alicesPost = await post(alice, {
text: 'test',
});
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
describe('Common', () => {
it('meta', async () => {
const res = await request('/meta', {
});
assert.strictEqual(res.status, 200);
});
it('GET root', async () => {
const res = await simpleGet('/');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, HTML);
});
it('GET docs', async () => {
const res = await simpleGet('/docs/ja-JP/about');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, HTML);
});
it('GET api-doc', async () => {
const res = await simpleGet('/api-doc');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, HTML);
});
it('GET api.json', async () => {
const res = await simpleGet('/api.json');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, JSON);
});
it('Validate api.json', async () => {
const config = await openapi.loadConfig();
const result = await openapi.bundle({
config,
ref: `http://localhost:${port}/api.json`,
});
for (const problem of result.problems) {
console.log(`${problem.message} - ${problem.location[0]?.pointer}`);
}
assert.strictEqual(result.problems.length, 0);
});
it('GET favicon.ico', async () => {
const res = await simpleGet('/favicon.ico');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, 'image/x-icon');
});
it('GET apple-touch-icon.png', async () => {
const res = await simpleGet('/apple-touch-icon.png');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, 'image/png');
});
it('GET twemoji svg', async () => {
const res = await simpleGet('/twemoji/2764.svg');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, 'image/svg+xml');
});
it('GET twemoji svg with hyphen', async () => {
const res = await simpleGet('/twemoji/2764-fe0f-200d-1f525.svg');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, 'image/svg+xml');
});
});
describe('/@:username', () => {
it('Only AP => AP', async () => {
const res = await simpleGet(`/@${alice.username}`, ONLY_AP);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, AP);
});
it('Prefer AP => AP', async () => {
const res = await simpleGet(`/@${alice.username}`, PREFER_AP);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, AP);
});
it('Prefer HTML => HTML', async () => {
const res = await simpleGet(`/@${alice.username}`, PREFER_HTML);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, HTML);
});
it('Unspecified => HTML', async () => {
const res = await simpleGet(`/@${alice.username}`, UNSPECIFIED);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, HTML);
});
});
describe('/users/:id', () => {
it('Only AP => AP', async () => {
const res = await simpleGet(`/users/${alice.id}`, ONLY_AP);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, AP);
});
it('Prefer AP => AP', async () => {
const res = await simpleGet(`/users/${alice.id}`, PREFER_AP);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, AP);
});
it('Prefer HTML => Redirect to /@:username', async () => {
const res = await simpleGet(`/users/${alice.id}`, PREFER_HTML);
assert.strictEqual(res.status, 302);
assert.strictEqual(res.location, `/@${alice.username}`);
});
it('Undecided => HTML', async () => {
const res = await simpleGet(`/users/${alice.id}`, UNSPECIFIED);
assert.strictEqual(res.status, 302);
assert.strictEqual(res.location, `/@${alice.username}`);
});
});
describe('/notes/:id', () => {
it('Only AP => AP', async () => {
const res = await simpleGet(`/notes/${alicesPost.id}`, ONLY_AP);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, AP);
});
it('Prefer AP => AP', async () => {
const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_AP);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, AP);
});
it('Prefer HTML => HTML', async () => {
const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_HTML);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, HTML);
});
it('Unspecified => HTML', async () => {
const res = await simpleGet(`/notes/${alicesPost.id}`, UNSPECIFIED);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, HTML);
});
});
describe('Feeds', () => {
it('RSS', async () => {
const res = await simpleGet(`/@${alice.username}.rss`, UNSPECIFIED);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, 'application/rss+xml; charset=utf-8');
});
it('ATOM', async () => {
const res = await simpleGet(`/@${alice.username}.atom`, UNSPECIFIED);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, 'application/atom+xml; charset=utf-8');
});
it('JSON', async () => {
const res = await simpleGet(`/@${alice.username}.json`, UNSPECIFIED);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, 'application/json; charset=utf-8');
});
});
});

View file

@ -0,0 +1,167 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { signup, request, post, react, connectStream, startServer, shutdownServer, simpleGet } from '../utils.js';
describe('FF visibility', () => {
let p: childProcess.ChildProcess;
let alice: any;
let bob: any;
let carol: any;
beforeAll(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
it('ffVisibility が public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {
await request('/i/update', {
ffVisibility: 'public',
}, alice);
const followingRes = await request('/users/following', {
userId: alice.id,
}, bob);
const followersRes = await request('/users/followers', {
userId: alice.id,
}, bob);
assert.strictEqual(followingRes.status, 200);
assert.strictEqual(Array.isArray(followingRes.body), true);
assert.strictEqual(followersRes.status, 200);
assert.strictEqual(Array.isArray(followersRes.body), true);
});
it('ffVisibility が followers なユーザーのフォロー/フォロワーを自分で見れる', async () => {
await request('/i/update', {
ffVisibility: 'followers',
}, alice);
const followingRes = await request('/users/following', {
userId: alice.id,
}, alice);
const followersRes = await request('/users/followers', {
userId: alice.id,
}, alice);
assert.strictEqual(followingRes.status, 200);
assert.strictEqual(Array.isArray(followingRes.body), true);
assert.strictEqual(followersRes.status, 200);
assert.strictEqual(Array.isArray(followersRes.body), true);
});
it('ffVisibility が followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => {
await request('/i/update', {
ffVisibility: 'followers',
}, alice);
const followingRes = await request('/users/following', {
userId: alice.id,
}, bob);
const followersRes = await request('/users/followers', {
userId: alice.id,
}, bob);
assert.strictEqual(followingRes.status, 400);
assert.strictEqual(followersRes.status, 400);
});
it('ffVisibility が followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => {
await request('/i/update', {
ffVisibility: 'followers',
}, alice);
await request('/following/create', {
userId: alice.id,
}, bob);
const followingRes = await request('/users/following', {
userId: alice.id,
}, bob);
const followersRes = await request('/users/followers', {
userId: alice.id,
}, bob);
assert.strictEqual(followingRes.status, 200);
assert.strictEqual(Array.isArray(followingRes.body), true);
assert.strictEqual(followersRes.status, 200);
assert.strictEqual(Array.isArray(followersRes.body), true);
});
it('ffVisibility が private なユーザーのフォロー/フォロワーを自分で見れる', async () => {
await request('/i/update', {
ffVisibility: 'private',
}, alice);
const followingRes = await request('/users/following', {
userId: alice.id,
}, alice);
const followersRes = await request('/users/followers', {
userId: alice.id,
}, alice);
assert.strictEqual(followingRes.status, 200);
assert.strictEqual(Array.isArray(followingRes.body), true);
assert.strictEqual(followersRes.status, 200);
assert.strictEqual(Array.isArray(followersRes.body), true);
});
it('ffVisibility が private なユーザーのフォロー/フォロワーを他人が見れない', async () => {
await request('/i/update', {
ffVisibility: 'private',
}, alice);
const followingRes = await request('/users/following', {
userId: alice.id,
}, bob);
const followersRes = await request('/users/followers', {
userId: alice.id,
}, bob);
assert.strictEqual(followingRes.status, 400);
assert.strictEqual(followersRes.status, 400);
});
describe('AP', () => {
it('ffVisibility が public 以外ならばAPからは取得できない', async () => {
{
await request('/i/update', {
ffVisibility: 'public',
}, alice);
const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json');
const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json');
assert.strictEqual(followingRes.status, 200);
assert.strictEqual(followersRes.status, 200);
}
{
await request('/i/update', {
ffVisibility: 'followers',
}, alice);
const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json').catch(res => ({ status: res.statusCode }));
const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json').catch(res => ({ status: res.statusCode }));
assert.strictEqual(followingRes.status, 403);
assert.strictEqual(followersRes.status, 403);
}
{
await request('/i/update', {
ffVisibility: 'private',
}, alice);
const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json').catch(res => ({ status: res.statusCode }));
const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json').catch(res => ({ status: res.statusCode }));
assert.strictEqual(followingRes.status, 403);
assert.strictEqual(followersRes.status, 403);
}
});
});
});

View file

@ -0,0 +1,123 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { signup, request, post, react, startServer, shutdownServer, waitFire } from '../utils.js';
describe('Mute', () => {
let p: childProcess.ChildProcess;
// alice mutes carol
let alice: any;
let bob: any;
let carol: any;
beforeAll(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
it('ミュート作成', async () => {
const res = await request('/mute/create', {
userId: carol.id,
}, alice);
assert.strictEqual(res.status, 204);
});
it('「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない', async () => {
const bobNote = await post(bob, { text: '@alice hi' });
const carolNote = await post(carol, { text: '@alice hi' });
const res = await request('/notes/mentions', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
});
it('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
await post(carol, { text: '@alice hi' });
const res = await request('/i', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.hasUnreadMentions, false);
});
it('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadMention');
assert.strictEqual(fired, false);
});
it('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
await request('/notifications/mark-all-as-read', {}, alice);
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadNotification');
assert.strictEqual(fired, false);
});
describe('Timeline', () => {
it('タイムラインにミュートしているユーザーの投稿が含まれない', async () => {
const aliceNote = await post(alice);
const bobNote = await post(bob);
const carolNote = await post(carol);
const res = await request('/notes/local-timeline', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
});
it('タイムラインにミュートしているユーザーの投稿のRenoteが含まれない', async () => {
const aliceNote = await post(alice);
const carolNote = await post(carol);
const bobNote = await post(bob, {
renoteId: carolNote.id,
});
const res = await request('/notes/local-timeline', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
});
});
describe('Notification', () => {
it('通知にミュートしているユーザーの通知が含まれない(リアクション)', async () => {
const aliceNote = await post(alice);
await react(bob, aliceNote, 'like');
await react(carol, aliceNote, 'like');
const res = await request('/i/notifications', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
});
});
});

View file

@ -0,0 +1,370 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { Note } from '../../src/models/entities/note.js';
import { async, signup, request, post, uploadUrl, startServer, shutdownServer, initTestDb, api } from '../utils.js';
describe('Note', () => {
let p: childProcess.ChildProcess;
let Notes: any;
let alice: any;
let bob: any;
beforeAll(async () => {
p = await startServer();
const connection = await initTestDb(true);
Notes = connection.getRepository(Note);
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
it('投稿できる', async () => {
const post = {
text: 'test',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.text, post.text);
});
it('ファイルを添付できる', async () => {
const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
const res = await request('/notes/create', {
fileIds: [file.id],
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.deepStrictEqual(res.body.createdNote.fileIds, [file.id]);
}, 1000 * 10);
it('他人のファイルは無視', async () => {
const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
const res = await request('/notes/create', {
text: 'test',
fileIds: [file.id],
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.deepStrictEqual(res.body.createdNote.fileIds, []);
}, 1000 * 10);
it('存在しないファイルは無視', async () => {
const res = await request('/notes/create', {
text: 'test',
fileIds: ['000000000000000000000000'],
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.deepStrictEqual(res.body.createdNote.fileIds, []);
});
it('不正なファイルIDは無視', async () => {
const res = await request('/notes/create', {
fileIds: ['kyoppie'],
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.deepStrictEqual(res.body.createdNote.fileIds, []);
});
it('返信できる', async () => {
const bobPost = await post(bob, {
text: 'foo',
});
const alicePost = {
text: 'bar',
replyId: bobPost.id,
};
const res = await request('/notes/create', alicePost, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.text, alicePost.text);
assert.strictEqual(res.body.createdNote.replyId, alicePost.replyId);
assert.strictEqual(res.body.createdNote.reply.text, bobPost.text);
});
it('renoteできる', async () => {
const bobPost = await post(bob, {
text: 'test',
});
const alicePost = {
renoteId: bobPost.id,
};
const res = await request('/notes/create', alicePost, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
});
it('引用renoteできる', async () => {
const bobPost = await post(bob, {
text: 'test',
});
const alicePost = {
text: 'test',
renoteId: bobPost.id,
};
const res = await request('/notes/create', alicePost, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.text, alicePost.text);
assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
});
it('文字数ぎりぎりで怒られない', async () => {
const post = {
text: '!'.repeat(3000),
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 200);
});
it('文字数オーバーで怒られる', async () => {
const post = {
text: '!'.repeat(3001),
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
it('存在しないリプライ先で怒られる', async () => {
const post = {
text: 'test',
replyId: '000000000000000000000000',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
it('存在しないrenote対象で怒られる', async () => {
const post = {
renoteId: '000000000000000000000000',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
it('不正なリプライ先IDで怒られる', async () => {
const post = {
text: 'test',
replyId: 'foo',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
it('不正なrenote対象IDで怒られる', async () => {
const post = {
renoteId: 'foo',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
it('存在しないユーザーにメンションできる', async () => {
const post = {
text: '@ghost yo',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.text, post.text);
});
it('同じユーザーに複数メンションしても内部的にまとめられる', async () => {
const post = {
text: '@bob @bob @bob yo',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.text, post.text);
const noteDoc = await Notes.findOneBy({ id: res.body.createdNote.id });
assert.deepStrictEqual(noteDoc.mentions, [bob.id]);
});
describe('notes/create', () => {
it('投票を添付できる', async () => {
const res = await request('/notes/create', {
text: 'test',
poll: {
choices: ['foo', 'bar'],
},
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.poll != null, true);
});
it('投票の選択肢が無くて怒られる', async () => {
const res = await request('/notes/create', {
poll: {},
}, alice);
assert.strictEqual(res.status, 400);
});
it('投票の選択肢が無くて怒られる (空の配列)', async () => {
const res = await request('/notes/create', {
poll: {
choices: [],
},
}, alice);
assert.strictEqual(res.status, 400);
});
it('投票の選択肢が1つで怒られる', async () => {
const res = await request('/notes/create', {
poll: {
choices: ['Strawberry Pasta'],
},
}, alice);
assert.strictEqual(res.status, 400);
});
it('投票できる', async () => {
const { body } = await request('/notes/create', {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
},
}, alice);
const res = await request('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 1,
}, alice);
assert.strictEqual(res.status, 204);
});
it('複数投票できない', async () => {
const { body } = await request('/notes/create', {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
},
}, alice);
await request('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 0,
}, alice);
const res = await request('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 2,
}, alice);
assert.strictEqual(res.status, 400);
});
it('許可されている場合は複数投票できる', async () => {
const { body } = await request('/notes/create', {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
multiple: true,
},
}, alice);
await request('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 0,
}, alice);
await request('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 1,
}, alice);
const res = await request('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 2,
}, alice);
assert.strictEqual(res.status, 204);
});
it('締め切られている場合は投票できない', async () => {
const { body } = await request('/notes/create', {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
expiredAfter: 1,
},
}, alice);
await new Promise(x => setTimeout(x, 2));
const res = await request('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 1,
}, alice);
assert.strictEqual(res.status, 400);
});
});
describe('notes/delete', () => {
it('delete a reply', async () => {
const mainNoteRes = await api('notes/create', {
text: 'main post',
}, alice);
const replyOneRes = await api('notes/create', {
text: 'reply one',
replyId: mainNoteRes.body.createdNote.id,
}, alice);
const replyTwoRes = await api('notes/create', {
text: 'reply two',
replyId: mainNoteRes.body.createdNote.id,
}, alice);
const deleteOneRes = await api('notes/delete', {
noteId: replyOneRes.body.createdNote.id,
}, alice);
assert.strictEqual(deleteOneRes.status, 204);
let mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id });
assert.strictEqual(mainNote.repliesCount, 1);
const deleteTwoRes = await api('notes/delete', {
noteId: replyTwoRes.body.createdNote.id,
}, alice);
assert.strictEqual(deleteTwoRes.status, 204);
mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id });
assert.strictEqual(mainNote.repliesCount, 0);
});
});
});

View file

@ -0,0 +1,545 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { Following } from '../../src/models/entities/following.js';
import { connectStream, signup, api, post, startServer, shutdownServer, initTestDb, waitFire } from '../utils.js';
describe('Streaming', () => {
let p: childProcess.ChildProcess;
let Followings: any;
const follow = async (follower: any, followee: any) => {
await Followings.save({
id: 'a',
createdAt: new Date(),
followerId: follower.id,
followeeId: followee.id,
followerHost: follower.host,
followerInbox: null,
followerSharedInbox: null,
followeeHost: followee.host,
followeeInbox: null,
followeeSharedInbox: null,
});
};
describe('Streaming', () => {
// Local users
let ayano: any;
let kyoko: any;
let chitose: any;
// Remote users
let akari: any;
let chinatsu: any;
let kyokoNote: any;
let list: any;
beforeAll(async () => {
p = await startServer();
const connection = await initTestDb(true);
Followings = connection.getRepository(Following);
ayano = await signup({ username: 'ayano' });
kyoko = await signup({ username: 'kyoko' });
chitose = await signup({ username: 'chitose' });
akari = await signup({ username: 'akari', host: 'example.com' });
chinatsu = await signup({ username: 'chinatsu', host: 'example.com' });
kyokoNote = await post(kyoko, { text: 'foo' });
// Follow: ayano => kyoko
await api('following/create', { userId: kyoko.id }, ayano);
// Follow: ayano => akari
await follow(ayano, akari);
// List: chitose => ayano, kyoko
list = await api('users/lists/create', {
name: 'my list',
}, chitose).then(x => x.body);
await api('users/lists/push', {
listId: list.id,
userId: ayano.id,
}, chitose);
await api('users/lists/push', {
listId: list.id,
userId: kyoko.id,
}, chitose);
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
describe('Events', () => {
it('mention event', async () => {
const fired = await waitFire(
kyoko, 'main', // kyoko:main
() => post(ayano, { text: 'foo @kyoko bar' }), // ayano mention => kyoko
msg => msg.type === 'mention' && msg.body.userId === ayano.id, // wait ayano
);
assert.strictEqual(fired, true);
});
it('renote event', async () => {
const fired = await waitFire(
kyoko, 'main', // kyoko:main
() => post(ayano, { renoteId: kyokoNote.id }), // ayano renote
msg => msg.type === 'renote' && msg.body.renoteId === kyokoNote.id, // wait renote
);
assert.strictEqual(fired, true);
});
});
describe('Home Timeline', () => {
it('自分の投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'homeTimeline', // ayano:Home
() => api('notes/create', { text: 'foo' }, ayano), // ayano posts
msg => msg.type === 'note' && msg.body.text === 'foo',
);
assert.strictEqual(fired, true);
});
it('フォローしているユーザーの投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'homeTimeline', // ayano:home
() => api('notes/create', { text: 'foo' }, kyoko), // kyoko posts
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
);
assert.strictEqual(fired, true);
});
it('フォローしていないユーザーの投稿は流れない', async () => {
const fired = await waitFire(
kyoko, 'homeTimeline', // kyoko:home
() => api('notes/create', { text: 'foo' }, ayano), // ayano posts
msg => msg.type === 'note' && msg.body.userId === ayano.id, // wait ayano
);
assert.strictEqual(fired, false);
});
it('フォローしているユーザーのダイレクト投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'homeTimeline', // ayano:home
() => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), // kyoko dm => ayano
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
);
assert.strictEqual(fired, true);
});
it('フォローしているユーザーでも自分が指定されていないダイレクト投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'homeTimeline', // ayano:home
() => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [chitose.id] }, kyoko), // kyoko dm => chitose
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
);
assert.strictEqual(fired, false);
});
}); // Home
describe('Local Timeline', () => {
it('自分の投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'localTimeline', // ayano:Local
() => api('notes/create', { text: 'foo' }, ayano), // ayano posts
msg => msg.type === 'note' && msg.body.text === 'foo',
);
assert.strictEqual(fired, true);
});
it('フォローしていないローカルユーザーの投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'localTimeline', // ayano:Local
() => api('notes/create', { text: 'foo' }, chitose), // chitose posts
msg => msg.type === 'note' && msg.body.userId === chitose.id, // wait chitose
);
assert.strictEqual(fired, true);
});
it('リモートユーザーの投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'localTimeline', // ayano:Local
() => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts
msg => msg.type === 'note' && msg.body.userId === chinatsu.id, // wait chinatsu
);
assert.strictEqual(fired, false);
});
it('フォローしてたとしてもリモートユーザーの投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'localTimeline', // ayano:Local
() => api('notes/create', { text: 'foo' }, akari), // akari posts
msg => msg.type === 'note' && msg.body.userId === akari.id, // wait akari
);
assert.strictEqual(fired, false);
});
it('ホーム指定の投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'localTimeline', // ayano:Local
() => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), // kyoko home posts
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
);
assert.strictEqual(fired, false);
});
it('フォローしているローカルユーザーのダイレクト投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'localTimeline', // ayano:Local
() => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), // kyoko DM => ayano
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
);
assert.strictEqual(fired, false);
});
it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'localTimeline', // ayano:Local
() => api('notes/create', { text: 'foo', visibility: 'followers' }, chitose),
msg => msg.type === 'note' && msg.body.userId === chitose.id, // wait chitose
);
assert.strictEqual(fired, false);
});
});
describe('Hybrid Timeline', () => {
it('自分の投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'hybridTimeline', // ayano:Hybrid
() => api('notes/create', { text: 'foo' }, ayano), // ayano posts
msg => msg.type === 'note' && msg.body.text === 'foo',
);
assert.strictEqual(fired, true);
});
it('フォローしていないローカルユーザーの投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'hybridTimeline', // ayano:Hybrid
() => api('notes/create', { text: 'foo' }, chitose), // chitose posts
msg => msg.type === 'note' && msg.body.userId === chitose.id, // wait chitose
);
assert.strictEqual(fired, true);
});
it('フォローしているリモートユーザーの投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'hybridTimeline', // ayano:Hybrid
() => api('notes/create', { text: 'foo' }, akari), // akari posts
msg => msg.type === 'note' && msg.body.userId === akari.id, // wait akari
);
assert.strictEqual(fired, true);
});
it('フォローしていないリモートユーザーの投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'hybridTimeline', // ayano:Hybrid
() => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts
msg => msg.type === 'note' && msg.body.userId === chinatsu.id, // wait chinatsu
);
assert.strictEqual(fired, false);
});
it('フォローしているユーザーのダイレクト投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'hybridTimeline', // ayano:Hybrid
() => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
);
assert.strictEqual(fired, true);
});
it('フォローしているユーザーのホーム投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'hybridTimeline', // ayano:Hybrid
() => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
);
assert.strictEqual(fired, true);
});
it('フォローしていないローカルユーザーのホーム投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'hybridTimeline', // ayano:Hybrid
() => api('notes/create', { text: 'foo', visibility: 'home' }, chitose),
msg => msg.type === 'note' && msg.body.userId === chitose.id,
);
assert.strictEqual(fired, false);
});
it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'hybridTimeline', // ayano:Hybrid
() => api('notes/create', { text: 'foo', visibility: 'followers' }, chitose),
msg => msg.type === 'note' && msg.body.userId === chitose.id,
);
assert.strictEqual(fired, false);
});
});
describe('Global Timeline', () => {
it('フォローしていないローカルユーザーの投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'globalTimeline', // ayano:Global
() => api('notes/create', { text: 'foo' }, chitose), // chitose posts
msg => msg.type === 'note' && msg.body.userId === chitose.id, // wait chitose
);
assert.strictEqual(fired, true);
});
it('フォローしていないリモートユーザーの投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'globalTimeline', // ayano:Global
() => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts
msg => msg.type === 'note' && msg.body.userId === chinatsu.id, // wait chinatsu
);
assert.strictEqual(fired, true);
});
it('ホーム投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'globalTimeline', // ayano:Global
() => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), // kyoko posts
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
);
assert.strictEqual(fired, false);
});
});
describe('UserList Timeline', () => {
it('リストに入れているユーザーの投稿が流れる', async () => {
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { text: 'foo' }, ayano),
msg => msg.type === 'note' && msg.body.userId === ayano.id,
{ listId: list.id },
);
assert.strictEqual(fired, true);
});
it('リストに入れていないユーザーの投稿は流れない', async () => {
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { text: 'foo' }, chinatsu),
msg => msg.type === 'note' && msg.body.userId === chinatsu.id,
{ listId: list.id },
);
assert.strictEqual(fired, false);
});
// #4471
it('リストに入れているユーザーのダイレクト投稿が流れる', async () => {
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [chitose.id] }, ayano),
msg => msg.type === 'note' && msg.body.userId === ayano.id,
{ listId: list.id },
);
assert.strictEqual(fired, true);
});
// #4335
it('リストに入れているがフォローはしてないユーザーのフォロワー宛て投稿は流れない', async () => {
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { text: 'foo', visibility: 'followers' }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
{ listId: list.id },
);
assert.strictEqual(fired, false);
});
});
describe('Hashtag Timeline', () => {
it('指定したハッシュタグの投稿が流れる', () => new Promise<void>(async done => {
const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => {
if (type === 'note') {
assert.deepStrictEqual(body.text, '#foo');
ws.close();
done();
}
}, {
q: [
['foo'],
],
});
post(chitose, {
text: '#foo',
});
}));
it('指定したハッシュタグの投稿が流れる (AND)', () => new Promise<void>(async done => {
let fooCount = 0;
let barCount = 0;
let fooBarCount = 0;
const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => {
if (type === 'note') {
if (body.text === '#foo') fooCount++;
if (body.text === '#bar') barCount++;
if (body.text === '#foo #bar') fooBarCount++;
}
}, {
q: [
['foo', 'bar'],
],
});
post(chitose, {
text: '#foo',
});
post(chitose, {
text: '#bar',
});
post(chitose, {
text: '#foo #bar',
});
setTimeout(() => {
assert.strictEqual(fooCount, 0);
assert.strictEqual(barCount, 0);
assert.strictEqual(fooBarCount, 1);
ws.close();
done();
}, 3000);
}));
it('指定したハッシュタグの投稿が流れる (OR)', () => new Promise<void>(async done => {
let fooCount = 0;
let barCount = 0;
let fooBarCount = 0;
let piyoCount = 0;
const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => {
if (type === 'note') {
if (body.text === '#foo') fooCount++;
if (body.text === '#bar') barCount++;
if (body.text === '#foo #bar') fooBarCount++;
if (body.text === '#piyo') piyoCount++;
}
}, {
q: [
['foo'],
['bar'],
],
});
post(chitose, {
text: '#foo',
});
post(chitose, {
text: '#bar',
});
post(chitose, {
text: '#foo #bar',
});
post(chitose, {
text: '#piyo',
});
setTimeout(() => {
assert.strictEqual(fooCount, 1);
assert.strictEqual(barCount, 1);
assert.strictEqual(fooBarCount, 1);
assert.strictEqual(piyoCount, 0);
ws.close();
done();
}, 3000);
}));
it('指定したハッシュタグの投稿が流れる (AND + OR)', () => new Promise<void>(async done => {
let fooCount = 0;
let barCount = 0;
let fooBarCount = 0;
let piyoCount = 0;
let waaaCount = 0;
const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => {
if (type === 'note') {
if (body.text === '#foo') fooCount++;
if (body.text === '#bar') barCount++;
if (body.text === '#foo #bar') fooBarCount++;
if (body.text === '#piyo') piyoCount++;
if (body.text === '#waaa') waaaCount++;
}
}, {
q: [
['foo', 'bar'],
['piyo'],
],
});
post(chitose, {
text: '#foo',
});
post(chitose, {
text: '#bar',
});
post(chitose, {
text: '#foo #bar',
});
post(chitose, {
text: '#piyo',
});
post(chitose, {
text: '#waaa',
});
setTimeout(() => {
assert.strictEqual(fooCount, 0);
assert.strictEqual(barCount, 0);
assert.strictEqual(fooBarCount, 1);
assert.strictEqual(piyoCount, 1);
assert.strictEqual(waaaCount, 0);
ws.close();
done();
}, 3000);
}));
});
});
});

View file

@ -0,0 +1,103 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { signup, request, post, react, connectStream, startServer, shutdownServer } from '../utils.js';
describe('Note thread mute', () => {
let p: childProcess.ChildProcess;
let alice: any;
let bob: any;
let carol: any;
beforeAll(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
}, 1000 * 30);
afterAll(async () => {
await shutdownServer(p);
});
it('notes/mentions にミュートしているスレッドの投稿が含まれない', async () => {
const bobNote = await post(bob, { text: '@alice @carol root note' });
const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' });
await request('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' });
const res = await request('/notes/mentions', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolReply.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolReplyWithoutMention.id), false);
});
it('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
const bobNote = await post(bob, { text: '@alice @carol root note' });
await request('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
const res = await request('/i', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.hasUnreadMentions, false);
});
it('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise(async done => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
const bobNote = await post(bob, { text: '@alice @carol root note' });
await request('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
let fired = false;
const ws = await connectStream(alice, 'main', async ({ type, body }) => {
if (type === 'unreadMention') {
if (body === bobNote.id) return;
fired = true;
}
});
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
setTimeout(() => {
assert.strictEqual(fired, false);
ws.close();
done();
}, 5000);
}));
it('i/notifications にミュートしているスレッドの通知が含まれない', async () => {
const bobNote = await post(bob, { text: '@alice @carol root note' });
const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' });
await request('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' });
const res = await request('/i/notifications', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.note.id === carolReply.id), false);
assert.strictEqual(res.body.some((notification: any) => notification.note.id === carolReplyWithoutMention.id), false);
// NOTE: bobの投稿はスレッドミュート前に行われたため通知に含まれていてもよい
});
});

View file

@ -0,0 +1,61 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
import { signup, request, post, uploadUrl, startServer, shutdownServer } from '../utils.js';
describe('users/notes', () => {
let p: childProcess.ChildProcess;
let alice: any;
let jpgNote: any;
let pngNote: any;
let jpgPngNote: any;
beforeAll(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
const jpg = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
const png = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.png');
jpgNote = await post(alice, {
fileIds: [jpg.id],
});
pngNote = await post(alice, {
fileIds: [png.id],
});
jpgPngNote = await post(alice, {
fileIds: [jpg.id, png.id],
});
}, 1000 * 30);
afterAll(async() => {
await shutdownServer(p);
});
it('ファイルタイプ指定 (jpg)', async () => {
const res = await request('/users/notes', {
userId: alice.id,
fileType: ['image/jpeg'],
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.length, 2);
assert.strictEqual(res.body.some((note: any) => note.id === jpgNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === jpgPngNote.id), true);
});
it('ファイルタイプ指定 (jpg or png)', async () => {
const res = await request('/users/notes', {
userId: alice.id,
fileType: ['image/jpeg', 'image/png'],
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.length, 3);
assert.strictEqual(res.body.some((note: any) => note.id === jpgNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === pngNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === jpgPngNote.id), true);
});
});