X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=test%2Fsrc%2Fdb%2Fpostgres.js;h=da071f2ab28566b53b73ba3ff25858c7e96e0355;hb=f793d88a96b9495172f5dd0c342f2036c902902d;hp=5aff1e8cceafae1e8faffd3629105cf59a1955ca;hpb=4f64b8910e1295207a42c757cb81c9b0e9ee3be2;p=websub-hub diff --git a/test/src/db/postgres.js b/test/src/db/postgres.js index 5aff1e8..da071f2 100644 --- a/test/src/db/postgres.js +++ b/test/src/db/postgres.js @@ -70,6 +70,13 @@ describe('DatabasePostgres', function () { sinon.restore(); }); + it('covers listener', function () { + const listenerOptions = new Config('test'); + listenerOptions.db.cacheEnabled = true; + const listenerDb = new DB(stubLogger, listenerOptions, pgpStub); + assert(listenerDb); + }); + // Ensure all interface methods are implemented describe('Implementation', function () { it('implements interface', async function () { @@ -104,6 +111,11 @@ describe('DatabasePostgres', function () { db.pgpInitOptions.query(event); assert(db.logger.debug.called); }); + it('covers NOTIFY', function () { + const event = { query: 'NOTIFY thing' }; + db.pgpInitOptions.query(event); + assert(!db.logger.debug.called); + }); }); // query describe('receive', function () { it('covers', function () { @@ -133,6 +145,35 @@ describe('DatabasePostgres', function () { assert(db.logger.debug.called); assert.deepStrictEqual(data, expectedData); }); + it('covers NOTIFY', function () { + const data = [ + { + column_one: 'one', // eslint-disable-line camelcase + column_two: 2, // eslint-disable-line camelcase + }, + { + column_one: 'foo', // eslint-disable-line camelcase + column_two: 4, // eslint-disable-line camelcase + }, + ]; + const result = { + command: 'NOTIFY', + }; + const event = {}; + const expectedData = [ + { + columnOne: 'one', + columnTwo: 2, + }, + { + columnOne: 'foo', + columnTwo: 4, + }, + ]; + db.pgpInitOptions.receive(data, result, event) + assert(!db.logger.debug.called); + assert.deepStrictEqual(data, expectedData); + }); }); // receive }); // pgpInitOptions @@ -156,6 +197,9 @@ describe('DatabasePostgres', function () { }); // _initTables describe('initialize', function () { + after(function () { + delete db.listener; + }); it('passes supported version', async function () { const version = { major: 1, minor: 0, patch: 0 }; sinon.stub(db.db, 'one').resolves(version); @@ -183,11 +227,33 @@ describe('DatabasePostgres', function () { }); it('covers migration', async function() { sinon.stub(db.db, 'oneOrNone').resolves({}); - sinon.stub(db.db, 'multiResult'); - sinon.stub(db, '_currentSchema').resolves(db.schemaVersionsSupported.max); + sinon.stub(db.db, 'multiResult').resolves({}); + sinon.stub(db, '_currentSchema').resolves(db.schemaVersionsSupported.min); sinon.stub(db.db, 'one').resolves(db.schemaVersionsSupported.max); await db.initialize(); }); + it('covers migration failure', async function() { + const expected = new Error('oh no'); + sinon.stub(db.db, 'oneOrNone').resolves({}); + sinon.stub(db.db, 'multiResult').rejects(expected); + sinon.stub(db, '_currentSchema').resolves(db.schemaVersionsSupported.min); + sinon.stub(db.db, 'one').resolves(db.schemaVersionsSupported.max); + try { + await db.initialize(); + assert.fail(noExpectedException); + } catch (e) { + assert.deepStrictEqual(e, expected); + } + }); + it('covers listener', async function() { + db.listener = { + start: sinon.stub(), + }; + const version = { major: 1, minor: 0, patch: 0 }; + sinon.stub(db.db, 'one').resolves(version); + await db.initialize(false); + assert(db.listener.start.called); + }); }); // initialize describe('healthCheck', function () { @@ -228,6 +294,9 @@ describe('DatabasePostgres', function () { }); // _queryFileHelper describe('_closeConnection', function () { + after(function () { + delete db.listener; + }); it('success', async function () { sinon.stub(db._pgp, 'end'); await db._closeConnection(); @@ -243,6 +312,14 @@ describe('DatabasePostgres', function () { assert.deepStrictEqual(e, expected); } }); + it('covers listener', async function () { + db.listener = { + stop: sinon.stub(), + }; + sinon.stub(db._pgp, 'end'); + await db._closeConnection(); + assert(db._pgp.end.called); + }); }); // _closeConnection describe('_purgeTables', function () { @@ -268,6 +345,84 @@ describe('DatabasePostgres', function () { }); }); // _purgeTables + describe('_topicChanged', function () { + beforeEach(function () { + db.cache = new Map(); + sinon.stub(db.cache, 'delete'); + }); + after(function () { + delete db.cache; + }); + it('covers', function () { + db._topicChanged('topic-id'); + assert(db.cache.delete.called); + }); + it('ignores ping', function () { + db._topicChanged('ping'); + assert(!db.cache.delete.called); + }); + }); // _topicChanged + + describe('_listenerEstablished', function () { + it('creates cache', function () { + delete db.cache; + db._listenerEstablished(); + assert(db.cache instanceof Map); + }); + }); // _listenerEstablished + + describe('_listenerLost', function () { + it('removes cache', function () { + db.cache = new Map(); + db._listenerLost(); + assert(!db.cache); + }); + }); // _listenerLost + + describe('_cacheGet', function () { + let key; + beforeEach(function () { + key = 'key'; + }); + it('nothing if no cache', function () { + delete db.cache; + const result = db._cacheGet(key); + assert.strictEqual(result, undefined); + }); + it('nothing if no entry', function () { + db.cache = new Map(); + const result = db._cacheGet(key); + assert.strictEqual(result, undefined); + }); + it('returns cached entry', function () { + db.cache = new Map(); + const expected = { + foo: 'bar', + }; + db._cacheSet(key, expected); + const result = db._cacheGet(key); + assert.deepStrictEqual(result, expected); + }); + }); // _cacheGet + + describe('_cacheSet', function () { + let key; + beforeEach(function () { + key = 'key'; + }); + it('covers no cache', function () { + delete db.cache; + db._cacheSet(key, 'data'); + }); + it('covers cache', function () { + db.cache = new Map(); + const expected = 'blah'; + db._cacheSet(key, expected); + const result = db._cacheGet(key); + assert.deepStrictEqual(result, expected); + }); + }); // _cacheSet + describe('context', function () { it('covers', async function () { await db.context(common.nop); @@ -415,7 +570,7 @@ describe('DatabasePostgres', function () { changes: 1, lastInsertRowid: undefined, duration: 10, - } + }; sinon.stub(db.db, 'result').resolves(dbResult); const result = await db.subscriptionDelete(dbCtx, callback, topicId); assert.deepStrictEqual(result, expected); @@ -432,6 +587,34 @@ describe('DatabasePostgres', function () { }); }); // subscriptionDelete + describe('subscriptionDeleteExpired', function () { + it('success', async function () { + const dbResult = { + rowCount: 1, + rows: [], + duration: 10, + }; + const expected = { + changes: 1, + lastInsertRowid: undefined, + duration: 10, + }; + sinon.stub(db.db, 'result').resolves(dbResult); + const result = await db.subscriptionDeleteExpired(dbCtx, topicId); + assert.deepStrictEqual(result, expected); + }); + it('failure', async function() { + const expected = new Error(); + sinon.stub(db.db, 'result').rejects(expected); + try { + await db.subscriptionDeleteExpired(dbCtx, topicId); + assert.fail(noExpectedException); + } catch (e) { + assert.deepStrictEqual(e, expected); + } + }); + }); + describe('subscriptionDeliveryClaim', function () { it('success', async function() { const dbResult = [ @@ -489,12 +672,16 @@ describe('DatabasePostgres', function () { }); // subscriptionDeliveryClaimById describe('subscriptionDeliveryComplete', function () { + let topicContentUpdated; + before(function () { + topicContentUpdated = new Date(); + }); it('success', async function() { const dbResult = { rowCount: 1, }; sinon.stub(db.db, 'result').resolves(dbResult); - await db.subscriptionDeliveryComplete(dbCtx, callback, topicId); + await db.subscriptionDeliveryComplete(dbCtx, callback, topicId, topicContentUpdated); }); it('failure', async function () { const dbResult = { @@ -502,7 +689,7 @@ describe('DatabasePostgres', function () { }; sinon.stub(db.db, 'result').onCall(0).resolves(dbResult); try { - await db.subscriptionDeliveryComplete(dbCtx, callback, topicId); + await db.subscriptionDeliveryComplete(dbCtx, callback, topicId, topicContentUpdated); assert.fail(noExpectedException); } catch (e) { assert(e instanceof DBErrors.UnexpectedResult); @@ -517,7 +704,7 @@ describe('DatabasePostgres', function () { }; sinon.stub(db.db, 'result').onCall(0).resolves(dbResult0).onCall(1).resolves(dbResult1); try { - await db.subscriptionDeliveryComplete(dbCtx, callback, topicId); + await db.subscriptionDeliveryComplete(dbCtx, callback, topicId, topicContentUpdated); assert.fail(noExpectedException); } catch (e) { assert(e instanceof DBErrors.UnexpectedResult); @@ -1024,8 +1211,15 @@ describe('DatabasePostgres', function () { }); // topicGetByUrl describe('topicGetContentById', function () { + let topic; + beforeEach(function () { + delete db.cache; + topic = { + id: topicId, + }; + }); it('success', async function() { - const expected = { id: topicId }; + const expected = topic; sinon.stub(db.db, 'oneOrNone').resolves(expected); const result = await db.topicGetContentById(dbCtx, topicId); assert.deepStrictEqual(result, expected); @@ -1046,8 +1240,88 @@ describe('DatabasePostgres', function () { assert.deepStrictEqual(e, expected); } }); + it('caches success', async function () { + db.cache = new Map(); + const expected = topic; + sinon.stub(db.db, 'oneOrNone').resolves(expected); + const result = await db.topicGetContentById(dbCtx, topicId); + assert.deepStrictEqual(result, expected); + }); + it('covers cached entry', async function() { + let result; + db.cache = new Map(); + const expected = topic; + sinon.stub(db.db, 'oneOrNone').resolves(expected); + result = await db.topicGetContentById(dbCtx, topicId); + assert.deepStrictEqual(result, expected); + result = await db.topicGetContentById(dbCtx, topicId); + assert.deepStrictEqual(result, expected); + }); }); // topicGetContentById + describe('topicPendingDelete', function () { + beforeEach(function () { + sinon.stub(db.db, 'one'); + sinon.stub(db.db, 'result'); + }); + it('success', async function () { + db.db.one.onCall(0).resolves({ + id: topicId, + isDeleted: true, + }).onCall(1).resolves({ + count: 0, + }); + const dbResult = { + rowCount: 1, + rows: [], + duration: 10, + }; + db.db.result.resolves(dbResult); + await db.topicPendingDelete(dbCtx, topicId); + assert(db.db.result.called); + }); + it('does not delete non-deleted topic', async function () { + db.db.one.onCall(0).resolves({ + id: topicId, + isDeleted: false, + }).onCall(1).resolves({ + count: 0, + }); + await db.topicPendingDelete(dbCtx, topicId); + assert(!db.db.result.called); + }); + it('does not delete topic with active subscriptions', async function () { + db.db.one.onCall(0).resolves({ + id: topicId, + isDeleted: true, + }).onCall(1).resolves({ + count: 10, + }); + await db.topicPendingDelete(dbCtx, topicId); + assert(!db.db.result.called); + }); + it('covers no deletion', async function () { + db.db.one.onCall(0).resolves({ + id: topicId, + isDeleted: true, + }).onCall(1).resolves({ + count: 0, + }); + const dbResult = { + rowCount: 0, + rows: [], + duration: 10, + }; + db.db.result.resolves(dbResult); + try { + await db.topicPendingDelete(dbCtx, topicId); + assert.fail(noExpectedException); + } catch (e) { + assert(e instanceof DBErrors.UnexpectedResult); + } + }); + }); + describe('topicSet', function () { let data; beforeEach(function () { @@ -1118,6 +1392,7 @@ describe('DatabasePostgres', function () { contentType: 'text/plain', contentHash: 'abc123', }; + sinon.stub(db.db, 'result'); }); it('success', async function() { const dbResult = { @@ -1130,7 +1405,7 @@ describe('DatabasePostgres', function () { lastInsertRowid: undefined, duration: 10, }; - sinon.stub(db.db, 'result').resolves(dbResult); + db.db.result.resolves(dbResult); const result = await db.topicSetContent(dbCtx, data); assert.deepStrictEqual(result, expected); }); @@ -1140,7 +1415,28 @@ describe('DatabasePostgres', function () { rows: [], duration: 10, }; - sinon.stub(db.db, 'result').resolves(dbResult); + db.db.result.resolves(dbResult); + try { + await db.topicSetContent(dbCtx, data); + assert.fail(noExpectedException); + } catch (e) { + assert(e instanceof DBErrors.UnexpectedResult); + } + }); + it('failure 2', async function () { + const dbResultSuccess = { + rowCount: 1, + rows: [], + duration: 10, + }; + const dbResultFail = { + rowCount: 0, + rows: [], + duration: 10, + }; + db.db.result + .onCall(0).resolves(dbResultSuccess) + .onCall(1).resolves(dbResultFail); try { await db.topicSetContent(dbCtx, data); assert.fail(noExpectedException); @@ -1185,7 +1481,7 @@ describe('DatabasePostgres', function () { } }); - }); + }); // topicUpdate describe('verificationClaim', function () { it('success', async function() {