Merge branch 'develop' into mkusername-empty

This commit is contained in:
Kagami Sascha Rosylight
2023-03-06 07:51:17 +01:00
committed by GitHub
201 changed files with 4859 additions and 2521 deletions

View File

@@ -1,18 +1,18 @@
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';
import { signup, api, post, startServer } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('API visibility', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
beforeAll(async () => {
p = await startServer();
}, 1000 * 30);
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
describe('Note visibility', () => {
@@ -60,7 +60,7 @@ describe('API visibility', () => {
//#endregion
const show = async (noteId: any, by: any) => {
return await request('/notes/show', {
return await api('/notes/show', {
noteId,
}, by);
};
@@ -75,7 +75,7 @@ describe('API visibility', () => {
target2 = await signup({ username: 'target2' });
// follow alice <= follower
await request('/following/create', { userId: alice.id }, follower);
await api('/following/create', { userId: alice.id }, follower);
// normal posts
pub = await post(alice, { text: 'x', visibility: 'public' });
@@ -413,21 +413,21 @@ describe('API visibility', () => {
//#region HTL
test('[HTL] public-post が 自分が見れる', async () => {
const res = await request('/notes/timeline', { limit: 100 }, alice);
const res = await api('/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');
});
test('[HTL] public-post が 非フォロワーから見れない', async () => {
const res = await request('/notes/timeline', { limit: 100 }, other);
const res = await api('/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);
});
test('[HTL] followers-post が フォロワーから見れる', async () => {
const res = await request('/notes/timeline', { limit: 100 }, follower);
const res = await api('/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');
@@ -436,21 +436,21 @@ describe('API visibility', () => {
//#region RTL
test('[replies] followers-reply が フォロワーから見れる', async () => {
const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, follower);
const res = await api('/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');
});
test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => {
const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, other);
const res = await api('/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);
});
test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, target);
const res = await api('/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');
@@ -459,14 +459,14 @@ describe('API visibility', () => {
//#region MTL
test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
const res = await request('/notes/mentions', { limit: 100 }, target);
const res = await api('/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');
});
test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => {
const res = await request('/notes/mentions', { limit: 100 }, target);
const res = await api('/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');
@@ -474,4 +474,4 @@ describe('API visibility', () => {
//#endregion
});
});
*/

View File

@@ -1,11 +1,11 @@
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';
import { signup, api, startServer } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('API', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
let alice: any;
let bob: any;
let carol: any;
@@ -15,69 +15,69 @@ describe('API', () => {
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
}, 1000 * 30);
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
describe('General validation', () => {
test('wrong type', async(async () => {
const res = await request('/test', {
test('wrong type', async () => {
const res = await api('/test', {
required: true,
string: 42,
});
assert.strictEqual(res.status, 400);
}));
});
test('missing require param', async(async () => {
const res = await request('/test', {
test('missing require param', async () => {
const res = await api('/test', {
string: 'a',
});
assert.strictEqual(res.status, 400);
}));
});
test('invalid misskey:id (empty string)', async(async () => {
const res = await request('/test', {
test('invalid misskey:id (empty string)', async () => {
const res = await api('/test', {
required: true,
id: '',
});
assert.strictEqual(res.status, 400);
}));
});
test('valid misskey:id', async(async () => {
const res = await request('/test', {
test('valid misskey:id', async () => {
const res = await api('/test', {
required: true,
id: '8wvhjghbxu',
});
assert.strictEqual(res.status, 200);
}));
});
test('default value', async(async () => {
const res = await request('/test', {
test('default value', async () => {
const res = await api('/test', {
required: true,
string: 'a',
});
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.default, 'hello');
}));
});
test('can set null even if it has default value', async(async () => {
const res = await request('/test', {
test('can set null even if it has default value', async () => {
const res = await api('/test', {
required: true,
nullableDefault: null,
});
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.nullableDefault, null);
}));
});
test('cannot set undefined if it has default value', async(async () => {
const res = await request('/test', {
test('cannot set undefined if it has default value', async () => {
const res = await api('/test', {
required: true,
nullableDefault: undefined,
});
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.nullableDefault, 'hello');
}));
});
});
});

View File

@@ -1,11 +1,11 @@
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';
import { signup, api, post, startServer } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('Block', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
// alice blocks bob
let alice: any;
@@ -17,14 +17,14 @@ describe('Block', () => {
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
}, 1000 * 30);
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
test('Block作成', async () => {
const res = await request('/blocking/create', {
const res = await api('/blocking/create', {
userId: bob.id,
}, alice);
@@ -32,7 +32,7 @@ describe('Block', () => {
});
test('ブロックされているユーザーをフォローできない', async () => {
const res = await request('/following/create', { userId: alice.id }, bob);
const res = await api('/following/create', { userId: alice.id }, bob);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
@@ -41,7 +41,7 @@ describe('Block', () => {
test('ブロックされているユーザーにリアクションできない', async () => {
const note = await post(alice, { text: 'hello' });
const res = await request('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
const res = await api('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
@@ -50,7 +50,7 @@ describe('Block', () => {
test('ブロックされているユーザーに返信できない', async () => {
const note = await post(alice, { text: 'hello' });
const res = await request('/notes/create', { replyId: note.id, text: 'yo' }, bob);
const res = await api('/notes/create', { replyId: note.id, text: 'yo' }, bob);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
@@ -59,7 +59,7 @@ describe('Block', () => {
test('ブロックされているユーザーのートをRenoteできない', async () => {
const note = await post(alice, { text: 'hello' });
const res = await request('/notes/create', { renoteId: note.id, text: 'yo' }, bob);
const res = await api('/notes/create', { renoteId: note.id, text: 'yo' }, bob);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
@@ -74,7 +74,7 @@ describe('Block', () => {
const bobNote = await post(bob);
const carolNote = await post(carol);
const res = await request('/notes/local-timeline', {}, bob);
const res = await api('/notes/local-timeline', {}, bob);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);

View File

@@ -1,29 +1,35 @@
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';
// node-fetch only supports it's own Blob yet
// https://github.com/node-fetch/node-fetch/pull/1664
import { Blob } from 'node-fetch';
import { startServer, signup, post, api, uploadFile } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('Endpoints', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
let alice: any;
let bob: any;
let carol: any;
let dave: any;
beforeAll(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
}, 1000 * 30);
carol = await signup({ username: 'carol' });
dave = await signup({ username: 'dave' });
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
describe('signup', () => {
test('不正なユーザー名でアカウントが作成できない', async () => {
const res = await request('api/signup', {
const res = await api('signup', {
username: 'test.',
password: 'test',
});
@@ -31,7 +37,7 @@ describe('Endpoints', () => {
});
test('空のパスワードでアカウントが作成できない', async () => {
const res = await request('api/signup', {
const res = await api('signup', {
username: 'test',
password: '',
});
@@ -44,7 +50,7 @@ describe('Endpoints', () => {
password: 'test1',
};
const res = await request('api/signup', me);
const res = await api('signup', me);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -52,7 +58,7 @@ describe('Endpoints', () => {
});
test('同じユーザー名のアカウントは作成できない', async () => {
const res = await request('api/signup', {
const res = await api('signup', {
username: 'test1',
password: 'test1',
});
@@ -63,7 +69,7 @@ describe('Endpoints', () => {
describe('signin', () => {
test('間違ったパスワードでサインインできない', async () => {
const res = await request('api/signin', {
const res = await api('signin', {
username: 'test1',
password: 'bar',
});
@@ -72,7 +78,7 @@ describe('Endpoints', () => {
});
test('クエリをインジェクションできない', async () => {
const res = await request('api/signin', {
const res = await api('signin', {
username: 'test1',
password: {
$gt: '',
@@ -83,7 +89,7 @@ describe('Endpoints', () => {
});
test('正しい情報でサインインできる', async () => {
const res = await request('api/signin', {
const res = await api('signin', {
username: 'test1',
password: 'test1',
});
@@ -111,11 +117,12 @@ describe('Endpoints', () => {
assert.strictEqual(res.body.birthday, myBirthday);
});
test('名前を空白にできない', async () => {
test('名前を空白にでき', async () => {
const res = await api('/i/update', {
name: ' ',
}, alice);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.name, ' ');
});
test('誕生日の設定を削除できる', async () => {
@@ -201,7 +208,6 @@ describe('Endpoints', () => {
test('リアクションできる', async () => {
const bobPost = await post(bob);
const alice = await signup({ username: 'alice' });
const res = await api('/notes/reactions/create', {
noteId: bobPost.id,
reaction: '🚀',
@@ -214,7 +220,7 @@ describe('Endpoints', () => {
}, alice);
assert.strictEqual(resNote.status, 200);
assert.strictEqual(resNote.body.reactions['🚀'], [alice.id]);
assert.strictEqual(resNote.body.reactions['🚀'], 1);
});
test('自分の投稿にもリアクションできる', async () => {
@@ -228,7 +234,7 @@ describe('Endpoints', () => {
assert.strictEqual(res.status, 204);
});
test('二重にリアクションできない', async () => {
test('二重にリアクションすると上書きされる', async () => {
const bobPost = await post(bob);
await api('/notes/reactions/create', {
@@ -241,7 +247,14 @@ describe('Endpoints', () => {
reaction: '🚀',
}, alice);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.status, 204);
const resNote = await api('/notes/show', {
noteId: bobPost.id,
}, alice);
assert.strictEqual(resNote.status, 200);
assert.deepStrictEqual(resNote.body.reactions, { '🚀': 1 });
});
test('存在しない投稿にはリアクションできない', async () => {
@@ -369,57 +382,22 @@ describe('Endpoints', () => {
});
});
/*
describe('/i', () => {
test('', 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', () => {
test('ドライブ情報を取得できる', async () => {
await uploadFile({
userId: alice.id,
size: 256
await uploadFile(alice, {
blob: new Blob([new Uint8Array(256)]),
});
await uploadFile({
userId: alice.id,
size: 512
await uploadFile(alice, {
blob: new Blob([new Uint8Array(512)]),
});
await uploadFile({
userId: alice.id,
size: 1024
await uploadFile(alice, {
blob: new Blob([new Uint8Array(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);
}));
expect(res.body).toHaveProperty('usage', 1792);
});
});
describe('drive/files/create', () => {
@@ -428,397 +406,400 @@ describe('API: Endpoints', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.name, 'Lenna.png');
}));
assert.strictEqual(res.body.name, 'Lenna.jpg');
});
test('ファイルに名前を付けられる', 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');
const res = await uploadFile(alice, { name: 'Belmond.jpg' });
expect(res).have.status(200);
expect(res.body).be.a('object');
expect(res.body).have.property('name').eql('Belmond.png');
}));
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.name, 'Belmond.jpg');
});
test('ファイルに名前を付けられるが、拡張子は正しいものになる', async () => {
const res = await uploadFile(alice, { name: 'Belmond.png' });
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.name, 'Belmond.png.jpg');
});
test('ファイル無しで怒られる', async () => {
const res = await api('/drive/files/create', {}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('SVGファイルを作成できる', async () => {
const res = await uploadFile(alice, __dirname + '/resources/image.svg');
const res = await uploadFile(alice, { path: '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', () => {
test('名前を更新できる', async () => {
const file = await uploadFile(alice);
const file = (await uploadFile(alice)).body;
const newName = 'いちごパスタ.png';
const res = await api('/drive/files/update', {
fileId: file.id,
name: newName
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);
}));
});
test('他人のファイルは更新できない', async () => {
const file = await uploadFile(bob);
const file = (await uploadFile(alice)).body;
const res = await api('/drive/files/update', {
fileId: file.id,
name: 'いちごパスタ.png'
}, alice);
name: 'いちごパスタ.png',
}, bob);
assert.strictEqual(res.status, 400);
}));
});
test('親フォルダを更新できる', async () => {
const file = await uploadFile(alice);
const file = (await uploadFile(alice)).body;
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: folder.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);
}));
});
test('親フォルダを無しにできる', async () => {
const file = await uploadFile(alice);
const file = (await uploadFile(alice)).body;
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
await api('/drive/files/update', {
fileId: file.id,
folderId: folder.id
folderId: folder.id,
}, alice);
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: null
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);
}));
});
test('他人のフォルダには入れられない', async () => {
const file = await uploadFile(alice);
const file = (await uploadFile(alice)).body;
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, bob)).body;
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: folder.id
folderId: folder.id,
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('存在しないフォルダで怒られる', async () => {
const file = await uploadFile(alice);
const file = (await uploadFile(alice)).body;
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: '000000000000000000000000'
folderId: '000000000000000000000000',
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('不正なフォルダIDで怒られる', async () => {
const file = await uploadFile(alice);
const file = (await uploadFile(alice)).body;
const res = await api('/drive/files/update', {
fileId: file.id,
folderId: 'foo'
folderId: 'foo',
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('ファイルが存在しなかったら怒る', async () => {
const res = await api('/drive/files/update', {
fileId: '000000000000000000000000',
name: 'いちごパスタ.png'
name: 'いちごパスタ.png',
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('間違ったIDで怒られる', async () => {
const res = await api('/drive/files/update', {
fileId: 'kyoppie',
name: 'いちごパスタ.png'
name: 'いちごパスタ.png',
}, alice);
assert.strictEqual(res.status, 400);
}));
});
});
describe('drive/folders/create', () => {
test('フォルダを作成できる', async () => {
const res = await api('/drive/folders/create', {
name: 'test'
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', () => {
test('名前を更新できる', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
name: 'new name'
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');
}));
});
test('他人のフォルダを更新できない', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, bob)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
name: 'new name'
name: 'new name',
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('親フォルダを更新できる', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const parentFolder = (await api('/drive/folders/create', {
name: 'parent'
name: 'parent',
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: parentFolder.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);
}));
});
test('親フォルダを無しに更新できる', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const parentFolder = (await api('/drive/folders/create', {
name: 'parent'
name: 'parent',
}, alice)).body;
await api('/drive/folders/update', {
folderId: folder.id,
parentId: parentFolder.id
parentId: parentFolder.id,
}, alice);
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: null
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);
}));
});
test('他人のフォルダを親フォルダに設定できない', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const parentFolder = (await api('/drive/folders/create', {
name: 'parent'
name: 'parent',
}, bob)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: parentFolder.id
parentId: parentFolder.id,
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('フォルダが循環するような構造にできない', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const parentFolder = (await api('/drive/folders/create', {
name: 'parent'
name: 'parent',
}, alice)).body;
await api('/drive/folders/update', {
folderId: parentFolder.id,
parentId: folder.id
parentId: folder.id,
}, alice);
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: parentFolder.id
parentId: parentFolder.id,
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('フォルダが循環するような構造にできない(再帰的)', async () => {
const folderA = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const folderB = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const folderC = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
await api('/drive/folders/update', {
folderId: folderB.id,
parentId: folderA.id
parentId: folderA.id,
}, alice);
await api('/drive/folders/update', {
folderId: folderC.id,
parentId: folderB.id
parentId: folderB.id,
}, alice);
const res = await api('/drive/folders/update', {
folderId: folderA.id,
parentId: folderC.id
parentId: folderC.id,
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('フォルダが循環するような構造にできない(自身)', async () => {
const folderA = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folderA.id,
parentId: folderA.id
parentId: folderA.id,
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('存在しない親フォルダを設定できない', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: '000000000000000000000000'
parentId: '000000000000000000000000',
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('不正な親フォルダIDで怒られる', async () => {
const folder = (await api('/drive/folders/create', {
name: 'test'
name: 'test',
}, alice)).body;
const res = await api('/drive/folders/update', {
folderId: folder.id,
parentId: 'foo'
parentId: 'foo',
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('存在しないフォルダを更新できない', async () => {
const res = await api('/drive/folders/update', {
folderId: '000000000000000000000000'
folderId: '000000000000000000000000',
}, alice);
assert.strictEqual(res.status, 400);
}));
});
test('不正なフォルダIDで怒られる', async () => {
const res = await api('/drive/folders/update', {
folderId: 'foo'
folderId: 'foo',
}, alice);
assert.strictEqual(res.status, 400);
}));
});
});
describe('notes/replies', () => {
test('自分に閲覧権限のない投稿は含まれない', async () => {
const alicePost = await post(alice, {
text: 'foo'
text: 'foo',
});
await post(bob, {
replyId: alicePost.id,
text: 'bar',
visibility: 'specified',
visibleUserIds: [alice.id]
visibleUserIds: [alice.id],
});
const res = await api('/notes/replies', {
noteId: alicePost.id
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', () => {
test('フォロワー限定投稿が含まれる', async () => {
await api('/following/create', {
userId: alice.id
}, bob);
userId: carol.id,
}, dave);
const alicePost = await post(alice, {
const carolPost = await post(carol, {
text: 'foo',
visibility: 'followers'
visibility: 'followers',
});
const res = await api('/notes/timeline', {}, bob);
const res = await api('/notes/timeline', {}, dave);
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);
}));
assert.strictEqual(res.body[0].id, carolPost.id);
});
});
});
*/

View File

@@ -1,9 +1,8 @@
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';
import { startServer, signup, post, api, simpleGet } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
// Request Accept
const ONLY_AP = 'application/activity+json';
@@ -13,11 +12,10 @@ const UNSPECIFIED = '*/*';
// Response Content-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 p: INestApplicationContext;
let alice: any;
let alicesPost: any;
@@ -28,15 +26,15 @@ describe('Fetch resource', () => {
alicesPost = await post(alice, {
text: 'test',
});
}, 1000 * 30);
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
describe('Common', () => {
test('meta', async () => {
const res = await request('/meta', {
const res = await api('/meta', {
});
assert.strictEqual(res.status, 200);
@@ -54,36 +52,26 @@ describe('Fetch resource', () => {
assert.strictEqual(res.type, HTML);
});
test('GET api-doc', async () => {
test('GET api-doc (廃止)', async () => {
const res = await simpleGet('/api-doc');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, HTML);
assert.strictEqual(res.status, 404);
});
test('GET api.json', async () => {
test('GET api.json (廃止)', async () => {
const res = await simpleGet('/api.json');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, JSON);
assert.strictEqual(res.status, 404);
});
test('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);
test('GET api/foo (存在しない)', async () => {
const res = await simpleGet('/api/foo');
assert.strictEqual(res.status, 404);
assert.strictEqual(res.body.error.code, 'UNKNOWN_API_ENDPOINT');
});
test('GET favicon.ico', async () => {
const res = await simpleGet('/favicon.ico');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.type, 'image/x-icon');
assert.strictEqual(res.type, 'image/vnd.microsoft.icon');
});
test('GET apple-touch-icon.png', async () => {

View File

@@ -1,36 +1,34 @@
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';
import { signup, api, startServer, simpleGet } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('FF visibility', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
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);
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
test('ffVisibility が public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {
await request('/i/update', {
await api('/i/update', {
ffVisibility: 'public',
}, alice);
const followingRes = await request('/users/following', {
const followingRes = await api('/users/following', {
userId: alice.id,
}, bob);
const followersRes = await request('/users/followers', {
const followersRes = await api('/users/followers', {
userId: alice.id,
}, bob);
@@ -41,14 +39,14 @@ describe('FF visibility', () => {
});
test('ffVisibility が followers なユーザーのフォロー/フォロワーを自分で見れる', async () => {
await request('/i/update', {
await api('/i/update', {
ffVisibility: 'followers',
}, alice);
const followingRes = await request('/users/following', {
const followingRes = await api('/users/following', {
userId: alice.id,
}, alice);
const followersRes = await request('/users/followers', {
const followersRes = await api('/users/followers', {
userId: alice.id,
}, alice);
@@ -59,14 +57,14 @@ describe('FF visibility', () => {
});
test('ffVisibility が followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => {
await request('/i/update', {
await api('/i/update', {
ffVisibility: 'followers',
}, alice);
const followingRes = await request('/users/following', {
const followingRes = await api('/users/following', {
userId: alice.id,
}, bob);
const followersRes = await request('/users/followers', {
const followersRes = await api('/users/followers', {
userId: alice.id,
}, bob);
@@ -75,18 +73,18 @@ describe('FF visibility', () => {
});
test('ffVisibility が followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => {
await request('/i/update', {
await api('/i/update', {
ffVisibility: 'followers',
}, alice);
await request('/following/create', {
await api('/following/create', {
userId: alice.id,
}, bob);
const followingRes = await request('/users/following', {
const followingRes = await api('/users/following', {
userId: alice.id,
}, bob);
const followersRes = await request('/users/followers', {
const followersRes = await api('/users/followers', {
userId: alice.id,
}, bob);
@@ -97,14 +95,14 @@ describe('FF visibility', () => {
});
test('ffVisibility が private なユーザーのフォロー/フォロワーを自分で見れる', async () => {
await request('/i/update', {
await api('/i/update', {
ffVisibility: 'private',
}, alice);
const followingRes = await request('/users/following', {
const followingRes = await api('/users/following', {
userId: alice.id,
}, alice);
const followersRes = await request('/users/followers', {
const followersRes = await api('/users/followers', {
userId: alice.id,
}, alice);
@@ -115,14 +113,14 @@ describe('FF visibility', () => {
});
test('ffVisibility が private なユーザーのフォロー/フォロワーを他人が見れない', async () => {
await request('/i/update', {
await api('/i/update', {
ffVisibility: 'private',
}, alice);
const followingRes = await request('/users/following', {
const followingRes = await api('/users/following', {
userId: alice.id,
}, bob);
const followersRes = await request('/users/followers', {
const followersRes = await api('/users/followers', {
userId: alice.id,
}, bob);
@@ -133,7 +131,7 @@ describe('FF visibility', () => {
describe('AP', () => {
test('ffVisibility が public 以外ならばAPからは取得できない', async () => {
{
await request('/i/update', {
await api('/i/update', {
ffVisibility: 'public',
}, alice);
@@ -143,22 +141,22 @@ describe('FF visibility', () => {
assert.strictEqual(followersRes.status, 200);
}
{
await request('/i/update', {
await api('/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 }));
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, 403);
assert.strictEqual(followersRes.status, 403);
}
{
await request('/i/update', {
await api('/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 }));
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, 403);
assert.strictEqual(followersRes.status, 403);
}

View File

@@ -1,11 +1,11 @@
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';
import { signup, api, post, react, startServer, waitFire } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('Mute', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
// alice mutes carol
let alice: any;
@@ -17,14 +17,14 @@ describe('Mute', () => {
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
}, 1000 * 30);
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
test('ミュート作成', async () => {
const res = await request('/mute/create', {
const res = await api('/mute/create', {
userId: carol.id,
}, alice);
@@ -35,7 +35,7 @@ describe('Mute', () => {
const bobNote = await post(bob, { text: '@alice hi' });
const carolNote = await post(carol, { text: '@alice hi' });
const res = await request('/notes/mentions', {}, alice);
const res = await api('/notes/mentions', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
@@ -45,11 +45,11 @@ describe('Mute', () => {
test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
await api('/i/read-all-unread-notes', {}, alice);
await post(carol, { text: '@alice hi' });
const res = await request('/i', {}, alice);
const res = await api('/i', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.hasUnreadMentions, false);
@@ -57,7 +57,7 @@ describe('Mute', () => {
test('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
await api('/i/read-all-unread-notes', {}, alice);
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadMention');
@@ -66,8 +66,8 @@ describe('Mute', () => {
test('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
await request('/notifications/mark-all-as-read', {}, alice);
await api('/i/read-all-unread-notes', {}, alice);
await api('/notifications/mark-all-as-read', {}, alice);
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadNotification');
@@ -80,7 +80,7 @@ describe('Mute', () => {
const bobNote = await post(bob);
const carolNote = await post(carol);
const res = await request('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
@@ -96,7 +96,7 @@ describe('Mute', () => {
renoteId: carolNote.id,
});
const res = await request('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
@@ -112,7 +112,7 @@ describe('Mute', () => {
await react(bob, aliceNote, 'like');
await react(carol, aliceNote, 'like');
const res = await request('/i/notifications', {}, alice);
const res = await api('/i/notifications', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);

View File

@@ -1,12 +1,12 @@
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';
import { Note } from '@/models/entities/Note.js';
import { signup, post, uploadUrl, startServer, initTestDb, api, uploadFile } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('Note', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
let Notes: any;
let alice: any;
@@ -18,10 +18,10 @@ describe('Note', () => {
Notes = connection.getRepository(Note);
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
}, 1000 * 30);
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
test('投稿できる', async () => {
@@ -29,7 +29,7 @@ describe('Note', () => {
text: 'test',
};
const res = await request('/notes/create', post, alice);
const res = await api('/notes/create', post, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -39,7 +39,7 @@ describe('Note', () => {
test('ファイルを添付できる', 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', {
const res = await api('/notes/create', {
fileIds: [file.id],
}, alice);
@@ -48,37 +48,37 @@ describe('Note', () => {
assert.deepStrictEqual(res.body.createdNote.fileIds, [file.id]);
}, 1000 * 10);
test('他人のファイルは無視', async () => {
test('他人のファイルで怒られる', 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', {
const res = await api('/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, []);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE');
assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
}, 1000 * 10);
test('存在しないファイルは無視', async () => {
const res = await request('/notes/create', {
test('存在しないファイルで怒られる', async () => {
const res = await api('/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, []);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE');
assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
});
test('不正なファイルIDは無視', async () => {
const res = await request('/notes/create', {
test('不正なファイルIDで怒られる', async () => {
const res = await api('/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, []);
assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE');
assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
});
test('返信できる', async () => {
@@ -91,7 +91,7 @@ describe('Note', () => {
replyId: bobPost.id,
};
const res = await request('/notes/create', alicePost, alice);
const res = await api('/notes/create', alicePost, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -109,7 +109,7 @@ describe('Note', () => {
renoteId: bobPost.id,
};
const res = await request('/notes/create', alicePost, alice);
const res = await api('/notes/create', alicePost, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -127,7 +127,7 @@ describe('Note', () => {
renoteId: bobPost.id,
};
const res = await request('/notes/create', alicePost, alice);
const res = await api('/notes/create', alicePost, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -140,7 +140,7 @@ describe('Note', () => {
const post = {
text: '!'.repeat(3000),
};
const res = await request('/notes/create', post, alice);
const res = await api('/notes/create', post, alice);
assert.strictEqual(res.status, 200);
});
@@ -148,7 +148,7 @@ describe('Note', () => {
const post = {
text: '!'.repeat(3001),
};
const res = await request('/notes/create', post, alice);
const res = await api('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
@@ -157,7 +157,7 @@ describe('Note', () => {
text: 'test',
replyId: '000000000000000000000000',
};
const res = await request('/notes/create', post, alice);
const res = await api('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
@@ -165,7 +165,7 @@ describe('Note', () => {
const post = {
renoteId: '000000000000000000000000',
};
const res = await request('/notes/create', post, alice);
const res = await api('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
@@ -174,7 +174,7 @@ describe('Note', () => {
text: 'test',
replyId: 'foo',
};
const res = await request('/notes/create', post, alice);
const res = await api('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
@@ -182,7 +182,7 @@ describe('Note', () => {
const post = {
renoteId: 'foo',
};
const res = await request('/notes/create', post, alice);
const res = await api('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
});
@@ -191,7 +191,7 @@ describe('Note', () => {
text: '@ghost yo',
};
const res = await request('/notes/create', post, alice);
const res = await api('/notes/create', post, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -203,7 +203,7 @@ describe('Note', () => {
text: '@bob @bob @bob yo',
};
const res = await request('/notes/create', post, alice);
const res = await api('/notes/create', post, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -213,9 +213,125 @@ describe('Note', () => {
assert.deepStrictEqual(noteDoc.mentions, [bob.id]);
});
describe('添付ファイル情報', () => {
test('ファイルを添付した場合、投稿成功時にファイル情報入りのレスポンスが帰ってくる', async () => {
const file = await uploadFile(alice);
const res = await api('/notes/create', {
fileIds: [file.body.id],
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.files.length, 1);
assert.strictEqual(res.body.createdNote.files[0].id, file.body.id);
});
test('ファイルを添付した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
const file = await uploadFile(alice);
const createdNote = await api('/notes/create', {
fileIds: [file.body.id],
}, alice);
assert.strictEqual(createdNote.status, 200);
const res = await api('/notes', {
withFiles: true,
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string; files: { id: string }[] }) => note.id === createdNote.body.createdNote.id);
assert.notEqual(myNote, null);
assert.strictEqual(myNote.files.length, 1);
assert.strictEqual(myNote.files[0].id, file.body.id);
});
test('ファイルが添付されたノートをリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
const file = await uploadFile(alice);
const createdNote = await api('/notes/create', {
fileIds: [file.body.id],
}, alice);
assert.strictEqual(createdNote.status, 200);
const renoted = await api('/notes/create', {
renoteId: createdNote.body.createdNote.id,
}, alice);
assert.strictEqual(renoted.status, 200);
const res = await api('/notes', {
renote: true,
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
assert.notEqual(myNote, null);
assert.strictEqual(myNote.renote.files.length, 1);
assert.strictEqual(myNote.renote.files[0].id, file.body.id);
});
test('ファイルが添付されたノートに返信した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
const file = await uploadFile(alice);
const createdNote = await api('/notes/create', {
fileIds: [file.body.id],
}, alice);
assert.strictEqual(createdNote.status, 200);
const reply = await api('/notes/create', {
replyId: createdNote.body.createdNote.id,
text: 'this is reply',
}, alice);
assert.strictEqual(reply.status, 200);
const res = await api('/notes', {
reply: true,
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string }) => note.id === reply.body.createdNote.id);
assert.notEqual(myNote, null);
assert.strictEqual(myNote.reply.files.length, 1);
assert.strictEqual(myNote.reply.files[0].id, file.body.id);
});
test('ファイルが添付されたノートへの返信をリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
const file = await uploadFile(alice);
const createdNote = await api('/notes/create', {
fileIds: [file.body.id],
}, alice);
assert.strictEqual(createdNote.status, 200);
const reply = await api('/notes/create', {
replyId: createdNote.body.createdNote.id,
text: 'this is reply',
}, alice);
assert.strictEqual(reply.status, 200);
const renoted = await api('/notes/create', {
renoteId: reply.body.createdNote.id,
}, alice);
assert.strictEqual(renoted.status, 200);
const res = await api('/notes', {
renote: true,
}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
assert.notEqual(myNote, null);
assert.strictEqual(myNote.renote.reply.files.length, 1);
assert.strictEqual(myNote.renote.reply.files[0].id, file.body.id);
});
});
describe('notes/create', () => {
test('投票を添付できる', async () => {
const res = await request('/notes/create', {
const res = await api('/notes/create', {
text: 'test',
poll: {
choices: ['foo', 'bar'],
@@ -228,14 +344,14 @@ describe('Note', () => {
});
test('投票の選択肢が無くて怒られる', async () => {
const res = await request('/notes/create', {
const res = await api('/notes/create', {
poll: {},
}, alice);
assert.strictEqual(res.status, 400);
});
test('投票の選択肢が無くて怒られる (空の配列)', async () => {
const res = await request('/notes/create', {
const res = await api('/notes/create', {
poll: {
choices: [],
},
@@ -244,7 +360,7 @@ describe('Note', () => {
});
test('投票の選択肢が1つで怒られる', async () => {
const res = await request('/notes/create', {
const res = await api('/notes/create', {
poll: {
choices: ['Strawberry Pasta'],
},
@@ -253,14 +369,14 @@ describe('Note', () => {
});
test('投票できる', async () => {
const { body } = await request('/notes/create', {
const { body } = await api('/notes/create', {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
},
}, alice);
const res = await request('/notes/polls/vote', {
const res = await api('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 1,
}, alice);
@@ -269,19 +385,19 @@ describe('Note', () => {
});
test('複数投票できない', async () => {
const { body } = await request('/notes/create', {
const { body } = await api('/notes/create', {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
},
}, alice);
await request('/notes/polls/vote', {
await api('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 0,
}, alice);
const res = await request('/notes/polls/vote', {
const res = await api('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 2,
}, alice);
@@ -290,7 +406,7 @@ describe('Note', () => {
});
test('許可されている場合は複数投票できる', async () => {
const { body } = await request('/notes/create', {
const { body } = await api('/notes/create', {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
@@ -298,17 +414,17 @@ describe('Note', () => {
},
}, alice);
await request('/notes/polls/vote', {
await api('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 0,
}, alice);
await request('/notes/polls/vote', {
await api('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 1,
}, alice);
const res = await request('/notes/polls/vote', {
const res = await api('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 2,
}, alice);
@@ -317,7 +433,7 @@ describe('Note', () => {
});
test('締め切られている場合は投票できない', async () => {
const { body } = await request('/notes/create', {
const { body } = await api('/notes/create', {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
@@ -327,7 +443,7 @@ describe('Note', () => {
await new Promise(x => setTimeout(x, 2));
const res = await request('/notes/polls/vote', {
const res = await api('/notes/polls/vote', {
noteId: body.createdNote.id,
choice: 1,
}, alice);

View File

@@ -1,12 +1,12 @@
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';
import { Following } from '@/models/entities/Following.js';
import { connectStream, signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('Streaming', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
let Followings: any;
const follow = async (follower: any, followee: any) => {
@@ -71,10 +71,10 @@ describe('Streaming', () => {
listId: list.id,
userId: kyoko.id,
}, chitose);
}, 1000 * 30);
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
describe('Events', () => {
@@ -404,43 +404,45 @@ describe('Streaming', () => {
});
}));
test('指定したハッシュタグの投稿が流れる (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);
}));
// XXX: QueryFailedError: duplicate key value violates unique constraint "IDX_347fec870eafea7b26c8a73bac"
// test('指定したハッシュタグの投稿が流れる (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);
// }));
test('指定したハッシュタグの投稿が流れる (OR)', () => new Promise<void>(async done => {
let fooCount = 0;

View File

@@ -1,11 +1,11 @@
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';
import { signup, api, post, connectStream, startServer } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('Note thread mute', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
let alice: any;
let bob: any;
@@ -16,22 +16,22 @@ describe('Note thread mute', () => {
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
}, 1000 * 30);
}, 1000 * 60 * 2);
afterAll(async () => {
await shutdownServer(p);
await p.close();
});
test('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);
await api('/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);
const res = await api('/notes/mentions', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
@@ -42,27 +42,27 @@ describe('Note thread mute', () => {
test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
await api('/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);
await api('/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);
const res = await api('/i', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.hasUnreadMentions, false);
});
test('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise(async done => {
test('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise<void>(async done => {
// 状態リセット
await request('/i/read-all-unread-notes', {}, alice);
await api('/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);
await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
let fired = false;
@@ -86,12 +86,12 @@ describe('Note thread mute', () => {
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);
await api('/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);
const res = await api('/i/notifications', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);

View File

@@ -1,11 +1,11 @@
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';
import { signup, api, post, uploadUrl, startServer } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
describe('users/notes', () => {
let p: childProcess.ChildProcess;
let p: INestApplicationContext;
let alice: any;
let jpgNote: any;
@@ -26,14 +26,14 @@ describe('users/notes', () => {
jpgPngNote = await post(alice, {
fileIds: [jpg.id, png.id],
});
}, 1000 * 30);
}, 1000 * 60 * 2);
afterAll(async() => {
await shutdownServer(p);
await p.close();
});
test('ファイルタイプ指定 (jpg)', async () => {
const res = await request('/users/notes', {
const res = await api('/users/notes', {
userId: alice.id,
fileType: ['image/jpeg'],
}, alice);
@@ -46,7 +46,7 @@ describe('users/notes', () => {
});
test('ファイルタイプ指定 (jpg or png)', async () => {
const res = await request('/users/notes', {
const res = await api('/users/notes', {
userId: alice.id,
fileType: ['image/jpeg', 'image/png'],
}, alice);

View File

@@ -0,0 +1,11 @@
import { Schema } from '@/misc/schema';
import Ajv from 'ajv';
export const getValidator = (paramDef: Schema) => {
const ajv = new Ajv({
useDefaults: true,
});
ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/);
return ajv.compile(paramDef);
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -33,11 +33,12 @@
"lib": [
"esnext"
],
"types": ["jest"]
"types": ["jest", "node"]
},
"compileOnSave": false,
"include": [
"./**/*.ts",
"../src/**/*.test.ts",
"../src/@types/**/*.ts",
]
}

View File

@@ -3,16 +3,18 @@ process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals';
import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing';
import { DataSource } from 'typeorm';
import * as lolex from '@sinonjs/fake-timers';
import rndstr from 'rndstr';
import { GlobalModule } from '@/GlobalModule.js';
import { RoleService } from '@/core/RoleService.js';
import type { Role, RolesRepository, RoleAssignmentsRepository, UsersRepository, User } from '@/models/index.js';
import { DI } from '@/di-symbols.js';
import { CoreModule } from '@/core/CoreModule.js';
import { MetaService } from '@/core/MetaService.js';
import { genAid } from '@/misc/id/aid.js';
import { UserCacheService } from '@/core/UserCacheService.js';
import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { sleep } from '../utils.js';
import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock';
@@ -25,6 +27,7 @@ describe('RoleService', () => {
let rolesRepository: RolesRepository;
let roleAssignmentsRepository: RoleAssignmentsRepository;
let metaService: jest.Mocked<MetaService>;
let clock: lolex.InstalledClock;
function createUser(data: Partial<User> = {}) {
const un = rndstr('a-z0-9', 16);
@@ -50,16 +53,12 @@ describe('RoleService', () => {
.then(x => rolesRepository.findOneByOrFail(x.identifiers[0]));
}
async function assign(roleId: Role['id'], userId: User['id']) {
await roleAssignmentsRepository.insert({
id: genAid(new Date()),
createdAt: new Date(),
roleId,
userId,
});
}
beforeEach(async () => {
clock = lolex.install({
now: new Date(),
shouldClearNativeTimers: true,
});
app = await Test.createTestingModule({
imports: [
GlobalModule,
@@ -67,6 +66,8 @@ describe('RoleService', () => {
providers: [
RoleService,
UserCacheService,
IdService,
GlobalEventService,
],
})
.useMocker((token) => {
@@ -92,12 +93,15 @@ describe('RoleService', () => {
});
afterEach(async () => {
clock.uninstall();
await Promise.all([
app.get(DI.metasRepository).delete({}),
usersRepository.delete({}),
rolesRepository.delete({}),
roleAssignmentsRepository.delete({}),
]);
await app.close();
});
@@ -115,7 +119,7 @@ describe('RoleService', () => {
expect(result.canManageCustomEmojis).toBe(false);
});
test('instance default policies 2', async () => {
test('instance default policies 2', async () => {
const user = await createUser();
metaService.fetch.mockResolvedValue({
policies: {
@@ -128,7 +132,7 @@ describe('RoleService', () => {
expect(result.canManageCustomEmojis).toBe(true);
});
test('with role', async () => {
test('with role', async () => {
const user = await createUser();
const role = await createRole({
name: 'a',
@@ -140,7 +144,7 @@ describe('RoleService', () => {
},
},
});
await assign(role.id, user.id);
await roleService.assign(user.id, role.id);
metaService.fetch.mockResolvedValue({
policies: {
canManageCustomEmojis: false,
@@ -152,7 +156,7 @@ describe('RoleService', () => {
expect(result.canManageCustomEmojis).toBe(true);
});
test('priority', async () => {
test('priority', async () => {
const user = await createUser();
const role1 = await createRole({
name: 'role1',
@@ -174,8 +178,8 @@ describe('RoleService', () => {
},
},
});
await assign(role1.id, user.id);
await assign(role2.id, user.id);
await roleService.assign(user.id, role1.id);
await roleService.assign(user.id, role2.id);
metaService.fetch.mockResolvedValue({
policies: {
driveCapacityMb: 50,
@@ -187,7 +191,7 @@ describe('RoleService', () => {
expect(result.driveCapacityMb).toBe(100);
});
test('conditional role', async () => {
test('conditional role', async () => {
const user1 = await createUser({
createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)),
});
@@ -228,5 +232,42 @@ describe('RoleService', () => {
expect(user1Policies.canManageCustomEmojis).toBe(false);
expect(user2Policies.canManageCustomEmojis).toBe(true);
});
test('expired role', async () => {
const user = await createUser();
const role = await createRole({
name: 'a',
policies: {
canManageCustomEmojis: {
useDefault: false,
priority: 0,
value: true,
},
},
});
await roleService.assign(user.id, role.id, new Date(Date.now() + (1000 * 60 * 60 * 24)));
metaService.fetch.mockResolvedValue({
policies: {
canManageCustomEmojis: false,
},
} as any);
const result = await roleService.getUserPolicies(user.id);
expect(result.canManageCustomEmojis).toBe(true);
clock.tick('25:00:00');
const resultAfter25h = await roleService.getUserPolicies(user.id);
expect(resultAfter25h.canManageCustomEmojis).toBe(false);
await roleService.assign(user.id, role.id);
// ストリーミング経由で反映されるまでちょっと待つ
clock.uninstall();
await sleep(100);
const resultAfter25hAgain = await roleService.getUserPolicies(user.id);
expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
});
});
});

View File

@@ -0,0 +1,42 @@
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', () => {
expect(contentDisposition('inline', 'foo bar')).toBe('inline; filename=\"foo_bar\"; filename*=UTF-8\'\'foo%20bar');
});
test('attachment', () => {
expect(contentDisposition('attachment', 'foo bar')).toBe('attachment; filename=\"foo_bar\"; filename*=UTF-8\'\'foo%20bar');
});
test('non ascii', () => {
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');
});
});

View File

@@ -1,87 +1,50 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import * as childProcess from 'child_process';
import * as http from 'node:http';
import { SIGKILL } from 'constants';
import { readFile } from 'node:fs/promises';
import { isAbsolute, basename } from 'node:path';
import WebSocket from 'ws';
import fetch from 'node-fetch';
import FormData from 'form-data';
import fetch, { Blob, File, RequestInit } from 'node-fetch';
import { DataSource } from 'typeorm';
import got, { RequestError } from 'got';
import loadConfig from '../src/config/load.js';
import { entities } from '@/postgres.js';
import { entities } from '../src/postgres.js';
import { loadConfig } from '../src/config.js';
import type * as misskey from 'misskey-js';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
export { server as startServer } from '@/boot/common.js';
const config = loadConfig();
export const port = config.port;
export const api = async (endpoint: string, params: any, me?: any) => {
endpoint = endpoint.replace(/^\//, '');
const auth = me ? {
i: me.token,
} : {};
try {
const res = await got<string>(`http://localhost:${port}/api/${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(Object.assign(auth, params)),
retry: {
limit: 0,
},
});
const status = res.statusCode;
const body = res.statusCode !== 204 ? await JSON.parse(res.body) : null;
return {
status,
body,
};
} catch (err: unknown) {
if (err instanceof RequestError && err.response) {
const status = err.response.statusCode;
const body = await JSON.parse(err.response.body as string);
return {
status,
body,
};
} else {
throw err;
}
}
const normalized = endpoint.replace(/^\//, '');
return await request(`api/${normalized}`, params, me);
};
export const request = async (path: string, params: any, me?: any): Promise<{ body: any, status: number }> => {
const request = async (path: string, params: any, me?: any): Promise<{ body: any, status: number }> => {
const auth = me ? {
i: me.token,
} : {};
const res = await fetch(`http://localhost:${port}/${path}`, {
const res = await relativeFetch(path, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(Object.assign(auth, params)),
redirect: 'manual',
});
const status = res.status;
const body = res.status === 200 ? await res.json().catch() : null;
const body = res.headers.get('content-type') === 'application/json; charset=utf-8'
? await res.json()
: null;
return {
body, status,
};
};
const relativeFetch = async (path: string, init?: RequestInit | undefined) => {
return await fetch(new URL(path, `http://127.0.0.1:${port}/`).toString(), init);
};
export const signup = async (params?: any): Promise<any> => {
const q = Object.assign({
username: 'test',
@@ -110,30 +73,46 @@ export const react = async (user: any, note: any, reaction: string): Promise<any
}, user);
};
interface UploadOptions {
/** Optional, absolute path or relative from ./resources/ */
path?: string | URL;
/** The name to be used for the file upload */
name?: string;
/** A Blob can be provided instead of path */
blob?: Blob;
}
/**
* Upload file
* @param user User
* @param _path Optional, absolute path or relative from ./resources/
*/
export const uploadFile = async (user: any, _path?: string): Promise<any> => {
const absPath = _path == null ? `${_dirname}/resources/Lenna.jpg` : path.isAbsolute(_path) ? _path : `${_dirname}/resources/${_path}`;
export const uploadFile = async (user: any, { path, name, blob }: UploadOptions = {}): Promise<any> => {
const absPath = path == null
? new URL('resources/Lenna.jpg', import.meta.url)
: isAbsolute(path.toString())
? new URL(path)
: new URL(path, new URL('resources/', import.meta.url));
const formData = new FormData() as any;
const formData = new FormData();
formData.append('i', user.token);
formData.append('file', fs.createReadStream(absPath));
formData.append('file', blob ??
new File([await readFile(absPath)], basename(absPath.toString())));
formData.append('force', 'true');
if (name) {
formData.append('name', name);
}
const res = await got<string>(`http://localhost:${port}/api/drive/files/create`, {
const res = await relativeFetch('api/drive/files/create', {
method: 'POST',
body: formData,
retry: {
limit: 0,
},
});
const body = res.statusCode !== 204 ? await JSON.parse(res.body) : null;
const body = res.status !== 204 ? await res.json() : null;
return body;
return {
status: res.status,
body,
};
};
export const uploadUrl = async (user: any, url: string) => {
@@ -160,7 +139,7 @@ export const uploadUrl = async (user: any, url: string) => {
export function connectStream(user: any, channel: string, listener: (message: Record<string, any>) => any, params?: any): Promise<WebSocket> {
return new Promise((res, rej) => {
const ws = new WebSocket(`ws://localhost:${port}/streaming?i=${user.token}`);
const ws = new WebSocket(`ws://127.0.0.1:${port}/streaming?i=${user.token}`);
ws.on('open', () => {
ws.on('message', data => {
@@ -187,7 +166,7 @@ export function connectStream(user: any, channel: string, listener: (message: Re
export const waitFire = async (user: any, channel: string, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: any) => {
return new Promise<boolean>(async (res, rej) => {
let timer: NodeJS.Timeout;
let timer: NodeJS.Timeout | null = null;
let ws: WebSocket;
try {
@@ -219,41 +198,25 @@ export const waitFire = async (user: any, channel: string, trgr: () => any, cond
});
};
export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status?: number, type?: string, location?: string }> => {
// node-fetchだと3xxを取れない
return await new Promise((resolve, reject) => {
const req = http.request(`http://localhost:${port}${path}`, {
headers: {
Accept: accept,
},
}, res => {
if (res.statusCode! >= 400) {
reject(res);
} else {
resolve({
status: res.statusCode,
type: res.headers['content-type'],
location: res.headers.location,
});
}
});
req.end();
export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status: number, body: any, type: string | null, location: string | null }> => {
const res = await relativeFetch(path, {
headers: {
Accept: accept,
},
redirect: 'manual',
});
};
export function launchServer(callbackSpawnedProcess: (p: childProcess.ChildProcess) => void, moreProcess: () => Promise<void> = async () => {}) {
return (done: (err?: Error) => any) => {
const p = childProcess.spawn('node', [_dirname + '/../index.js'], {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
env: { NODE_ENV: 'test', PATH: process.env.PATH },
});
callbackSpawnedProcess(p);
p.on('message', message => {
if (message === 'ok') moreProcess().then(() => done()).catch(e => done(e));
});
const body = res.headers.get('content-type') === 'application/json; charset=utf-8'
? await res.json()
: null;
return {
status: res.status,
body,
type: res.headers.get('content-type'),
location: res.headers.get('location'),
};
}
};
export async function initTestDb(justBorrow = false, initEntities?: any[]) {
if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test';
@@ -275,46 +238,6 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) {
return db;
}
export function startServer(timeout = 60 * 1000): Promise<childProcess.ChildProcess> {
return new Promise((res, rej) => {
const t = setTimeout(() => {
p.kill(SIGKILL);
rej('timeout to start');
}, timeout);
const p = childProcess.spawn('node', [_dirname + '/../built/index.js'], {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
env: { NODE_ENV: 'test', PATH: process.env.PATH },
});
p.on('error', e => rej(e));
p.on('message', message => {
if (message === 'ok') {
clearTimeout(t);
res(p);
}
});
});
}
export function shutdownServer(p: childProcess.ChildProcess | undefined, timeout = 20 * 1000) {
if (p == null) return Promise.resolve('nop');
return new Promise((res, rej) => {
const t = setTimeout(() => {
p.kill(SIGKILL);
res('force exit');
}, timeout);
p.once('exit', () => {
clearTimeout(t);
res('exited');
});
p.kill();
});
}
export function sleep(msec: number) {
return new Promise<void>(res => {
setTimeout(() => {