From cee1d5e2d01359e6d762a10fc67d4e7c1cc830eb Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Thu, 30 Mar 2023 02:33:19 +0200 Subject: chore: integrate misskey-js as a workspace item (git subtree) (#10409) * Additional changes for the merge * api-misskey-js --- packages/misskey-js/test/api.ts | 212 ++++++++++++++++++++++++++++++++++ packages/misskey-js/test/streaming.ts | 165 ++++++++++++++++++++++++++ 2 files changed, 377 insertions(+) create mode 100644 packages/misskey-js/test/api.ts create mode 100644 packages/misskey-js/test/streaming.ts (limited to 'packages/misskey-js/test') diff --git a/packages/misskey-js/test/api.ts b/packages/misskey-js/test/api.ts new file mode 100644 index 0000000000..47c8378014 --- /dev/null +++ b/packages/misskey-js/test/api.ts @@ -0,0 +1,212 @@ +import { APIClient, isAPIError } from '../src/api'; +import { enableFetchMocks } from 'jest-fetch-mock'; + +enableFetchMocks(); + +function getFetchCall(call: any[]) { + const { body, method } = call[1]; + if (body != null && typeof body != 'string') { + throw new Error('invalid body'); + } + return { + url: call[0], + method: method, + body: JSON.parse(body as any) + }; +} + +describe('API', () => { + test('success', async () => { + fetchMock.resetMocks(); + fetchMock.mockResponse(async (req) => { + const body = await req.json(); + if (req.method == 'POST' && req.url == 'https://misskey.test/api/i') { + if (body.i === 'TOKEN') { + return JSON.stringify({ id: 'foo' }); + } else { + return { status: 400 }; + } + } else { + return { status: 404 }; + } + }); + + const cli = new APIClient({ + origin: 'https://misskey.test', + credential: 'TOKEN', + }); + + const res = await cli.request('i'); + + expect(res).toEqual({ + id: 'foo' + }); + + expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({ + url: 'https://misskey.test/api/i', + method: 'POST', + body: { i: 'TOKEN' } + }); + }); + + test('with params', async () => { + fetchMock.resetMocks(); + fetchMock.mockResponse(async (req) => { + const body = await req.json(); + if (req.method == 'POST' && req.url == 'https://misskey.test/api/notes/show') { + if (body.i === 'TOKEN' && body.noteId === 'aaaaa') { + return JSON.stringify({ id: 'foo' }); + } else { + return { status: 400 }; + } + } else { + return { status: 404 }; + } + }); + + const cli = new APIClient({ + origin: 'https://misskey.test', + credential: 'TOKEN', + }); + + const res = await cli.request('notes/show', { noteId: 'aaaaa' }); + + expect(res).toEqual({ + id: 'foo' + }); + + expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({ + url: 'https://misskey.test/api/notes/show', + method: 'POST', + body: { i: 'TOKEN', noteId: 'aaaaa' } + }); + }); + + test('204 No Content で null が返る', async () => { + fetchMock.resetMocks(); + fetchMock.mockResponse(async (req) => { + if (req.method == 'POST' && req.url == 'https://misskey.test/api/reset-password') { + return { status: 204 }; + } else { + return { status: 404 }; + } + }); + + const cli = new APIClient({ + origin: 'https://misskey.test', + credential: 'TOKEN', + }); + + const res = await cli.request('reset-password', { token: 'aaa', password: 'aaa' }); + + expect(res).toEqual(null); + + expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({ + url: 'https://misskey.test/api/reset-password', + method: 'POST', + body: { i: 'TOKEN', token: 'aaa', password: 'aaa' } + }); + }); + + test('インスタンスの credential が指定されていても引数で credential が null ならば null としてリクエストされる', async () => { + fetchMock.resetMocks(); + fetchMock.mockResponse(async (req) => { + const body = await req.json(); + if (req.method == 'POST' && req.url == 'https://misskey.test/api/i') { + if (typeof body.i === 'string') { + return JSON.stringify({ id: 'foo' }); + } else { + return { + status: 401, + body: JSON.stringify({ + error: { + message: 'Credential required.', + code: 'CREDENTIAL_REQUIRED', + id: '1384574d-a912-4b81-8601-c7b1c4085df1', + } + }) + }; + } + } else { + return { status: 404 }; + } + }); + + try { + const cli = new APIClient({ + origin: 'https://misskey.test', + credential: 'TOKEN', + }); + + await cli.request('i', {}, null); + } catch (e) { + expect(isAPIError(e)).toEqual(true); + } + }); + + test('api error', async () => { + fetchMock.resetMocks(); + fetchMock.mockResponse(async (req) => { + return { + status: 500, + body: JSON.stringify({ + error: { + message: 'Internal error occurred. Please contact us if the error persists.', + code: 'INTERNAL_ERROR', + id: '5d37dbcb-891e-41ca-a3d6-e690c97775ac', + kind: 'server', + }, + }) + }; + }); + + try { + const cli = new APIClient({ + origin: 'https://misskey.test', + credential: 'TOKEN', + }); + + await cli.request('i'); + } catch (e: any) { + expect(isAPIError(e)).toEqual(true); + expect(e.id).toEqual('5d37dbcb-891e-41ca-a3d6-e690c97775ac'); + } + }); + + test('network error', async () => { + fetchMock.resetMocks(); + fetchMock.mockAbort(); + + try { + const cli = new APIClient({ + origin: 'https://misskey.test', + credential: 'TOKEN', + }); + + await cli.request('i'); + } catch (e) { + expect(isAPIError(e)).toEqual(false); + } + }); + + test('json parse error', async () => { + fetchMock.resetMocks(); + fetchMock.mockResponse(async (req) => { + return { + status: 500, + body: 'I AM NOT JSON' + }; + }); + + try { + const cli = new APIClient({ + origin: 'https://misskey.test', + credential: 'TOKEN', + }); + + await cli.request('i'); + } catch (e) { + expect(isAPIError(e)).toEqual(false); + } + }); +}); diff --git a/packages/misskey-js/test/streaming.ts b/packages/misskey-js/test/streaming.ts new file mode 100644 index 0000000000..913db8b287 --- /dev/null +++ b/packages/misskey-js/test/streaming.ts @@ -0,0 +1,165 @@ +import WS from 'jest-websocket-mock'; +import Stream from '../src/streaming'; + +describe('Streaming', () => { + test('useChannel', async () => { + const server = new WS('wss://misskey.test/streaming'); + const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); + const mainChannelReceived: any[] = []; + const main = stream.useChannel('main'); + main.on('meUpdated', payload => { + mainChannelReceived.push(payload); + }); + + const ws = await server.connected; + expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); + + const msg = JSON.parse(await server.nextMessage as string); + const mainChannelId = msg.body.id; + expect(msg.type).toEqual('connect'); + expect(msg.body.channel).toEqual('main'); + expect(mainChannelId != null).toEqual(true); + + server.send(JSON.stringify({ + type: 'channel', + body: { + id: mainChannelId, + type: 'meUpdated', + body: { + id: 'foo' + } + } + })); + + expect(mainChannelReceived[0]).toEqual({ + id: 'foo' + }); + + stream.close(); + server.close(); + }); + + test('useChannel with parameters', async () => { + const server = new WS('wss://misskey.test/streaming'); + const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); + const messagingChannelReceived: any[] = []; + const messaging = stream.useChannel('messaging', { otherparty: 'aaa' }); + messaging.on('message', payload => { + messagingChannelReceived.push(payload); + }); + + const ws = await server.connected; + expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); + + const msg = JSON.parse(await server.nextMessage as string); + const messagingChannelId = msg.body.id; + expect(msg.type).toEqual('connect'); + expect(msg.body.channel).toEqual('messaging'); + expect(msg.body.params).toEqual({ otherparty: 'aaa' }); + expect(messagingChannelId != null).toEqual(true); + + server.send(JSON.stringify({ + type: 'channel', + body: { + id: messagingChannelId, + type: 'message', + body: { + id: 'foo' + } + } + })); + + expect(messagingChannelReceived[0]).toEqual({ + id: 'foo' + }); + + stream.close(); + server.close(); + }); + + test('ちゃんとチャンネルごとにidが異なる', async () => { + const server = new WS('wss://misskey.test/streaming'); + const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); + + stream.useChannel('messaging', { otherparty: 'aaa' }); + stream.useChannel('messaging', { otherparty: 'bbb' }); + + const ws = await server.connected; + expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); + + const msg = JSON.parse(await server.nextMessage as string); + const messagingChannelId = msg.body.id; + const msg2 = JSON.parse(await server.nextMessage as string); + const messagingChannelId2 = msg2.body.id; + + expect(messagingChannelId != null).toEqual(true); + expect(messagingChannelId2 != null).toEqual(true); + expect(messagingChannelId).not.toEqual(messagingChannelId2); + + stream.close(); + server.close(); + }); + + test('Connection#send', async () => { + const server = new WS('wss://misskey.test/streaming'); + const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); + + const messaging = stream.useChannel('messaging', { otherparty: 'aaa' }); + messaging.send('read', { id: 'aaa' }); + + const ws = await server.connected; + expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); + + const connectMsg = JSON.parse(await server.nextMessage as string); + const channelId = connectMsg.body.id; + const msg = JSON.parse(await server.nextMessage as string); + + expect(msg.type).toEqual('ch'); + expect(msg.body.id).toEqual(channelId); + expect(msg.body.type).toEqual('read'); + expect(msg.body.body).toEqual({ id: 'aaa' }); + + stream.close(); + server.close(); + }); + + test('Connection#dispose', async () => { + const server = new WS('wss://misskey.test/streaming'); + const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); + const mainChannelReceived: any[] = []; + const main = stream.useChannel('main'); + main.on('meUpdated', payload => { + mainChannelReceived.push(payload); + }); + + const ws = await server.connected; + expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); + + const msg = JSON.parse(await server.nextMessage as string); + const mainChannelId = msg.body.id; + expect(msg.type).toEqual('connect'); + expect(msg.body.channel).toEqual('main'); + expect(mainChannelId != null).toEqual(true); + main.dispose(); + + server.send(JSON.stringify({ + type: 'channel', + body: { + id: mainChannelId, + type: 'meUpdated', + body: { + id: 'foo' + } + } + })); + + expect(mainChannelReceived.length).toEqual(0); + + stream.close(); + server.close(); + }); + + // TODO: SharedConnection#dispose して一定時間経ったら disconnect メッセージがサーバーに送られてくるかのテスト + + // TODO: チャンネル接続が使いまわされるかのテスト +}); -- cgit v1.2.3-freya