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