Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: move tests from our SaaS repo into the public repo #1147

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 248 additions & 0 deletions test/gen-server/lib/DocApiForwarder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import { delay } from 'app/common/delay';
import { createDummyGristServer } from 'app/server/lib/GristServer';
import axios, { AxiosResponse } from 'axios';
import { fromCallback } from "bluebird";
import { assert } from 'chai';
import express = require("express");
import FormData from 'form-data';
import { Server } from 'http';
import defaultsDeep = require('lodash/defaultsDeep');
import morganLogger from 'morgan';
import { AddressInfo } from 'net';
import sinon = require("sinon");

import { createInitialDb, removeConnection, setUpDB } from "test/gen-server/seed";
import { configForUser } from 'test/gen-server/testUtils';

import { DocApiForwarder } from "app/gen-server/lib/DocApiForwarder";
import { DocWorkerMap, getDocWorkerMap } from "app/gen-server/lib/DocWorkerMap";
import { HomeDBManager } from "app/gen-server/lib/homedb/HomeDBManager";
import { addRequestUser } from 'app/server/lib/Authorizer';
import { jsonErrorHandler } from 'app/server/lib/expressWrap';
import log from 'app/server/lib/log';
import * as testUtils from 'test/server/testUtils';


const chimpy = configForUser('Chimpy');
const kiwi = configForUser('kiwi');

const logToConsole = false;

async function createServer(app: express.Application, name: string) {
let server: Server;
if (logToConsole) {
app.use(morganLogger((...args: any[]) => {
return `${log.timestamp()} ${name} ${morganLogger.dev(...args)}`;
}));
}
app.set('port', 0);
await fromCallback((cb: any) => server = app.listen(app.get('port'), 'localhost', cb));
log.info(`${name} listening ${getUrl(server!)}`);
return server!;
}

function getUrl(server: Server) {
return `http://localhost:${(server.address() as AddressInfo).port}`;
}

