@byline/db-postgres 1.12.1 → 1.12.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/lib/test-bootstrap.d.ts +8 -0
- package/dist/lib/test-bootstrap.js +27 -0
- package/dist/lib/test-db.d.ts +39 -0
- package/dist/lib/test-db.js +106 -0
- package/dist/lib/test-helper.js +7 -8
- package/dist/modules/admin/tests/auth-integration.test.js +91 -73
- package/dist/modules/admin/tests/session-provider.test.js +77 -60
- package/dist/modules/storage/tests/storage-document-paths.test.js +14 -32
- package/dist/modules/storage/tests/storage-field-types.test.js +13 -14
- package/dist/modules/storage/tests/storage-flatten-reconstruct.test.js +22 -23
- package/dist/modules/storage/tests/storage-restore.test.js +10 -21
- package/dist/modules/storage/tests/storage-store-manifest.test.js +15 -16
- package/dist/modules/storage/tests/storage-versioning.test.js +8 -9
- package/package.json +15 -7
|
@@ -5,18 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
* Integration test for the "make current" / restore-version flow.
|
|
10
|
-
*
|
|
11
|
-
* Validates that the Postgres locale='all' round-trip
|
|
12
|
-
* (`getDocumentByVersion` → `createDocumentVersion`) preserves multi-locale
|
|
13
|
-
* content and stable block `_id`s — the core invariant
|
|
14
|
-
* `restoreDocumentVersion` in @byline/core depends on. Pure storage-layer:
|
|
15
|
-
* the lifecycle wrapper, auth, and hooks are covered by unit tests in
|
|
16
|
-
* `packages/core/src/services/document-lifecycle.test.node.ts`.
|
|
17
|
-
*/
|
|
18
|
-
import assert from 'node:assert';
|
|
19
|
-
import { after, before, describe, it } from 'node:test';
|
|
8
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
20
9
|
import { setupTestDB, teardownTestDB } from '../../../lib/test-helper.js';
|
|
21
10
|
let commandBuilders;
|
|
22
11
|
let queryBuilders;
|
|
@@ -45,7 +34,7 @@ const RestoreCollectionConfig = {
|
|
|
45
34
|
};
|
|
46
35
|
let testCollection = {};
|
|
47
36
|
describe('Document version restore — storage round-trip', () => {
|
|
48
|
-
|
|
37
|
+
beforeAll(async () => {
|
|
49
38
|
const testDB = setupTestDB([RestoreCollectionConfig]);
|
|
50
39
|
commandBuilders = testDB.commandBuilders;
|
|
51
40
|
queryBuilders = testDB.queryBuilders;
|
|
@@ -55,7 +44,7 @@ describe('Document version restore — storage round-trip', () => {
|
|
|
55
44
|
throw new Error('Failed to create test collection');
|
|
56
45
|
testCollection = { id: collection.id };
|
|
57
46
|
});
|
|
58
|
-
|
|
47
|
+
afterAll(async () => {
|
|
59
48
|
try {
|
|
60
49
|
await commandBuilders.collections.delete(testCollection.id);
|
|
61
50
|
}
|
|
@@ -96,7 +85,7 @@ describe('Document version restore — storage round-trip', () => {
|
|
|
96
85
|
});
|
|
97
86
|
const v1Sections = v1Read.fields.sections;
|
|
98
87
|
const v1ItemIds = v1Sections[0].sectionItem.map((item) => item._id);
|
|
99
|
-
|
|
88
|
+
expect(v1ItemIds.every((id) => typeof id === 'string' && id.length > 0), 'v1 items should have stable _ids').toBeTruthy();
|
|
100
89
|
// Mutate to v2 (different content)
|
|
101
90
|
const v2Data = {
|
|
102
91
|
sku: v1Data.sku,
|
|
@@ -134,7 +123,7 @@ describe('Document version restore — storage round-trip', () => {
|
|
|
134
123
|
locale: 'all',
|
|
135
124
|
status: 'draft',
|
|
136
125
|
});
|
|
137
|
-
|
|
126
|
+
expect(v3.document.event_type, 'event_type should be persisted as "restore"').toBe('restore');
|
|
138
127
|
// Reconstruct v3. Multi-locale fields, block _ids, and per-item content
|
|
139
128
|
// should match v1 — not v2.
|
|
140
129
|
const v3Read = await queryBuilders.documents.getDocumentByVersion({
|
|
@@ -142,16 +131,16 @@ describe('Document version restore — storage round-trip', () => {
|
|
|
142
131
|
locale: 'all',
|
|
143
132
|
});
|
|
144
133
|
const v3Fields = v3Read.fields;
|
|
145
|
-
|
|
134
|
+
expect(v3Fields.title, 'restored title should match v1 across all locales').toEqual(v1Data.title);
|
|
146
135
|
const v3Sections = v3Fields.sections;
|
|
147
136
|
const v3ItemIds = v3Sections[0].sectionItem.map((item) => item._id);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
137
|
+
expect(v3ItemIds, 'restored block items should keep the v1 _ids verbatim (identity preserved across restore)').toEqual(v1ItemIds);
|
|
138
|
+
expect(v3Sections[0].sectionItem.length, "restored version should have v1's two items, not v2's single item").toBe(2);
|
|
139
|
+
expect(v3Sections[0].sectionItem[0].heading).toEqual({
|
|
151
140
|
en: 'Intro EN',
|
|
152
141
|
fr: 'Intro FR',
|
|
153
142
|
});
|
|
154
|
-
|
|
143
|
+
expect(v3Sections[0].sectionItem[1].heading).toEqual({
|
|
155
144
|
en: 'Body EN',
|
|
156
145
|
fr: 'Body FR',
|
|
157
146
|
});
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
|
-
import
|
|
9
|
-
import { describe, it } from 'node:test';
|
|
8
|
+
import { describe, expect, it } from 'vitest';
|
|
10
9
|
import { allStoreTypes, buildSelectList, columns, fieldTypeLiterals, UNIFIED_COLUMN_COUNT, } from '../storage-store-manifest.js';
|
|
11
10
|
/**
|
|
12
11
|
* Parse a generated SELECT list into individual column expressions.
|
|
@@ -36,13 +35,13 @@ describe('storage-store-manifest', () => {
|
|
|
36
35
|
describe('column count', () => {
|
|
37
36
|
it('UNIFIED_COLUMN_COUNT matches manifest', () => {
|
|
38
37
|
// columns array has all columns except field_type, which is added during generation
|
|
39
|
-
|
|
38
|
+
expect(UNIFIED_COLUMN_COUNT).toBe(columns.length + 1);
|
|
40
39
|
});
|
|
41
40
|
for (const storeType of allStoreTypes) {
|
|
42
41
|
it(`${storeType} SELECT list has ${UNIFIED_COLUMN_COUNT} columns`, () => {
|
|
43
42
|
const selectList = buildSelectList(storeType);
|
|
44
43
|
const cols = parseColumns(selectList);
|
|
45
|
-
|
|
44
|
+
expect(cols.length, `Expected ${UNIFIED_COLUMN_COUNT} columns for ${storeType}, got ${cols.length}:\n${cols.map((c, i) => ` ${i + 1}. ${c}`).join('\n')}`).toBe(UNIFIED_COLUMN_COUNT);
|
|
46
45
|
});
|
|
47
46
|
}
|
|
48
47
|
});
|
|
@@ -53,7 +52,7 @@ describe('storage-store-manifest', () => {
|
|
|
53
52
|
if (storeType === 'text')
|
|
54
53
|
continue;
|
|
55
54
|
const aliases = parseColumns(buildSelectList(storeType)).map(extractAlias);
|
|
56
|
-
|
|
55
|
+
expect(aliases, `Column order mismatch between text and ${storeType}`).toEqual(referenceAliases);
|
|
57
56
|
}
|
|
58
57
|
});
|
|
59
58
|
});
|
|
@@ -62,7 +61,7 @@ describe('storage-store-manifest', () => {
|
|
|
62
61
|
for (const storeType of allStoreTypes) {
|
|
63
62
|
const selectList = buildSelectList(storeType);
|
|
64
63
|
const expected = fieldTypeLiterals[storeType];
|
|
65
|
-
|
|
64
|
+
expect(selectList.includes(`'${expected}' as "field_type"`), `Expected field_type '${expected}' for ${storeType}`).toBe(true);
|
|
66
65
|
}
|
|
67
66
|
});
|
|
68
67
|
});
|
|
@@ -71,7 +70,7 @@ describe('storage-store-manifest', () => {
|
|
|
71
70
|
for (const storeType of allStoreTypes) {
|
|
72
71
|
const cols = parseColumns(buildSelectList(storeType));
|
|
73
72
|
const fieldTypeCol = cols[3];
|
|
74
|
-
|
|
73
|
+
expect(fieldTypeCol?.includes('field_type'), `Expected field_type at index 3 for ${storeType}, got: ${fieldTypeCol}`).toBe(true);
|
|
75
74
|
}
|
|
76
75
|
});
|
|
77
76
|
});
|
|
@@ -79,20 +78,20 @@ describe('storage-store-manifest', () => {
|
|
|
79
78
|
it('text store maps value → text_value', () => {
|
|
80
79
|
const cols = parseColumns(buildSelectList('text'));
|
|
81
80
|
const textValueCol = cols.find((c) => c.includes('text_value'));
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
expect(textValueCol, 'text_value column not found').toBeTruthy();
|
|
82
|
+
expect(textValueCol?.includes('value as "text_value"'), `Expected 'value as "text_value"', got: ${textValueCol}`).toBe(true);
|
|
84
83
|
});
|
|
85
84
|
it('boolean store maps value → boolean_value', () => {
|
|
86
85
|
const cols = parseColumns(buildSelectList('boolean'));
|
|
87
86
|
const boolCol = cols.find((c) => c.includes('boolean_value'));
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
expect(boolCol, 'boolean_value column not found').toBeTruthy();
|
|
88
|
+
expect(boolCol?.includes('value as "boolean_value"'), `Expected 'value as "boolean_value"', got: ${boolCol}`).toBe(true);
|
|
90
89
|
});
|
|
91
90
|
it('numeric store includes number_type, value_integer, value_decimal, value_float', () => {
|
|
92
91
|
const cols = parseColumns(buildSelectList('numeric'));
|
|
93
92
|
const colText = cols.join(' ');
|
|
94
93
|
for (const field of ['number_type', 'value_integer', 'value_decimal', 'value_float']) {
|
|
95
|
-
|
|
94
|
+
expect(colText.includes(field), `Expected ${field} in numeric SELECT list`).toBe(true);
|
|
96
95
|
}
|
|
97
96
|
});
|
|
98
97
|
it('file store includes all file-specific columns', () => {
|
|
@@ -107,14 +106,14 @@ describe('storage-store-manifest', () => {
|
|
|
107
106
|
'storage_provider',
|
|
108
107
|
'storage_path',
|
|
109
108
|
]) {
|
|
110
|
-
|
|
109
|
+
expect(colText.includes(field), `Expected ${field} in file SELECT list`).toBe(true);
|
|
111
110
|
}
|
|
112
111
|
});
|
|
113
112
|
it('non-owning stores emit NULL for type-specific columns', () => {
|
|
114
113
|
const cols = parseColumns(buildSelectList('text'));
|
|
115
114
|
const numericCol = cols.find((c) => c.includes('number_type'));
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
expect(numericCol, 'number_type column not found').toBeTruthy();
|
|
116
|
+
expect(numericCol?.includes('NULL::varchar'), `Expected NULL for number_type in text store, got: ${numericCol}`).toBe(true);
|
|
118
117
|
});
|
|
119
118
|
});
|
|
120
119
|
describe('base columns', () => {
|
|
@@ -132,7 +131,7 @@ describe('storage-store-manifest', () => {
|
|
|
132
131
|
const cols = parseColumns(buildSelectList(storeType));
|
|
133
132
|
for (const baseName of baseNames) {
|
|
134
133
|
const col = cols.find((c) => c.trim() === baseName);
|
|
135
|
-
|
|
134
|
+
expect(col, `Expected bare column '${baseName}' in ${storeType} SELECT list`).toBeTruthy();
|
|
136
135
|
}
|
|
137
136
|
}
|
|
138
137
|
});
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
|
-
import
|
|
9
|
-
import { after, before, describe, it } from 'node:test';
|
|
8
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
10
9
|
import { setupTestDB, teardownTestDB } from '../../../lib/test-helper.js';
|
|
11
10
|
// Test database setup
|
|
12
11
|
let commandBuilders;
|
|
@@ -211,7 +210,7 @@ const complexProductDocument = {
|
|
|
211
210
|
// Global test variables
|
|
212
211
|
let testCollection = {};
|
|
213
212
|
describe('03 Document Creation and Versioning', () => {
|
|
214
|
-
|
|
213
|
+
beforeAll(async () => {
|
|
215
214
|
// Connect to test database
|
|
216
215
|
const testDB = setupTestDB([VersionsCollectionConfig]);
|
|
217
216
|
commandBuilders = testDB.commandBuilders;
|
|
@@ -225,7 +224,7 @@ describe('03 Document Creation and Versioning', () => {
|
|
|
225
224
|
testCollection = { id: collection.id, name: collection.path };
|
|
226
225
|
console.log('Test collection created:', testCollection);
|
|
227
226
|
});
|
|
228
|
-
|
|
227
|
+
afterAll(async () => {
|
|
229
228
|
// Clean up test collection (cascades to documents)
|
|
230
229
|
try {
|
|
231
230
|
await commandBuilders.collections.delete(testCollection.id);
|
|
@@ -253,7 +252,7 @@ describe('03 Document Creation and Versioning', () => {
|
|
|
253
252
|
status: 'draft',
|
|
254
253
|
});
|
|
255
254
|
console.log('Document created:', result);
|
|
256
|
-
|
|
255
|
+
expect(result.document.document_id, 'Document creation failed').not.toBe(null);
|
|
257
256
|
});
|
|
258
257
|
it('should create a document and document version with the same path', async () => {
|
|
259
258
|
const timestamp = Date.now();
|
|
@@ -271,7 +270,7 @@ describe('03 Document Creation and Versioning', () => {
|
|
|
271
270
|
status: 'draft',
|
|
272
271
|
});
|
|
273
272
|
console.log('firstVersion created:', firstVersion);
|
|
274
|
-
|
|
273
|
+
expect(firstVersion.document.document_id, 'Document creation failed').not.toBe(null);
|
|
275
274
|
const secondVersion = await commandBuilders.documents.createDocumentVersion({
|
|
276
275
|
documentId: firstVersion.document.document_id,
|
|
277
276
|
collectionId: testCollection.id,
|
|
@@ -300,7 +299,7 @@ describe('03 Document Creation and Versioning', () => {
|
|
|
300
299
|
locale: 'all',
|
|
301
300
|
status: 'draft',
|
|
302
301
|
});
|
|
303
|
-
|
|
302
|
+
expect(firstVersion.document.document_id, 'Document creation failed').not.toBe(null);
|
|
304
303
|
const secondVersion = await commandBuilders.documents.createDocumentVersion({
|
|
305
304
|
documentId: firstVersion.document.document_id,
|
|
306
305
|
collectionId: testCollection.id,
|
|
@@ -312,7 +311,7 @@ describe('03 Document Creation and Versioning', () => {
|
|
|
312
311
|
locale: 'all',
|
|
313
312
|
status: 'draft',
|
|
314
313
|
});
|
|
315
|
-
|
|
314
|
+
expect(secondVersion.document.document_id, 'Document creation failed').not.toBe(null);
|
|
316
315
|
const thirdVersion = await commandBuilders.documents.createDocumentVersion({
|
|
317
316
|
documentId: firstVersion.document.document_id,
|
|
318
317
|
collectionId: testCollection.id,
|
|
@@ -324,7 +323,7 @@ describe('03 Document Creation and Versioning', () => {
|
|
|
324
323
|
locale: 'all',
|
|
325
324
|
status: 'draft',
|
|
326
325
|
});
|
|
327
|
-
|
|
326
|
+
expect(thirdVersion.document.document_id, 'Document creation failed').not.toBe(null);
|
|
328
327
|
const versionHistory = await queryBuilders.documents.getDocumentHistory({
|
|
329
328
|
collection_id: testCollection.id,
|
|
330
329
|
document_id: firstVersion.document.document_id,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@byline/db-postgres",
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
|
-
"version": "1.12.
|
|
5
|
+
"version": "1.12.2",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.9.0"
|
|
8
8
|
},
|
|
@@ -41,6 +41,11 @@
|
|
|
41
41
|
"import": "./dist/database/schema/index.js",
|
|
42
42
|
"require": "./dist/database/schema/index.js"
|
|
43
43
|
},
|
|
44
|
+
"./testing": {
|
|
45
|
+
"types": "./dist/lib/test-db.d.ts",
|
|
46
|
+
"import": "./dist/lib/test-db.js",
|
|
47
|
+
"require": "./dist/lib/test-db.js"
|
|
48
|
+
},
|
|
44
49
|
"./package.json": "./package.json"
|
|
45
50
|
},
|
|
46
51
|
"files": [
|
|
@@ -52,9 +57,9 @@
|
|
|
52
57
|
"pg": "^8.20.0",
|
|
53
58
|
"uuid": "^14.0.0",
|
|
54
59
|
"zod": "^4.4.3",
|
|
55
|
-
"@byline/
|
|
56
|
-
"@byline/
|
|
57
|
-
"@byline/
|
|
60
|
+
"@byline/auth": "1.12.2",
|
|
61
|
+
"@byline/core": "1.12.2",
|
|
62
|
+
"@byline/admin": "1.12.2"
|
|
58
63
|
},
|
|
59
64
|
"devDependencies": {
|
|
60
65
|
"@biomejs/biome": "2.4.15",
|
|
@@ -62,10 +67,12 @@
|
|
|
62
67
|
"@types/pg": "^8.20.0",
|
|
63
68
|
"chokidar": "^5.0.0",
|
|
64
69
|
"chokidar-cli": "^3.0.0",
|
|
70
|
+
"dotenv": "^17.4.2",
|
|
65
71
|
"drizzle-kit": "^0.31.10",
|
|
66
72
|
"tsc-alias": "^1.8.17",
|
|
67
73
|
"tsx": "^4.21.0",
|
|
68
|
-
"typescript": "6.0.3"
|
|
74
|
+
"typescript": "6.0.3",
|
|
75
|
+
"vitest": "^4.1.5"
|
|
69
76
|
},
|
|
70
77
|
"publishConfig": {
|
|
71
78
|
"access": "public",
|
|
@@ -83,8 +90,9 @@
|
|
|
83
90
|
"drizzle:push": "tsc -p tsconfig.json && drizzle-kit push --config=drizzle.config.ts",
|
|
84
91
|
"drizzle:drop": "drizzle-kit drop --config=drizzle.config.ts",
|
|
85
92
|
"drizzle:seed": "tsx --no-warnings --env-file=.env ./src/database/seeds/index.ts",
|
|
86
|
-
"test": "
|
|
87
|
-
"test:
|
|
93
|
+
"test": "echo 'db-postgres has no unit tests; run pnpm test:integration for the DB-backed suite.'",
|
|
94
|
+
"test:integration": "LOG_LEVEL=off vitest run --mode=integration",
|
|
95
|
+
"test:watch": "LOG_LEVEL=off vitest --mode=integration",
|
|
88
96
|
"typecheck": "tsc --noEmit"
|
|
89
97
|
}
|
|
90
98
|
}
|