feathers-adapter-vitest 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/syntax.js ADDED
@@ -0,0 +1,354 @@
1
+ import assert from 'node:assert';
2
+ import { describe, beforeEach, afterEach } from 'vitest';
3
+ export default (test, app, _errors, serviceName, idProp) => {
4
+ describe('Query Syntax', () => {
5
+ let bob;
6
+ let alice;
7
+ let doug;
8
+ let service;
9
+ beforeEach(async () => {
10
+ service = app.service(serviceName);
11
+ bob = await app.service(serviceName).create({
12
+ name: 'Bob',
13
+ age: 25,
14
+ });
15
+ doug = await app.service(serviceName).create({
16
+ name: 'Doug',
17
+ age: 32,
18
+ });
19
+ alice = await app.service(serviceName).create({
20
+ name: 'Alice',
21
+ age: 19,
22
+ });
23
+ });
24
+ afterEach(async () => {
25
+ await service.remove(bob[idProp]);
26
+ await service.remove(alice[idProp]);
27
+ await service.remove(doug[idProp]);
28
+ });
29
+ test('.find + equal', async () => {
30
+ const params = { query: { name: 'Alice' } };
31
+ const data = await service.find(params);
32
+ assert.ok(Array.isArray(data));
33
+ assert.strictEqual(data.length, 1);
34
+ assert.strictEqual(data[0].name, 'Alice');
35
+ });
36
+ test('.find + equal multiple', async () => {
37
+ const data = await service.find({
38
+ query: { name: 'Alice', age: 20 },
39
+ });
40
+ assert.strictEqual(data.length, 0);
41
+ });
42
+ describe('special filters', () => {
43
+ test('.find + $sort', async () => {
44
+ let data = await service.find({
45
+ query: {
46
+ $sort: { name: 1 },
47
+ },
48
+ });
49
+ assert.strictEqual(data.length, 3);
50
+ assert.strictEqual(data[0].name, 'Alice');
51
+ assert.strictEqual(data[1].name, 'Bob');
52
+ assert.strictEqual(data[2].name, 'Doug');
53
+ data = await service.find({
54
+ query: {
55
+ $sort: { name: -1 },
56
+ },
57
+ });
58
+ assert.strictEqual(data.length, 3);
59
+ assert.strictEqual(data[0].name, 'Doug');
60
+ assert.strictEqual(data[1].name, 'Bob');
61
+ assert.strictEqual(data[2].name, 'Alice');
62
+ });
63
+ test('.find + $sort + string', async () => {
64
+ const data = await service.find({
65
+ query: {
66
+ $sort: { name: '1' },
67
+ },
68
+ });
69
+ assert.strictEqual(data.length, 3);
70
+ assert.strictEqual(data[0].name, 'Alice');
71
+ assert.strictEqual(data[1].name, 'Bob');
72
+ assert.strictEqual(data[2].name, 'Doug');
73
+ });
74
+ test('.find + $limit', async () => {
75
+ const data = await service.find({
76
+ query: {
77
+ $limit: 2,
78
+ },
79
+ });
80
+ assert.strictEqual(data.length, 2);
81
+ });
82
+ test('.find + $limit 0', async () => {
83
+ const data = await service.find({
84
+ query: {
85
+ $limit: 0,
86
+ },
87
+ });
88
+ assert.strictEqual(data.length, 0);
89
+ });
90
+ test('.find + $skip', async () => {
91
+ const data = await service.find({
92
+ query: {
93
+ $sort: { name: 1 },
94
+ $skip: 1,
95
+ },
96
+ });
97
+ assert.strictEqual(data.length, 2);
98
+ assert.strictEqual(data[0].name, 'Bob');
99
+ assert.strictEqual(data[1].name, 'Doug');
100
+ });
101
+ test('.find + $select', async () => {
102
+ const data = await service.find({
103
+ query: {
104
+ name: 'Alice',
105
+ $select: ['name'],
106
+ },
107
+ });
108
+ assert.strictEqual(data.length, 1);
109
+ assert.ok(idProp in data[0], 'data has id');
110
+ assert.strictEqual(data[0].name, 'Alice');
111
+ assert.strictEqual(data[0].age, undefined);
112
+ });
113
+ test('.find + $or', async () => {
114
+ const data = await service.find({
115
+ query: {
116
+ $or: [{ name: 'Alice' }, { name: 'Bob' }],
117
+ $sort: { name: 1 },
118
+ },
119
+ });
120
+ assert.strictEqual(data.length, 2);
121
+ assert.strictEqual(data[0].name, 'Alice');
122
+ assert.strictEqual(data[1].name, 'Bob');
123
+ });
124
+ test('.find + $in', async () => {
125
+ const data = await service.find({
126
+ query: {
127
+ name: {
128
+ $in: ['Alice', 'Bob'],
129
+ },
130
+ $sort: { name: 1 },
131
+ },
132
+ });
133
+ assert.strictEqual(data.length, 2);
134
+ assert.strictEqual(data[0].name, 'Alice');
135
+ assert.strictEqual(data[1].name, 'Bob');
136
+ });
137
+ test('.find + $nin', async () => {
138
+ const data = await service.find({
139
+ query: {
140
+ name: {
141
+ $nin: ['Alice', 'Bob'],
142
+ },
143
+ },
144
+ });
145
+ assert.strictEqual(data.length, 1);
146
+ assert.strictEqual(data[0].name, 'Doug');
147
+ });
148
+ test('.find + $lt', async () => {
149
+ const data = await service.find({
150
+ query: {
151
+ age: {
152
+ $lt: 30,
153
+ },
154
+ },
155
+ });
156
+ assert.strictEqual(data.length, 2);
157
+ });
158
+ test('.find + $lte', async () => {
159
+ const data = await service.find({
160
+ query: {
161
+ age: {
162
+ $lte: 25,
163
+ },
164
+ },
165
+ });
166
+ assert.strictEqual(data.length, 2);
167
+ });
168
+ test('.find + $gt', async () => {
169
+ const data = await service.find({
170
+ query: {
171
+ age: {
172
+ $gt: 30,
173
+ },
174
+ },
175
+ });
176
+ assert.strictEqual(data.length, 1);
177
+ });
178
+ test('.find + $gte', async () => {
179
+ const data = await service.find({
180
+ query: {
181
+ age: {
182
+ $gte: 25,
183
+ },
184
+ },
185
+ });
186
+ assert.strictEqual(data.length, 2);
187
+ });
188
+ test('.find + $ne', async () => {
189
+ const data = await service.find({
190
+ query: {
191
+ age: {
192
+ $ne: 25,
193
+ },
194
+ },
195
+ });
196
+ assert.strictEqual(data.length, 2);
197
+ });
198
+ });
199
+ test('.find + $gt + $lt + $sort', async () => {
200
+ const params = {
201
+ query: {
202
+ age: {
203
+ $gt: 18,
204
+ $lt: 30,
205
+ },
206
+ $sort: { name: 1 },
207
+ },
208
+ };
209
+ const data = await service.find(params);
210
+ assert.strictEqual(data.length, 2);
211
+ assert.strictEqual(data[0].name, 'Alice');
212
+ assert.strictEqual(data[1].name, 'Bob');
213
+ });
214
+ test('.find + $or nested + $sort', async () => {
215
+ const params = {
216
+ query: {
217
+ $or: [
218
+ { name: 'Doug' },
219
+ {
220
+ age: {
221
+ $gte: 18,
222
+ $lt: 25,
223
+ },
224
+ },
225
+ ],
226
+ $sort: { name: 1 },
227
+ },
228
+ };
229
+ const data = await service.find(params);
230
+ assert.strictEqual(data.length, 2);
231
+ assert.strictEqual(data[0].name, 'Alice');
232
+ assert.strictEqual(data[1].name, 'Doug');
233
+ });
234
+ test('.find + $and', async () => {
235
+ const params = {
236
+ query: {
237
+ $and: [{ age: 19 }],
238
+ $sort: { name: 1 },
239
+ },
240
+ };
241
+ const data = await service.find(params);
242
+ assert.strictEqual(data.length, 1);
243
+ assert.strictEqual(data[0].name, 'Alice');
244
+ });
245
+ test('.find + $and + $or', async () => {
246
+ const params = {
247
+ query: {
248
+ $and: [{ $or: [{ name: 'Alice' }] }],
249
+ $sort: { name: 1 },
250
+ },
251
+ };
252
+ const data = await service.find(params);
253
+ assert.strictEqual(data.length, 1);
254
+ assert.strictEqual(data[0].name, 'Alice');
255
+ });
256
+ describe('params.adapter', () => {
257
+ test('params.adapter + paginate', async () => {
258
+ const page = await service.find({
259
+ adapter: {
260
+ paginate: { default: 3 },
261
+ },
262
+ });
263
+ assert.strictEqual(page.limit, 3);
264
+ assert.strictEqual(page.skip, 0);
265
+ });
266
+ test('params.adapter + multi', async () => {
267
+ const items = [
268
+ {
269
+ name: 'Garald',
270
+ age: 200,
271
+ },
272
+ {
273
+ name: 'Harald',
274
+ age: 24,
275
+ },
276
+ ];
277
+ const multiParams = {
278
+ adapter: {
279
+ multi: ['create'],
280
+ },
281
+ };
282
+ const users = await service.create(items, multiParams);
283
+ assert.strictEqual(users.length, 2);
284
+ await service.remove(users[0][idProp]);
285
+ await service.remove(users[1][idProp]);
286
+ await assert.rejects(() => service.patch(null, { age: 2 }, multiParams), {
287
+ message: 'Can not patch multiple entries',
288
+ });
289
+ });
290
+ });
291
+ describe('paginate', function () {
292
+ beforeEach(() => {
293
+ service.options.paginate = {
294
+ default: 1,
295
+ max: 2,
296
+ };
297
+ });
298
+ afterEach(() => {
299
+ service.options.paginate = {};
300
+ });
301
+ test('.find + paginate', async () => {
302
+ const page = await service.find({
303
+ query: { $sort: { name: -1 } },
304
+ });
305
+ assert.strictEqual(page.total, 3);
306
+ assert.strictEqual(page.limit, 1);
307
+ assert.strictEqual(page.skip, 0);
308
+ assert.strictEqual(page.data[0].name, 'Doug');
309
+ });
310
+ test('.find + paginate + query', async () => {
311
+ const page = await service.find({
312
+ query: {
313
+ $sort: { name: -1 },
314
+ name: 'Doug',
315
+ },
316
+ });
317
+ assert.strictEqual(page.total, 1);
318
+ assert.strictEqual(page.limit, 1);
319
+ assert.strictEqual(page.skip, 0);
320
+ assert.strictEqual(page.data[0].name, 'Doug');
321
+ });
322
+ test('.find + paginate + $limit + $skip', async () => {
323
+ const params = {
324
+ query: {
325
+ $skip: 1,
326
+ $limit: 4,
327
+ $sort: { name: -1 },
328
+ },
329
+ };
330
+ const page = await service.find(params);
331
+ assert.strictEqual(page.total, 3);
332
+ assert.strictEqual(page.limit, 2);
333
+ assert.strictEqual(page.skip, 1);
334
+ assert.strictEqual(page.data[0].name, 'Bob');
335
+ assert.strictEqual(page.data[1].name, 'Alice');
336
+ });
337
+ test('.find + paginate + $limit 0', async () => {
338
+ const page = await service.find({
339
+ query: { $limit: 0 },
340
+ });
341
+ assert.strictEqual(page.total, 3);
342
+ assert.strictEqual(page.data.length, 0);
343
+ });
344
+ test('.find + paginate + params', async () => {
345
+ const page = await service.find({ paginate: { default: 3 } });
346
+ assert.strictEqual(page.limit, 3);
347
+ assert.strictEqual(page.skip, 0);
348
+ const results = await service.find({ paginate: false });
349
+ assert.ok(Array.isArray(results));
350
+ assert.strictEqual(results.length, 3);
351
+ });
352
+ });
353
+ });
354
+ };
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "feathers-adapter-vitest",
3
+ "version": "0.0.2",
4
+ "description": "Feathers shared database adapter test suite with vitest",
5
+ "homepage": "https://feathersjs.com",
6
+ "keywords": [
7
+ "feathers"
8
+ ],
9
+ "license": "MIT",
10
+ "author": "Feathers Community",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git://github.com/feathers-community/feathers-adapter-vitest.git"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/feathers-community/feathers-adapter-vitest/issues"
17
+ },
18
+ "type": "module",
19
+ "funding": {
20
+ "type": "opencollective",
21
+ "url": "https://opencollective.com/feathers"
22
+ },
23
+ "engines": {
24
+ "node": ">= 22"
25
+ },
26
+ "main": "dist/index.js",
27
+ "types": "dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "import": "./dist/index.js",
31
+ "types": "./dist/index.d.ts"
32
+ }
33
+ },
34
+ "files": [
35
+ "CHANGELOG.md",
36
+ "LICENSE",
37
+ "README.md",
38
+ "src/**",
39
+ "dist/**"
40
+ ],
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "dependencies": {
45
+ "@feathersjs/feathers": "^5.0.35"
46
+ },
47
+ "devDependencies": {
48
+ "@feathers-community/eslint-config": "^0.0.8",
49
+ "@tsconfig/node22": "^22.0.2",
50
+ "@types/node": "^24.3.1",
51
+ "eslint": "^9.35.0",
52
+ "shx": "^0.4.0",
53
+ "typescript": "^5.9.2",
54
+ "vitest": "^3.2.4"
55
+ },
56
+ "peerDependencies": {
57
+ "vitest": "^3.0.0"
58
+ },
59
+ "scripts": {
60
+ "build": "tsc",
61
+ "lint": "eslint",
62
+ "test": "vitest run"
63
+ }
64
+ }
package/src/basic.ts ADDED
@@ -0,0 +1,68 @@
1
+ import assert from 'node:assert'
2
+ import { describe, beforeEach, it } from 'vitest'
3
+ import type { AdapterBasicTest } from './declarations.js'
4
+ import type { Application } from '@feathersjs/feathers'
5
+
6
+ type BasicTestOptions = {
7
+ app: Application
8
+ test: AdapterBasicTest
9
+ serviceName: string
10
+ idProp: string
11
+ }
12
+
13
+ export default (options: BasicTestOptions) => {
14
+ const { test, app, serviceName, idProp } = options
15
+
16
+ describe('Basic Functionality', () => {
17
+ let service: any
18
+
19
+ beforeEach(() => {
20
+ service = app.service(serviceName)
21
+ })
22
+
23
+ it('.id', () => {
24
+ assert.strictEqual(
25
+ service.id,
26
+ idProp,
27
+ 'id property is set to expected name',
28
+ )
29
+ })
30
+
31
+ test('.options', () => {
32
+ assert.ok(service.options, 'Options are available in service.options')
33
+ })
34
+
35
+ test('.events', () => {
36
+ assert.ok(
37
+ service.events.includes('testing'),
38
+ 'service.events is set and includes "testing"',
39
+ )
40
+ })
41
+
42
+ describe('Raw Methods', () => {
43
+ test('._get', () => {
44
+ assert.strictEqual(typeof service._get, 'function')
45
+ })
46
+
47
+ test('._find', () => {
48
+ assert.strictEqual(typeof service._find, 'function')
49
+ })
50
+
51
+ test('._create', () => {
52
+ assert.strictEqual(typeof service._create, 'function')
53
+ })
54
+
55
+ test('._update', () => {
56
+ assert.strictEqual(typeof service._update, 'function')
57
+ })
58
+
59
+ test('._patch', () => {
60
+ assert.strictEqual(typeof service._patch, 'function')
61
+ })
62
+
63
+ test('._remove', () => {
64
+ assert.strictEqual(typeof service._remove, 'function')
65
+ })
66
+ })
67
+ })
68
+ }
@@ -0,0 +1,104 @@
1
+ export type AdapterTest = (name: AdapterTestName, runner: any) => void
2
+
3
+ export type AdapterBasicTest = (name: AdapterBasicTestName, runner: any) => void
4
+ export type AdapterMethodsTest = (
5
+ name: AdapterMethodsTestName,
6
+ runner: any,
7
+ ) => void
8
+ export type AdapterSyntaxTest = (
9
+ name: AdapterSyntaxTestName,
10
+ runner: any,
11
+ ) => void
12
+
13
+ export type AdapterTestName =
14
+ | AdapterBasicTestName
15
+ | AdapterMethodsTestName
16
+ | AdapterSyntaxTestName
17
+
18
+ export type AdapterTestMap = Record<AdapterTestName, boolean>
19
+
20
+ export type AdapterBasicTestName =
21
+ | '.id'
22
+ | '.options'
23
+ | '.events'
24
+ | '._get'
25
+ | '._find'
26
+ | '._create'
27
+ | '._update'
28
+ | '._patch'
29
+ | '._remove'
30
+ | '.$get'
31
+ | '.$find'
32
+ | '.$create'
33
+ | '.$update'
34
+ | '.$patch'
35
+ | '.$remove'
36
+
37
+ export type AdapterMethodsTestName =
38
+ | '.get'
39
+ | '.get + $select'
40
+ | '.get + id + query'
41
+ | '.get + NotFound'
42
+ | '.get + id + query id'
43
+ | '.find'
44
+ | '.remove'
45
+ | '.remove + $select'
46
+ | '.remove + id + query'
47
+ | '.remove + multi'
48
+ | '.remove + multi no pagination'
49
+ | '.remove + id + query id'
50
+ | '.update'
51
+ | '.update + $select'
52
+ | '.update + id + query'
53
+ | '.update + NotFound'
54
+ | '.update + query + NotFound'
55
+ | '.update + id + query id'
56
+ | '.patch'
57
+ | '.patch + $select'
58
+ | '.patch + id + query'
59
+ | '.patch multiple'
60
+ | '.patch multiple no pagination'
61
+ | '.patch multi query same'
62
+ | '.patch multi query changed'
63
+ | '.patch + NotFound'
64
+ | '.patch + query + NotFound'
65
+ | '.patch + id + query id'
66
+ | '.create'
67
+ | '.create + $select'
68
+ | '.create multi'
69
+ | '.create ignores query'
70
+ | 'internal .find'
71
+ | 'internal .get'
72
+ | 'internal .create'
73
+ | 'internal .update'
74
+ | 'internal .patch'
75
+ | 'internal .remove'
76
+
77
+ export type AdapterSyntaxTestName =
78
+ | '.find + equal'
79
+ | '.find + equal multiple'
80
+ | '.find + $sort'
81
+ | '.find + $sort + string'
82
+ | '.find + $limit'
83
+ | '.find + $limit 0'
84
+ | '.find + $skip'
85
+ | '.find + $select'
86
+ | '.find + $or'
87
+ | '.find + $in'
88
+ | '.find + $nin'
89
+ | '.find + $lt'
90
+ | '.find + $lte'
91
+ | '.find + $gt'
92
+ | '.find + $gte'
93
+ | '.find + $ne'
94
+ | '.find + $gt + $lt + $sort'
95
+ | '.find + $or nested + $sort'
96
+ | '.find + $and'
97
+ | '.find + $and + $or'
98
+ | 'params.adapter + paginate'
99
+ | 'params.adapter + multi'
100
+ | '.find + paginate'
101
+ | '.find + paginate + query'
102
+ | '.find + paginate + $limit + $skip'
103
+ | '.find + paginate + $limit 0'
104
+ | '.find + paginate + params'
package/src/index.ts ADDED
@@ -0,0 +1,54 @@
1
+ import type { Application } from '@feathersjs/feathers'
2
+ import basicTests from './basic.js'
3
+ import type { AdapterTestMap, AdapterTestName } from './declarations.js'
4
+ import methodTests from './methods.js'
5
+ import syntaxTests from './syntax.js'
6
+ import { describe, it, afterAll } from 'vitest'
7
+
8
+ export type TestSuiteOptions = {
9
+ app: Application
10
+ serviceName: string
11
+ /**
12
+ * @default 'id'
13
+ */
14
+ idProp?: string
15
+ }
16
+
17
+ export const defineTestSuite = (testMap?: AdapterTestMap) => {
18
+ return (options: TestSuiteOptions) => {
19
+ const { app, serviceName, idProp = 'id' } = options
20
+
21
+ const skippedTests: AdapterTestName[] = []
22
+ const allTests: AdapterTestName[] = []
23
+
24
+ const test = (name: AdapterTestName, runner: any) => {
25
+ const skip = testMap ? !testMap[name] : false
26
+ const its = skip ? it.skip : it
27
+
28
+ if (skip) {
29
+ skippedTests.push(name)
30
+ }
31
+
32
+ allTests.push(name)
33
+
34
+ its(name, runner)
35
+ }
36
+
37
+ describe(`Adapter tests for '${serviceName}' service with '${idProp}' id property`, () => {
38
+ afterAll(() => {
39
+ if (skippedTests.length) {
40
+ console.log(
41
+ `\nSkipped the following ${skippedTests.length} Feathers adapter test(s) out of ${allTests.length} total:`,
42
+ )
43
+ console.log(JSON.stringify(skippedTests, null, ' '))
44
+ }
45
+ })
46
+
47
+ basicTests({ test, app, serviceName, idProp })
48
+ methodTests({ test, app, serviceName, idProp })
49
+ syntaxTests({ test, app, serviceName, idProp })
50
+ })
51
+ }
52
+ }
53
+
54
+ export * from './declarations.js'