UNPKG

tailwind

Version:

tailwind is a base module for streaming and evented CQS applications.

1,429 lines (1,171 loc) 41.6 kB
'use strict'; const { PassThrough } = require('stream'), path = require('path'); const assert = require('assertthat'), jsonLinesClient = require('json-lines-client'), needle = require('needle'), nodeenv = require('nodeenv'), uuid = require('uuidv4'); const buildEvent = require('../../../../shared/buildEvent'), issueToken = require('../../../../shared/issueToken'), startApp = require('./startApp'); suite('Server', () => { let restoreEnvironmentVariables; suiteSetup(() => { // Disable SSL certificate checks to allow running these tests with a // self-signed certificate. restoreEnvironmentVariables = nodeenv('NODE_TLS_REJECT_UNAUTHORIZED', '0'); }); suiteTeardown(() => { restoreEnvironmentVariables(); }); suite('routes', () => { let app; suiteSetup(async () => { app = await startApp({ port: 3000, corsOrigin: '*' }); }); test('delivers the correct CORS headers.', async () => { const corsOrigins = { 3001: { origin: 'http://www.thenativeweb.io', allow: '*', expected: '*' }, 3002: { origin: 'http://www.thenativeweb.io', allow: 'http://www.thenativeweb.io', expected: 'http://www.thenativeweb.io' }, 3003: { origin: 'http://www.thenativeweb.io', allow: /\.thenativeweb\.io$/, expected: 'http://www.thenativeweb.io' }, 3004: { origin: 'http://www.example.com', allow: /\.thenativeweb\.io$/, expected: undefined }, 3005: { origin: 'http://www.thenativeweb.io', allow: [ 'http://www.thenativeweb.io', 'http://www.example.com' ], expected: 'http://www.thenativeweb.io' }, 3006: { origin: 'http://www.example.com', allow: 'http://www.thenativeweb.io', expected: undefined } }; const ports = Object.keys(corsOrigins); for (let i = 0; i < ports.length; i++) { const port = ports[i]; const corsOrigin = corsOrigins[port]; await startApp({ port, corsOrigin: corsOrigin.allow }); const res = await needle('options', `http://localhost:${port}/v1/ping`, undefined, { headers: { origin: corsOrigin.origin, 'access-control-request-method': 'POST', 'access-control-request-headers': 'X-Requested-With' } }); assert.that(res.headers['access-control-allow-origin']).is.equalTo(corsOrigin.expected); assert.that(res.headers['access-control-allow-methods']).is.equalTo('GET,POST'); assert.that(res.statusCode).is.equalTo(200); } }); suite('GET /v1/ping', () => { test('returns 200.', async () => { const res = await needle('get', 'http://localhost:3000/v1/ping'); assert.that(res.statusCode).is.equalTo(200); }); test('returns application/json.', async () => { const res = await needle('get', 'http://localhost:3000/v1/ping'); assert.that(res.headers['content-type']).is.equalTo('application/json; charset=utf-8'); }); test('answers with api version v1.', async () => { const res = await needle('get', 'http://localhost:3000/v1/ping'); assert.that(res.body).is.equalTo({ api: 'v1' }); }); }); suite('GET /v1/configuration.json', () => { test('returns 200.', async () => { const res = await needle('get', 'http://localhost:3000/v1/configuration.json'); assert.that(res.statusCode).is.equalTo(200); }); test('returns text/javascript.', async () => { const res = await needle('get', 'http://localhost:3000/v1/configuration.json'); assert.that(res.headers['content-type']).is.equalTo('application/json; charset=utf-8'); }); test('serves the application configuration.', async () => { const res = await needle('get', 'http://localhost:3000/v1/configuration.json'); assert.that(res.body).is.ofType('object'); assert.that(res.body.writeModel).is.equalTo({ network: { node: { commands: { ping: {}}, events: { pinged: {}} } } }); assert.that(res.body.readModel).is.equalTo({ lists: { pings: {} } }); }); }); suite('POST /v1/command', () => { test('returns 415 if the content-type header is missing.', async () => { const res = await needle('post', 'http://localhost:3000/v1/command', 'foobar'); assert.that(res.statusCode).is.equalTo(415); assert.that(res.body).is.equalTo('Header content-type must be application/json.'); }); test('returns 415 if content-type is not set to application/json.', async () => { const res = await needle('post', 'http://localhost:3000/v1/command', 'foobar', { headers: { 'content-type': 'text/plain' }, json: true }); assert.that(res.statusCode).is.equalTo(415); assert.that(res.body).is.equalTo('Header content-type must be application/json.'); }); test('returns 400 if a malformed command is sent.', async () => { const res = await needle('post', 'http://localhost:3000/v1/command', { foo: 'bar' }, { json: true }); assert.that(res.statusCode).is.equalTo(400); assert.that(res.body).is.equalTo('Malformed command.'); }); test('returns 400 if a wellformed command is sent with a non-existent context name.', async () => { const command = new app.Command({ context: { name: 'foo' }, aggregate: { name: 'node', id: uuid() }, name: 'ping', data: { foo: 'foobar' } }); const res = await needle('post', 'http://localhost:3000/v1/command', command, { json: true }); assert.that(res.statusCode).is.equalTo(400); assert.that(res.body).is.equalTo('Unknown context name.'); }); test('returns 400 if a wellformed command is sent with a non-existent aggregate name.', async () => { const command = new app.Command({ context: { name: 'network' }, aggregate: { name: 'foo', id: uuid() }, name: 'ping', data: { foo: 'foobar' } }); const res = await needle('post', 'http://localhost:3000/v1/command', command, { json: true }); assert.that(res.statusCode).is.equalTo(400); assert.that(res.body).is.equalTo('Unknown aggregate name.'); }); test('returns 400 if a wellformed command is sent with a non-existent command name.', async () => { const command = new app.Command({ context: { name: 'network' }, aggregate: { name: 'node', id: uuid() }, name: 'foo', data: { foo: 'foobar' } }); const res = await needle('post', 'http://localhost:3000/v1/command', command, { json: true }); assert.that(res.statusCode).is.equalTo(400); assert.that(res.body).is.equalTo('Unknown command name.'); }); test('returns 200 if a wellformed command is sent and everything is fine.', async () => { const command = new app.Command({ context: { name: 'network' }, aggregate: { name: 'node', id: uuid() }, name: 'ping', data: { foo: 'foobar' } }); await new Promise((resolve, reject) => { app.api.incoming.once('data', () => { resolve(); }); (async () => { try { const res = await needle('post', 'http://localhost:3000/v1/command', command, { json: true }); assert.that(res.statusCode).is.equalTo(200); } catch (ex) { reject(ex); } })(); }); }); test('emits an incoming command to the app.api.incoming stream.', async () => { const command = new app.Command({ context: { name: 'network' }, aggregate: { name: 'node', id: uuid() }, name: 'ping', data: { foo: 'foobar' } }); await new Promise((resolve, reject) => { app.api.incoming.once('data', actual => { try { assert.that(actual.context.name).is.equalTo(command.context.name); assert.that(actual.aggregate.name).is.equalTo(command.aggregate.name); assert.that(actual.aggregate.id).is.equalTo(command.aggregate.id); assert.that(actual.name).is.equalTo(command.name); assert.that(actual.data).is.equalTo(command.data); assert.that(actual.user.id).is.equalTo('anonymous'); assert.that(actual.user.token.sub).is.equalTo('anonymous'); assert.that(actual.user.token.iss).is.equalTo('https://token.invalid'); } catch (ex) { return reject(ex); } resolve(); }); needle.post('http://localhost:3000/v1/command', command, { json: true }); }); }); }); suite('POST /v1/events', () => { test('receives an event from the app.api.outgoing stream.', async () => { const joinedEvent = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'Jane Doe' }); const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events' }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'Jane Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(joinedEvent); }); }); test('receives multiple events from the app.api.outgoing stream.', async () => { const joinedEvent1 = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'Jane Doe' }); const joinedEvent2 = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'John Doe' }); const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events' }); await new Promise((resolve, reject) => { let counter = 0; const onData = function (event) { try { counter += 1; switch (counter) { case 1: { assert.that(event.data).is.equalTo({ participant: 'Jane Doe' }); break; } case 2: { assert.that(event.data).is.equalTo({ participant: 'John Doe' }); server.disconnect(); server.stream.removeListener('data', onData); resolve(); break; } default: { // Intentionally left blank. } } } catch (ex) { reject(ex); } }; server.stream.on('data', onData); app.api.outgoing.write(joinedEvent1); app.api.outgoing.write(joinedEvent2); }); }); test('receives filtered events from the app.api.outgoing stream.', async () => { const startedEvent = buildEvent('planning', 'peerGroup', uuid(), 'started', { participant: 'Jane Doe' }); const joinedEvent = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'John Doe' }); const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events', body: { name: 'joined' } }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'John Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(startedEvent); app.api.outgoing.write(joinedEvent); }); }); suite('filters events based on authorization options', () => { test('sends public events to public users.', async () => { const eventForPublic = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'John Doe' }); eventForPublic.metadata.isAuthorized = { owner: uuid(), forAuthenticated: true, forPublic: true }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events' }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'John Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(eventForPublic); }); }); test('sends public events to authenticated users.', async () => { const eventForPublic = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'John Doe' }); eventForPublic.metadata.isAuthorized = { owner: uuid(), forAuthenticated: true, forPublic: true }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events', headers: { authorization: `Bearer ${issueToken('Jane Doe')}` } }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'John Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(eventForPublic); }); }); test('sends public events to owners.', async () => { const ownerId = uuid(); const eventForPublic = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'John Doe' }); eventForPublic.metadata.isAuthorized = { owner: ownerId, forAuthenticated: true, forPublic: true }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events', headers: { authorization: `Bearer ${issueToken(ownerId)}` } }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'John Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(eventForPublic); }); }); test('does not send authenticated events to public users.', async () => { const eventForAuthenticated = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'Jane Doe' }); eventForAuthenticated.metadata.isAuthorized = { owner: uuid(), forAuthenticated: true, forPublic: false }; const eventForPublic = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'John Doe' }); eventForPublic.metadata.isAuthorized = { owner: uuid(), forAuthenticated: true, forPublic: true }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events' }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'John Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(eventForAuthenticated); app.api.outgoing.write(eventForPublic); }); }); test('sends authenticated events to authenticated users.', async () => { const eventForAuthenticated = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'Jane Doe' }); eventForAuthenticated.metadata.isAuthorized = { owner: uuid(), forAuthenticated: true, forPublic: false }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events', headers: { authorization: `Bearer ${issueToken('Jane Doe')}` } }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'Jane Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(eventForAuthenticated); }); }); test('sends authenticated events to owners.', async () => { const ownerId = uuid(); const eventForAuthenticated = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'Jane Doe' }); eventForAuthenticated.metadata.isAuthorized = { owner: ownerId, forAuthenticated: true, forPublic: false }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events', headers: { authorization: `Bearer ${issueToken(ownerId)}` } }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'Jane Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(eventForAuthenticated); }); }); test('does not send owner events to public users.', async () => { const ownerId = uuid(); const eventForOwner = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'Jane Doe' }); eventForOwner.metadata.isAuthorized = { owner: ownerId, forAuthenticated: false, forPublic: false }; const eventForPublic = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'John Doe' }); eventForPublic.metadata.isAuthorized = { owner: uuid(), forAuthenticated: true, forPublic: true }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events' }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'John Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(eventForOwner); app.api.outgoing.write(eventForPublic); }); }); test('does not send owner events to authenticated users.', async () => { const ownerId = uuid(); const eventForOwner = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'Jane Doe' }); eventForOwner.metadata.isAuthorized = { owner: ownerId, forAuthenticated: false, forPublic: false }; const eventForPublic = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'John Doe' }); eventForPublic.metadata.isAuthorized = { owner: uuid(), forAuthenticated: true, forPublic: true }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events', headers: { authorization: `Bearer ${issueToken('Jane Doe')}` } }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'John Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(eventForOwner); app.api.outgoing.write(eventForPublic); }); }); test('sends owner events to owners.', async () => { const ownerId = uuid(); const eventForOwner = buildEvent('planning', 'peerGroup', uuid(), 'joined', { participant: 'Jane Doe' }); eventForOwner.metadata.isAuthorized = { owner: ownerId, forAuthenticated: false, forPublic: false }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/events', headers: { authorization: `Bearer ${issueToken(ownerId)}` } }); await new Promise((resolve, reject) => { server.stream.once('data', event => { try { assert.that(event.data).is.equalTo({ participant: 'Jane Doe' }); server.disconnect(); } catch (ex) { return reject(ex); } resolve(); }); app.api.outgoing.write(eventForOwner); }); }); }); }); suite('POST /v1/read/:modelType/:modelName?where=...&orderBy=...&skip=...&take=...', () => { test('returns 404 when no model type is given.', async () => { await assert.that(async () => { await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read' }); }).is.throwingAsync(ex => ex.code === 'ESTATUSCODEUNEXPECTED'); }); test('returns 404 when no model name is given.', async () => { await assert.that(async () => { await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/Lists' }); }).is.throwingAsync(ex => ex.code === 'ESTATUSCODEUNEXPECTED'); }); test('returns 400 when specifying a non-existent model type.', async () => { await assert.that(async () => { await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/non-existent/foo' }); }).is.throwingAsync(ex => ex.code === 'ESTATUSCODEUNEXPECTED'); }); test('returns 400 when specifying a non-existent model name.', async () => { await assert.that(async () => { await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/foo' }); }).is.throwingAsync(ex => ex.code === 'ESTATUSCODEUNEXPECTED'); }); test('passes the given model type and model name to the app.api.read function.', async () => { app.api.read = async function (modelType, modelName) { assert.that(modelType).is.equalTo('lists'); assert.that(modelName).is.equalTo('pings'); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings' }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('passes the given where to the app.api.read function.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.where).is.equalTo({ $and: [ { lastName: 'Doe' }, { $or: [ { 'isAuthorized.owner': 'anonymous' }, { 'isAuthorized.forPublic': true } ]} ] }); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { where: JSON.stringify({ lastName: 'Doe' }) } }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('attaches the authenticated user to the where clause.', async () => { const ownerId = uuid(); app.api.read = async function (modelType, modelName, options) { assert.that(options.where).is.equalTo({ $and: [ { lastName: 'Doe' }, { $or: [ { 'isAuthorized.owner': ownerId }, { 'isAuthorized.forPublic': true }, { 'isAuthorized.forAuthenticated': true } ]} ] }); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { where: JSON.stringify({ lastName: 'Doe' }) }, headers: { authorization: `Bearer ${issueToken(ownerId)}` } }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('falls back to an empty where if where is missing.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.where).is.equalTo({ $and: [ {}, { $or: [ { 'isAuthorized.owner': 'anonymous' }, { 'isAuthorized.forPublic': true } ]} ] }); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings' }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('passes the given order by to the app.api.read function.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.orderBy).is.equalTo({ lastName: 'ascending' }); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { orderBy: JSON.stringify({ lastName: 'ascending' }) } }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('falls back to an empty order by if order by is missing.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.orderBy).is.equalTo({}); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings' }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('passes the given skip to the app.api.read function.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.skip).is.equalTo(23); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { skip: 23 } }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('falls back to skip=0 if skip is missing.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.skip).is.equalTo(0); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings' }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('falls back to skip=0 if skip is invalid.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.skip).is.equalTo(0); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { skip: 'abc' } }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('passes the given take to the app.api.read function.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.take).is.equalTo(23); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { take: 23 } }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('falls back to take=100 if take is missing.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.take).is.equalTo(100); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings' }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('falls back to take=100 if take is invalid.', async () => { app.api.read = async function (modelType, modelName, options) { assert.that(options.take).is.equalTo(100); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { take: 'abc' } }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('passes the user to the app.api.read function.', async () => { const ownerId = uuid(); app.api.read = async function (modelType, modelName, { user }) { assert.that(user).is.atLeast({ id: ownerId, token: { iss: 'https://auth.thenativeweb.io', sub: ownerId } }); const fakeStream = new PassThrough({ objectMode: true }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { where: JSON.stringify({ lastName: 'Doe' }) }, headers: { authorization: `Bearer ${issueToken(ownerId)}` } }); await new Promise(resolve => { server.stream.once('end', () => { resolve(); }); server.stream.resume(); }); }); test('returns 400 when an invalid where is given.', async () => { await assert.that(async () => { await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { where: 'foo' } }); }).is.throwingAsync(ex => ex.code === 'ESTATUSCODEUNEXPECTED'); }); test('returns 400 when an invalid order by is given.', async () => { await assert.that(async () => { await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings', query: { orderBy: 'foo' } }); }).is.throwingAsync(ex => ex.code === 'ESTATUSCODEUNEXPECTED'); }); test('returns 500 when read returns an error.', async () => { app.api.read = async function () { throw new Error(); }; await assert.that(async () => { await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings' }); }).is.throwingAsync(ex => ex.code === 'ESTATUSCODEUNEXPECTED'); }); test('streams a single data item from the app.api.read function to the client.', async () => { app.api.read = async function () { const fakeStream = new PassThrough({ objectMode: true }); fakeStream.write({ foo: 'bar' }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings' }); await new Promise((resolve, reject) => { server.stream.once('data', modelEvent => { try { assert.that(modelEvent).is.equalTo({ foo: 'bar' }); } catch (ex) { return reject(ex); } resolve(); }); }); }); test('returns multiple documents as JSON.', async () => { app.api.read = async function () { const fakeStream = new PassThrough({ objectMode: true }); fakeStream.write({ foo: 'bar' }); fakeStream.write({ foo: 'baz' }); fakeStream.end(); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings' }); await new Promise((resolve, reject) => { let counter = 0; server.stream.on('data', modelEvent => { try { counter += 1; switch (counter) { case 1: assert.that(modelEvent).is.equalTo({ foo: 'bar' }); break; case 2: assert.that(modelEvent).is.equalTo({ foo: 'baz' }); resolve(); break; default: throw new Error('Invalid operation.'); } } catch (ex) { return reject(ex); } }); }); }); test('closes the stream when the client disconnects.', async () => { const fakeStream = new PassThrough({ objectMode: true }); app.api.read = async function () { fakeStream.write({ foo: 'bar' }); return fakeStream; }; const server = await jsonLinesClient({ protocol: 'http', host: 'localhost', port: 3000, path: '/v1/read/lists/pings' }); server.disconnect(); // Wait a short time to give the stream the chance to close. await new Promise(resolve => setTimeout(resolve, 0.1 * 1000)); await new Promise((resolve, reject) => { fakeStream.once('error', err => { try { assert.that(err.message).is.equalTo('write after end'); } catch (ex) { return reject(ex); } resolve(); }).write({ foo: 'bar' }); }); }); }); }); suite('serveStatic', () => { suiteSetup(async () => { const staticDirectory = path.join(__dirname, 'serveStatic'); await startApp({ port: 2999, corsOrigin: '*', serveStatic: staticDirectory }); }); test('serves static content from given directory.', async () => { const res = await needle('get', 'http://localhost:2999/test.txt'); assert.that(res.statusCode).is.equalTo(200); assert.that(res.body).is.ofType('string'); assert.that(res.body).is.equalTo('This is a static file.\n'); }); test('uses gzip compression.', async () => { const res = await needle('get', 'http://localhost:2999/compression-test.html', { headers: { 'Accept-Encoding': 'gzip, deflate' } }); assert.that(res.statusCode).is.equalTo(200); assert.that(res.headers['content-encoding']).is.equalTo('gzip'); }); }); });