Skip to content

Commit

Permalink
test: add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
paulpascal committed May 18, 2024
1 parent 5ec527d commit 00825e3
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 17 deletions.
81 changes: 75 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@xmldom/xmldom": "^0.8.10",
"async-retry": "^1.3.3",
"canonical-json": "0.0.4",
"cross-fetch": "^4.0.0",
"csv-parse": "^4.16.0",
"dom-compare": "^0.6.0",
"eslint": "^6.8.0",
Expand Down
4 changes: 2 additions & 2 deletions src/lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const url = require('url');
const cache = new Map();

// Helper function to create request headers with session token (if available)
const withCookieSession = (...args) => {
const withSessionCookie = (...args) => {
const options = typeof args[0] === 'object' ? Object.assign({}, args[0]) : { url: args[0] };

if (args.length > 1) {
Expand All @@ -26,7 +26,7 @@ const withCookieSession = (...args) => {
};

const _request = (method) => (...args) => {
const requestOptions = withCookieSession(...args);
const requestOptions = withSessionCookie(...args);
return retry(() => rpn[method](requestOptions), { retries: 5, randomize: false, factor: 1.5 });
};

Expand Down
37 changes: 28 additions & 9 deletions src/lib/db.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,44 @@
const { Headers } = require('cross-fetch');
const PouchDB = require('pouchdb-core');
PouchDB.plugin(require('pouchdb-adapter-http'));
PouchDB.plugin(require('pouchdb-mapreduce'));

const ArchivingDB = require('./archiving-db');
const environment = require('./environment');

const sessionCookieAwareFetch = () => (url, opts = {}) => {
const sessionToken = environment.sessionToken;

if (sessionToken) {
const setHeader = (headers, name, value) => {
if (headers instanceof Headers) {
headers.set(name, value);
} else if (Array.isArray(headers)) {
headers.push([name, value]);
} else if (typeof headers === 'object') {
headers[name] = value;
}
};

// Ensure opts.headers exists
if (!opts.headers) {
opts.headers = new Headers();
}

// Set the 'Cookie' header
setHeader(opts.headers, 'Cookie', sessionToken);
}

return PouchDB.fetch(url, opts);
};

module.exports = () => {
if (environment.isArchiveMode) {
return new ArchivingDB(environment.archiveDestination);
}

return new PouchDB(environment.apiUrl, {
ajax: { timeout: 60000 },
fetch: (url, opts) => {
const sessionToken = environment.sessionToken;
if (sessionToken) {
opts.headers.set('Cookie', sessionToken);
opts.credentials = 'include';
}
return PouchDB.fetch(url, opts);
},
fetch: sessionCookieAwareFetch(),
});
};

51 changes: 51 additions & 0 deletions test/lib/api.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,55 @@ describe('api', () => {
}
});
});

describe('with session cookie', function () {
let retrySub, rpnStub;
const sessionToken = 'sessionTokenValue';

beforeEach(() => {
sinon.stub(environment, 'isArchiveMode').get(() => false);
sinon.stub(environment, 'sessionToken').get(() => sessionToken);
sinon.stub(environment, 'apiUrl').get(() => 'http://example.com/db-name');

retrySub = sinon.stub().callsFake(async (fn) => fn());
rpnStub = {
get: sinon.stub().resolves(),
post: sinon.stub().resolves(),
put: sinon.stub().resolves(),
};
api.__set__('retry', retrySub);
api.__set__('rpn', rpnStub);
});

afterEach(() => {
sinon.restore();
});

it('should call request with available session token in headers as cookie', async () => {
await api().version();
expect(rpnStub.get.callCount).to.eq(1);
const requestOptions = rpnStub.get.getCall(0).args[0];
expect(requestOptions.headers.Cookie).to.eq(sessionToken);
});

it('should not include cookie in request headers if session token not available', async () => {
sinon.stub(environment, 'sessionToken').get(() => undefined);

await api().updateAppSettings(JSON.stringify({
transitions: [ 'test' ]
}));

// 1- logDeprecatedTransitions
// some request does not have (headers) => it should then be present final request options
expect(rpnStub.get.callCount).to.eq(1);
const requestOptions1 = rpnStub.get.getCall(0).args[0];
expect(requestOptions1.headers).to.be.undefined;

// 2- updateAppSettings
// those that have (headers) => should have it present but missing Cookie in the final request options
expect(rpnStub.put.callCount).to.eq(1);
const requestOptions2 = rpnStub.put.getCall(0).args[0];
expect(requestOptions2.headers.Cookie).to.be.undefined;
});
});
});
52 changes: 52 additions & 0 deletions test/lib/db.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { expect } = require('chai');
const { Headers } = require('cross-fetch');
const sinon = require('sinon');
const rewire = require('rewire');

const db = rewire('../../src/lib/db');
const environment = require('../../src/lib/environment');

describe('PouchDB', () => {
describe('authentication with session cookie', () => {
let sessionCookieAwareFetch;
const sessionToken = 'sessionTokenValue';

beforeEach(() => {
sinon.stub(environment, 'isArchiveMode').get(() => false);
sinon.stub(environment, 'sessionToken').get(() => sessionToken);
sinon.stub(environment, 'apiUrl').get(() => 'http://example.com/db-name');

sessionCookieAwareFetch = db.__get__('sessionCookieAwareFetch');
});

afterEach(() => {
sinon.restore();
});

it('should include session token in PouchDB fetch headers(Headers) if available', async () => {
const fetch = sessionCookieAwareFetch();
const options = { headers: new Headers() };

await fetch('http://example.com/db-name', options);
expect(options.headers.get('Cookie')).to.contains(sessionToken);
});

it('should include session token in PouchDB fetch headers(object) if available', async () => {
const fetch = sessionCookieAwareFetch();
const options = { headers: {} };

await fetch('http://example.com/db-name', options);
expect(options.headers.Cookie).to.contains(sessionToken);
});

it('should not include session token in PouchDB fetch headers if not available', async () => {
sinon.stub(environment, 'sessionToken').get(() => undefined);

const fetch = sessionCookieAwareFetch();
const options = { headers: new Headers() };

await fetch('http://example.com/db-name', options);
expect(options.headers.get('Cookie')).to.be.empty;
});
});
});

0 comments on commit 00825e3

Please sign in to comment.