786365722b13880f698b4f46184b3d4f3749bda2
[websub-hub] / test / src / db / base.js
1 /* eslint-env mocha */
2 'use strict';
3
4 const assert = require('assert');
5 const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
6
7 const stubDB = require('../../stub-db');
8 const stubLogger = require('../../stub-logger');
9 const DB = require('../../../src/db/base');
10 const DBErrors = require('../../../src/db/errors');
11
12 describe('DatabaseBase', function () {
13 let db;
14 beforeEach(function () {
15 db = new DB(stubLogger);
16 });
17 afterEach(function () {
18 sinon.restore();
19 });
20
21 it('covers no options', function () {
22 db = new DB();
23 });
24
25 describe('Interface', function () {
26 it('covers abstract methods', async function () {
27 await Promise.all(stubDB._implementation.map(async (m) => {
28 try {
29 // eslint-disable-next-line security/detect-object-injection
30 await db[m]();
31 assert.fail(`${m}: did not catch NotImplemented exception`);
32 } catch (e) {
33 assert(e instanceof DBErrors.NotImplemented, `${m}: unexpected exception ${e.name}`);
34 }
35 }));
36 }); // covers abstract methods
37 it('covers private abstract methods', async function () {
38 [
39 '_engineInfo',
40 ].map((m) => {
41 try {
42 // eslint-disable-next-line security/detect-object-injection
43 db[m]();
44 } catch (e) {
45 assert(e instanceof DBErrors.NotImplemented, `${m}: unexpected exception ${e.name}`);
46 }
47 });
48 });
49 }); // Interface
50
51 describe('_camelfy', function () {
52 it('empty arg', function () {
53 const result = DB._camelfy();
54 assert.strictEqual(result, undefined);
55 });
56 it('no change', function () {
57 const str = 'camelCase';
58 const result = DB._camelfy(str);
59 assert.strictEqual(result, str);
60 });
61 it('does expected', function () {
62 const str = 'snake_case_thing';
63 const result = DB._camelfy(str);
64 assert.strictEqual(result, 'snakeCaseThing');
65 });
66 }); // _camelfy
67
68 describe('_ensureTypes', function () {
69 let object;
70 beforeEach(function () {
71 object = {
72 num: 123,
73 bignum: BigInt(456),
74 str: 'some words',
75 veryNull: null,
76 obj: {},
77 buf: Buffer.from('foop'),
78 };
79 });
80 it('succeeds', function () {
81 db._ensureTypes(object, ['num', 'bignum'], ['number']);
82 db._ensureTypes(object, ['str', 'veryNull'], ['string', 'null']);
83 db._ensureTypes(object, ['buf'], ['buffer']);
84 });
85 it('data failure', function () {
86 try {
87 db._ensureTypes(object, ['missingField'], ['string', 'null']);
88 assert.fail('validation should have failed');
89 } catch (e) {
90 assert(e instanceof DBErrors.DataValidation);
91 }
92 });
93 it('failure covers singular', function () {
94 try {
95 db._ensureTypes(object, ['missingField'], ['string']);
96 assert.fail('validation should have failed');
97 } catch (e) {
98 assert(e instanceof DBErrors.DataValidation);
99 }
100 });
101 it('parameter failure', function () {
102 try {
103 db._ensureTypes(object, ['missingField'], undefined);
104 assert.fail('validation should have failed');
105 } catch (e) {
106 assert(e instanceof DBErrors.DataValidation);
107 }
108 });
109 }); // _ensureTypes
110
111 describe('initialize', function () {
112 let currentSchema;
113 beforeEach(function () {
114 currentSchema = {
115 major: 1,
116 minor: 0,
117 patch: 0,
118 };
119 db.schemaVersionsSupported = {
120 min: { ...currentSchema },
121 max: { ...currentSchema },
122 };
123 sinon.stub(db, '_currentSchema').resolves(currentSchema);
124 });
125 it('covers success', async function () {
126 await db.initialize();
127 });
128 it('covers failure', async function() {
129 db.schemaVersionsSupported = {
130 min: {
131 major: 3,
132 minor: 2,
133 patch: 1,
134 },
135 max: {
136 major: 5,
137 minor: 0,
138 patch: 0,
139 },
140 };
141 try {
142 await db.initialize();
143 assert.fail('did not get expected exception');
144 } catch (e) {
145 assert(e instanceof DBErrors.MigrationNeeded);
146 }
147 });
148 }); // initialize
149
150 describe('_topicDefaults', function () {
151 let topic;
152 beforeEach(function () {
153 topic = {};
154 });
155 it('covers', function () {
156 db._topicDefaults(topic);
157 assert.strictEqual(topic.leaseSecondsPreferred, db.topicLeaseDefaults.leaseSecondsPreferred);
158 });
159 it('covers empty', function () {
160 db._topicDefaults();
161 });
162 }); // _topicDefaults
163
164 describe('_topicSetDataValidate', function () {
165 let data;
166 beforeEach(function () {
167 data = {
168 url: 'https://example.com/',
169
170 };
171 });
172 it('covers success', function () {
173 db._topicSetDataValidate(data);
174 });
175 it('covers invalid value', function () {
176 data.leaseSecondsPreferred = -100;
177 try {
178 db._topicSetDataValidate(data);
179 assert.fail('did not get expected exception');
180 } catch (e) {
181 assert(e instanceof DBErrors.DataValidation);
182 }
183 });
184 it('covers invalid range', function () {
185 data.leaseSecondsPreferred = 10000;
186 data.leaseSecondsMax = 1000;
187 try {
188 db._topicSetDataValidate(data);
189 assert.fail('did not get expected exception');
190 } catch (e) {
191 assert(e instanceof DBErrors.DataValidation);
192 }
193 });
194 }); // _topicSetDataValidation
195
196 describe('_topicSetContentDataValidate', function () {
197 it('covers', function () {
198 db._topicSetContentDataValidate({
199 content: Buffer.from('foo'),
200 contentHash: '123',
201 });
202 });
203 }); // _topicSetContentDataValidate
204
205 describe('_topicUpdateDataValidate', function () {
206 it('succeeds', function () {
207 db._topicUpdateDataValidate({
208 leaseSecondsPreferred: 123,
209 leaseSecondsMin: 100,
210 leaseSecondsMax: 1000,
211 publisherValidationUrl: 'https://example.com/pub/',
212 contentHashAlgorithm: 'sha256',
213 });
214 });
215 it('covers no url', function () {
216 db._topicUpdateDataValidate({
217 leaseSecondsPreferred: 123,
218 leaseSecondsMin: 100,
219 leaseSecondsMax: 1000,
220 contentHashAlgorithm: 'sha256',
221 });
222 });
223 it('rejects invalid url', function () {
224 try {
225 db._topicUpdateDataValidate({
226 leaseSecondsPreferred: 123,
227 leaseSecondsMin: 100,
228 leaseSecondsMax: 1000,
229 publisherValidationUrl: 'flarbl',
230 contentHashAlgorithm: 'sha256',
231 });
232 assert.fail('did not get expected exception');
233 } catch (e) {
234 assert(e instanceof DBErrors.DataValidation);
235 }
236 });
237 it('rejects invalid algorithm', function () {
238 try {
239 db._topicUpdateDataValidate({
240 leaseSecondsPreferred: 123,
241 leaseSecondsMin: 100,
242 leaseSecondsMax: 1000,
243 publisherValidationUrl: 'https://example.com/pub/',
244 contentHashAlgorithm: 'md6',
245 });
246 assert.fail('did not get expected exception');
247 } catch (e) {
248 assert(e instanceof DBErrors.DataValidation);
249 }
250 });
251 }); // _topicUpdateDataValidate
252
253 describe('_verificationDataValidate', function () {
254 it('covers', function () {
255 db._verificationDataValidate({
256 topicId: 'b9ede5aa-e595-11eb-b30f-0025905f714a',
257 callback: 'https://example.com/cb',
258 mode: 'subscribe',
259 leaseSeconds: 123,
260 isPublisherValidated: true,
261 });
262 });
263 }); // _verificationDataValidate
264
265 describe('_subscriptionUpsertDataValidate', function () {
266 it('covers', function () {
267 db._subscriptionUpsertDataValidate({
268 topicId: 'b9ede5aa-e595-11eb-b30f-0025905f714a',
269 callback: 'https://example.com/cb',
270 leaseSeconds: 123,
271 });
272 });
273 }); // _subscriptionUpsertDataValidate
274
275 describe('_subscriptionUpdateDataValidate', function () {
276 it('succeeds', function () {
277 db._subscriptionUpdateDataValidate({
278 signatureAlgorithm: 'sha256',
279 });
280 });
281 it('rejects invalid', function () {
282 try {
283 db._subscriptionUpdateDataValidate({
284 signatureAlgorithm: 'md5',
285 });
286 assert.fail('did not get expected exception');
287 } catch (e) {
288 assert(e instanceof DBErrors.DataValidation);
289 }
290 });
291 }); // _subscriptionUpdateDataValidate
292
293 describe('_verificationUpdateDataValidate', function () {
294 it('covers', function () {
295 db._verificationUpdateDataValidate({
296 verificationId: 'b9ede5aa-e595-11eb-b30f-0025905f714a',
297 mode: 'denied',
298 isPublisherValidated: true,
299 });
300 });
301 }); // _verificationUpdateDataValidate
302
303 }); // DatabaseBase