describe('DocApiForwarder', function() {

testUtils.setTmpLogLevel('error');

let homeServer: Server;
let docWorker: Server;
let resp: AxiosResponse;
let homeUrl: string;
let dbManager: HomeDBManager;
const docWorkerStub = sinon.stub();

before(async function() {
setUpDB(this);
dbManager = new HomeDBManager();
await dbManager.connect();
await createInitialDb(dbManager.connection);
await dbManager.initializeSpecialIds();

// create cheap doc worker
let app = express();
docWorker = await createServer(app, 'docw');
app.use(express.json());
app.use(docWorkerStub);

// create cheap home server
app = express();
homeServer = await createServer(app, 'home');
homeUrl = getUrl(homeServer);

// stubs doc worker map
const docWorkerMapStub = sinon.createStubInstance(DocWorkerMap);
docWorkerMapStub.assignDocWorker.returns(Promise.resolve({
docWorker: {
internalUrl: getUrl(docWorker) + '/dw/foo',
publicUrl: '',
id: '',
},
docMD5: null,
isActive: true,
}));

// create and register forwarder
const docApiForwarder = new DocApiForwarder(docWorkerMapStub, dbManager, null as any);
app.use("/api", addRequestUser.bind(null, dbManager, getDocWorkerMap().getPermitStore('internal'),
{gristServer: createDummyGristServer()} as any));
docApiForwarder.addEndpoints(app);
app.use('/api', jsonErrorHandler);
});

after(async function() {
await removeConnection();
homeServer.close();
docWorker.close();
dbManager.flushDocAuthCache(); // To avoid hanging up exit from tests.
});

beforeEach(() => {
docWorkerStub.resetHistory();
docWorkerStub.callsFake((req: any, res: any) => res.status(200).json('mango tree'));
});

it('should forward GET /api/docs/:did/tables/:tid/data', async function() {
resp = await axios.get(`${homeUrl}/api/docs/sampledocid_16/tables/table1/data`, chimpy);
assert.equal(resp.status, 200);
assert.equal(resp.data, 'mango tree');
assert(docWorkerStub.calledOnce);
const req = docWorkerStub.getCall(0).args[0];
assert.equal(req.get('Authorization'), 'Bearer api_key_for_chimpy');
assert.equal(req.get('Content-Type'), 'application/json');
assert.equal(req.originalUrl, '/dw/foo/api/docs/sampledocid_16/tables/table1/data');
assert.equal(req.method, 'GET');
});

it('should forward GET /api/docs/:did/tables/:tid/data?filter=<...>', async function() {
const filter = encodeURIComponent(JSON.stringify({FOO: ['bar']})); // => %7B%22FOO%22%3A%5B%22bar%22%5D%7D
resp = await axios.get(`${homeUrl}/api/docs/sampledocid_16/tables/table1/data?filter=${filter}`, chimpy);
assert.equal(resp.status, 200);
assert.equal(resp.data, 'mango tree');
assert(docWorkerStub.calledOnce);
const req = docWorkerStub.getCall(0).args[0];
assert.equal(req.get('Authorization'), 'Bearer api_key_for_chimpy');
assert.equal(req.get('Content-Type'), 'application/json');
assert.equal(req.originalUrl,
'/dw/foo/api/docs/sampledocid_16/tables/table1/data?filter=%7B%22FOO%22%3A%5B%22bar%22%5D%7D');
assert.equal(req.method, 'GET');
});

it('should deny user without view permissions', async function() {
resp = await axios.get(`${homeUrl}/api/docs/sampledocid_13/tables/table1/data`, kiwi);
assert.equal(resp.status, 403);
assert.deepEqual(resp.data, {error: 'No view access'});
assert.equal(docWorkerStub.callCount, 0);
});


it('should forward POST /api/docs/:did/tables/:tid/data', async function() {
resp = await axios.post(`${homeUrl}/api/docs/sampledocid_16/tables/table1/data`, {message: 'golden pears'}, chimpy);
assert.equal(resp.status, 200);
assert.equal(resp.data, 'mango tree');
assert(docWorkerStub.calledOnce);
const req = docWorkerStub.getCall(0).args[0];
assert.equal(req.get('Authorization'), 'Bearer api_key_for_chimpy');
assert.equal(req.get('Content-Type'), 'application/json');
assert.equal(req.originalUrl, '/dw/foo/api/docs/sampledocid_16/tables/table1/data');
assert.equal(req.method, 'POST');
assert.deepEqual(req.body, {message: 'golden pears'});
});


it('should forward PATCH /api/docs/:did/tables/:tid/data', async function() {
resp = await axios.patch(`${homeUrl}/api/docs/sampledocid_16/tables/table1/data`,
{message: 'golden pears'}, chimpy);
assert.equal(resp.status, 200);
assert.equal(resp.data, 'mango tree');
assert(docWorkerStub.calledOnce);
const req = docWorkerStub.getCall(0).args[0];
assert.equal(req.get('Authorization'), 'Bearer api_key_for_chimpy');
assert.equal(req.get('Content-Type'), 'application/json');
assert.equal(req.originalUrl, '/dw/foo/api/docs/sampledocid_16/tables/table1/data');
assert.equal(req.method, 'PATCH');
assert.deepEqual(req.body, {message: 'golden pears'});
});

it('should forward POST /api/docs/:did/attachments', async function() {
const formData = new FormData();
formData.append('upload', 'abcdef', "hello.png");
resp = await axios.post(`${homeUrl}/api/docs/sampledocid_16/attachments`, formData,
defaultsDeep({headers: formData.getHeaders()}, chimpy));
assert.equal(resp.status, 200);
assert.deepEqual(resp.headers['content-type'], 'application/json; charset=utf-8');
assert.deepEqual(resp.data, 'mango tree');
assert(docWorkerStub.calledOnce);
const req = docWorkerStub.getCall(0).args[0];
assert.equal(req.get('Authorization'), 'Bearer api_key_for_chimpy');
assert.match(req.get('Content-Type'), /^multipart\/form-data; boundary=/);
assert.equal(req.originalUrl, '/dw/foo/api/docs/sampledocid_16/attachments');
assert.equal(req.method, 'POST');
});

it('should forward GET /api/docs/:did/attachments/:attId/download', async function() {
docWorkerStub.callsFake((_req: any, res: any) =>
res.status(200)
.type('.png')
.set('Content-Disposition', 'attachment; filename="hello.png"')
.set('Cache-Control', 'private, max-age=3600')
.send(Buffer.from('abcdef')));
resp = await axios.get(`${homeUrl}/api/docs/sampledocid_16/attachments/123/download`, chimpy);
assert.equal(resp.status, 200);
assert.deepEqual(resp.headers['content-type'], 'image/png');
assert.deepEqual(resp.headers['content-disposition'], 'attachment; filename="hello.png"');
assert.deepEqual(resp.headers['cache-control'], 'private, max-age=3600');
assert.deepEqual(resp.data, 'abcdef');
assert(docWorkerStub.calledOnce);
const req = docWorkerStub.getCall(0).args[0];
assert.equal(req.get('Authorization'), 'Bearer api_key_for_chimpy');
assert.equal(req.get('Content-Type'), 'application/json');
assert.equal(req.originalUrl, '/dw/foo/api/docs/sampledocid_16/attachments/123/download');
assert.equal(req.method, 'GET');
});

it('should forward error message on failure', async function() {
docWorkerStub.callsFake((_req: any, res: any) => res.status(500).send({error: 'internal error'}));
resp = await axios.get(`${homeUrl}/api/docs/sampledocid_16/tables/table1/data`, chimpy);
assert.equal(resp.status, 500);
assert.deepEqual(resp.data, {error: 'internal error'});
assert(docWorkerStub.calledOnce);
const req = docWorkerStub.getCall(0).args[0];
assert.equal(req.get('Authorization'), 'Bearer api_key_for_chimpy');
assert.equal(req.get('Content-Type'), 'application/json');
assert.equal(req.originalUrl, '/dw/foo/api/docs/sampledocid_16/tables/table1/data');
assert.equal(req.method, 'GET');
});

it('should notice aborted requests and cancel forwarded ones', async function() {
let requestReceived: Function;
let closeReceived: Function;
let requestDone: Function;
const checkIsClosed = sinon.spy();
const promiseForRequestReceived = new Promise(r => { requestReceived = r; });
const promiseForCloseReceived = new Promise(r => { closeReceived = r; });
const promiseForRequestDone = new Promise(r => { requestDone = r; });
docWorkerStub.callsFake(async (req: any, res: any) => {
req.on('close', closeReceived);
requestReceived();
await Promise.race([promiseForCloseReceived, delay(100)]);
checkIsClosed(req.closed || req.aborted);
res.status(200).json('fig tree?');
requestDone();
});
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
const response = axios.get(`${homeUrl}/api/docs/sampledocid_16/tables/table1/data`,
{...chimpy, cancelToken: source.token});
await promiseForRequestReceived;
source.cancel('cancelled for testing');
await assert.isRejected(response, /cancelled for testing/);
await promiseForRequestDone;
sinon.assert.calledOnce(checkIsClosed);
assert.deepEqual(checkIsClosed.args, [[true]]);
});
});
Loading
Loading