@byline/db-mysql 4.8.0
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/LICENSE +373 -0
- package/README.md +283 -0
- package/dist/database/schema/auth.d.ts +951 -0
- package/dist/database/schema/auth.js +226 -0
- package/dist/database/schema/common.d.ts +168 -0
- package/dist/database/schema/common.js +130 -0
- package/dist/database/schema/index.d.ts +3419 -0
- package/dist/database/schema/index.js +895 -0
- package/dist/database/schema/schema-pins.test.node.d.ts +52 -0
- package/dist/database/schema/schema-pins.test.node.js +398 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +151 -0
- package/dist/lib/boot-check.d.ts +22 -0
- package/dist/lib/boot-check.js +42 -0
- package/dist/lib/boot-check.test.node.d.ts +8 -0
- package/dist/lib/boot-check.test.node.js +46 -0
- package/dist/lib/db-manager.d.ts +54 -0
- package/dist/lib/db-manager.js +49 -0
- package/dist/lib/test-db.d.ts +37 -0
- package/dist/lib/test-db.js +111 -0
- package/dist/lib/test-helper.d.ts +33 -0
- package/dist/lib/test-helper.js +68 -0
- package/dist/modules/admin/admin-permissions-repository.d.ts +35 -0
- package/dist/modules/admin/admin-permissions-repository.js +101 -0
- package/dist/modules/admin/admin-preferences-repository.d.ts +62 -0
- package/dist/modules/admin/admin-preferences-repository.js +156 -0
- package/dist/modules/admin/admin-roles-repository.d.ts +11 -0
- package/dist/modules/admin/admin-roles-repository.js +209 -0
- package/dist/modules/admin/admin-store.d.ts +20 -0
- package/dist/modules/admin/admin-store.js +30 -0
- package/dist/modules/admin/admin-users-repository.d.ts +11 -0
- package/dist/modules/admin/admin-users-repository.js +303 -0
- package/dist/modules/admin/index.d.ts +27 -0
- package/dist/modules/admin/index.js +27 -0
- package/dist/modules/admin/refresh-tokens-repository.d.ts +34 -0
- package/dist/modules/admin/refresh-tokens-repository.js +160 -0
- package/dist/modules/audit/audit-commands.d.ts +29 -0
- package/dist/modules/audit/audit-commands.js +40 -0
- package/dist/modules/audit/audit-queries.d.ts +41 -0
- package/dist/modules/audit/audit-queries.js +169 -0
- package/dist/modules/counters/counters-commands.d.ts +54 -0
- package/dist/modules/counters/counters-commands.js +121 -0
- package/dist/modules/counters/tests/counters-concurrency.test.d.ts +8 -0
- package/dist/modules/counters/tests/counters-concurrency.test.js +111 -0
- package/dist/modules/storage/classify-error.d.ts +34 -0
- package/dist/modules/storage/classify-error.js +50 -0
- package/dist/modules/storage/classify-error.test.node.d.ts +8 -0
- package/dist/modules/storage/classify-error.test.node.js +89 -0
- package/dist/modules/storage/normalize-row.d.ts +55 -0
- package/dist/modules/storage/normalize-row.js +135 -0
- package/dist/modules/storage/normalize-row.test.node.d.ts +8 -0
- package/dist/modules/storage/normalize-row.test.node.js +89 -0
- package/dist/modules/storage/storage-commands.d.ts +443 -0
- package/dist/modules/storage/storage-commands.js +1263 -0
- package/dist/modules/storage/storage-insert.d.ts +21 -0
- package/dist/modules/storage/storage-insert.js +152 -0
- package/dist/modules/storage/storage-queries.d.ts +805 -0
- package/dist/modules/storage/storage-queries.js +1815 -0
- package/dist/modules/storage/storage-store-manifest.d.ts +77 -0
- package/dist/modules/storage/storage-store-manifest.js +168 -0
- package/dist/modules/storage/storage-utils.d.ts +49 -0
- package/dist/modules/storage/storage-utils.js +76 -0
- package/dist/modules/storage/tests/dialect-pins.integration.test.d.ts +8 -0
- package/dist/modules/storage/tests/dialect-pins.integration.test.js +266 -0
- package/dist/modules/storage/tests/storage-commands.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-commands.test.js +324 -0
- package/dist/modules/storage/tests/storage-document-paths.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-document-paths.test.js +214 -0
- package/dist/modules/storage/tests/storage-document-tree.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-document-tree.test.js +361 -0
- package/dist/modules/storage/tests/storage-queries.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-queries.test.js +685 -0
- package/dist/modules/storage/tests/storage-status-and-lifecycle.test.d.ts +8 -0
- package/dist/modules/storage/tests/storage-status-and-lifecycle.test.js +268 -0
- package/package.json +91 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import type { UnifiedFieldValue } from '@byline/core';
|
|
9
|
+
/**
|
|
10
|
+
* Canonicalise a raw UNION ALL driver row to the shared `UnifiedFieldValue`
|
|
11
|
+
* contract before it reaches `extractFlattenedFieldValue`. Mirrors
|
|
12
|
+
* `packages/db-postgres/src/modules/storage/normalize-row.ts`, whose pg
|
|
13
|
+
* counterpart also coerces `value_date` / `value_timestamp_tz` to `Date`
|
|
14
|
+
* (as of task 13b — see that file's docblock for the pg-specific evidence)
|
|
15
|
+
* but is otherwise closer to an identity cast: the mysql2 driver needs
|
|
16
|
+
* substantially more canonicalisation below, verified against a live
|
|
17
|
+
* MySQL 9.7.1 server rather than assumed (mysql2's declared types are a
|
|
18
|
+
* hypothesis, not fact — see `src/index.ts`'s boot-check comment for the
|
|
19
|
+
* prior instance of this on this program). `value_time` is deliberately
|
|
20
|
+
* left untouched on both adapters — a bare time-of-day has no calendar
|
|
21
|
+
* date or time zone to normalise, and the task-13b "converge on Date"
|
|
22
|
+
* ruling explicitly excludes it (see the `time` fixture in
|
|
23
|
+
* `packages/db-conformance/src/suites/field-types.ts`):
|
|
24
|
+
*
|
|
25
|
+
* - `TINYINT(1)` → `boolean`. mysql2 returns a JS `number` (`0`/`1`) for
|
|
26
|
+
* `TINYINT(1)` columns selected through a raw query, not a `boolean` —
|
|
27
|
+
* confirmed live. Only the three `UnifiedFieldValue` columns backed by a
|
|
28
|
+
* `boolean()` column need the coercion: `boolean_value` (the boolean
|
|
29
|
+
* store's own value), `thumbnail_generated` (file store), and
|
|
30
|
+
* `cascade_delete` (relation store).
|
|
31
|
+
* - `DECIMAL` stays a **string** — confirmed live with the pool's
|
|
32
|
+
* `decimalNumbers: false` option (`src/index.ts`), which must not be
|
|
33
|
+
* undone; MySQL/JS float coercion would lose precision on money/decimal
|
|
34
|
+
* values, matching the pg adapter's `numeric` handling.
|
|
35
|
+
* - `JSON` columns arrive already parsed by the driver — confirmed live
|
|
36
|
+
* (an inserted JS object round-trips as an object, not a JSON string) —
|
|
37
|
+
* so this function must not re-`JSON.parse` them.
|
|
38
|
+
* - `DATE`/`DATETIME` → **string**, not `Date` — the opposite of what an
|
|
39
|
+
* earlier version of this comment claimed. `drizzle-orm`'s mysql2
|
|
40
|
+
* driver installs its own `typeCast` on every raw `db.execute(sql\`...\`)`
|
|
41
|
+
* call (this UNION ALL query has no schema-typed `fields` mapper, so it
|
|
42
|
+
* always takes that path) that unconditionally calls `field.string()`
|
|
43
|
+
* for `TIMESTAMP`/`DATETIME`/`DATE` columns, overriding whatever the
|
|
44
|
+
* pool's own `timezone` option would otherwise do — confirmed live:
|
|
45
|
+
* `value_date` (a `datetimeStore.date_type === 'date'` row) comes back
|
|
46
|
+
* as `'2026-01-15'`; `value_timestamp_tz` (`date_type === 'datetime'`)
|
|
47
|
+
* comes back as `'2026-01-15 10:30:00.123000'`. Both reach
|
|
48
|
+
* `restoreFieldSetData` (`@byline/core`) and become the runtime value
|
|
49
|
+
* of a document's `date`/`datetime` field, so an un-coerced string here
|
|
50
|
+
* was a real, user-facing defect — not just an internal-tooling one —
|
|
51
|
+
* caught by no existing test because none exercised a `date`/`datetime`
|
|
52
|
+
* field type through this UNION ALL path. `toDate` (shared,
|
|
53
|
+
* `storage-utils.js`) and `toDateOnly` below fix it.
|
|
54
|
+
*/
|
|
55
|
+
export declare function normalizeRow(row: Record<string, unknown>): UnifiedFieldValue;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { toDate } from './storage-utils.js';
|
|
9
|
+
/**
|
|
10
|
+
* Canonicalise a raw UNION ALL driver row to the shared `UnifiedFieldValue`
|
|
11
|
+
* contract before it reaches `extractFlattenedFieldValue`. Mirrors
|
|
12
|
+
* `packages/db-postgres/src/modules/storage/normalize-row.ts`, whose pg
|
|
13
|
+
* counterpart also coerces `value_date` / `value_timestamp_tz` to `Date`
|
|
14
|
+
* (as of task 13b — see that file's docblock for the pg-specific evidence)
|
|
15
|
+
* but is otherwise closer to an identity cast: the mysql2 driver needs
|
|
16
|
+
* substantially more canonicalisation below, verified against a live
|
|
17
|
+
* MySQL 9.7.1 server rather than assumed (mysql2's declared types are a
|
|
18
|
+
* hypothesis, not fact — see `src/index.ts`'s boot-check comment for the
|
|
19
|
+
* prior instance of this on this program). `value_time` is deliberately
|
|
20
|
+
* left untouched on both adapters — a bare time-of-day has no calendar
|
|
21
|
+
* date or time zone to normalise, and the task-13b "converge on Date"
|
|
22
|
+
* ruling explicitly excludes it (see the `time` fixture in
|
|
23
|
+
* `packages/db-conformance/src/suites/field-types.ts`):
|
|
24
|
+
*
|
|
25
|
+
* - `TINYINT(1)` → `boolean`. mysql2 returns a JS `number` (`0`/`1`) for
|
|
26
|
+
* `TINYINT(1)` columns selected through a raw query, not a `boolean` —
|
|
27
|
+
* confirmed live. Only the three `UnifiedFieldValue` columns backed by a
|
|
28
|
+
* `boolean()` column need the coercion: `boolean_value` (the boolean
|
|
29
|
+
* store's own value), `thumbnail_generated` (file store), and
|
|
30
|
+
* `cascade_delete` (relation store).
|
|
31
|
+
* - `DECIMAL` stays a **string** — confirmed live with the pool's
|
|
32
|
+
* `decimalNumbers: false` option (`src/index.ts`), which must not be
|
|
33
|
+
* undone; MySQL/JS float coercion would lose precision on money/decimal
|
|
34
|
+
* values, matching the pg adapter's `numeric` handling.
|
|
35
|
+
* - `JSON` columns arrive already parsed by the driver — confirmed live
|
|
36
|
+
* (an inserted JS object round-trips as an object, not a JSON string) —
|
|
37
|
+
* so this function must not re-`JSON.parse` them.
|
|
38
|
+
* - `DATE`/`DATETIME` → **string**, not `Date` — the opposite of what an
|
|
39
|
+
* earlier version of this comment claimed. `drizzle-orm`'s mysql2
|
|
40
|
+
* driver installs its own `typeCast` on every raw `db.execute(sql\`...\`)`
|
|
41
|
+
* call (this UNION ALL query has no schema-typed `fields` mapper, so it
|
|
42
|
+
* always takes that path) that unconditionally calls `field.string()`
|
|
43
|
+
* for `TIMESTAMP`/`DATETIME`/`DATE` columns, overriding whatever the
|
|
44
|
+
* pool's own `timezone` option would otherwise do — confirmed live:
|
|
45
|
+
* `value_date` (a `datetimeStore.date_type === 'date'` row) comes back
|
|
46
|
+
* as `'2026-01-15'`; `value_timestamp_tz` (`date_type === 'datetime'`)
|
|
47
|
+
* comes back as `'2026-01-15 10:30:00.123000'`. Both reach
|
|
48
|
+
* `restoreFieldSetData` (`@byline/core`) and become the runtime value
|
|
49
|
+
* of a document's `date`/`datetime` field, so an un-coerced string here
|
|
50
|
+
* was a real, user-facing defect — not just an internal-tooling one —
|
|
51
|
+
* caught by no existing test because none exercised a `date`/`datetime`
|
|
52
|
+
* field type through this UNION ALL path. `toDate` (shared,
|
|
53
|
+
* `storage-utils.js`) and `toDateOnly` below fix it.
|
|
54
|
+
*/
|
|
55
|
+
export function normalizeRow(row) {
|
|
56
|
+
return {
|
|
57
|
+
...row,
|
|
58
|
+
boolean_value: normalizeTinyIntBoolean(row.boolean_value),
|
|
59
|
+
thumbnail_generated: normalizeTinyIntBoolean(row.thumbnail_generated),
|
|
60
|
+
cascade_delete: normalizeTinyIntBoolean(row.cascade_delete),
|
|
61
|
+
value_date: toDateOnly(row.value_date),
|
|
62
|
+
value_timestamp_tz: toDate(row.value_timestamp_tz, 'value_timestamp_tz'),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* `'2026-01-15'` (MySQL `DATE` text, no time-of-day component) → a `Date`
|
|
67
|
+
* at **UTC** midnight for that calendar date.
|
|
68
|
+
*
|
|
69
|
+
* This is an elected divergence from node-postgres, not a parity claim — an
|
|
70
|
+
* earlier version of this docblock claimed UTC midnight matches
|
|
71
|
+
* node-postgres's own default parser for a `date` column. It doesn't.
|
|
72
|
+
* node-postgres's default `date` parser is `postgres-date` (see its
|
|
73
|
+
* `index.js:16-17`, whose own comment reads "Force YYYY-MM-DD dates to be
|
|
74
|
+
* parsed as local time") — that mismatch is the reason the UTC-vs-local
|
|
75
|
+
* question was raised in the first place. It's moot in practice, though:
|
|
76
|
+
* pg's `normalize-row.ts` bypasses `postgres-date` entirely (drizzle-orm's
|
|
77
|
+
* node-postgres session installs an identity `getTypeParser` for `DATE`, so
|
|
78
|
+
* that default parser never runs on this read path), and as of task 13b
|
|
79
|
+
* pg's own `toDateOnly` anchors at UTC midnight too, for the same
|
|
80
|
+
* determinism reason: host-local midnight would make the same stored `date`
|
|
81
|
+
* value materialise as a different calendar day depending on which host
|
|
82
|
+
* served it.
|
|
83
|
+
*
|
|
84
|
+
* **Ruling (project owner, task 13b): settled.** Both adapters return a
|
|
85
|
+
* `Date` at UTC midnight for `date` fields — verified together, via the
|
|
86
|
+
* shared `@byline/db-conformance` `field-types.ts` fixtures, under `TZ=UTC`
|
|
87
|
+
* and two non-UTC zones on both sides of it (`Asia/Bangkok`, `+07`, and
|
|
88
|
+
* `America/New_York`, a negative offset — a positive-only check can't
|
|
89
|
+
* distinguish a local-getter bug from a correct UTC one when the fixture's
|
|
90
|
+
* instant is itself UTC midnight). This is no longer provisional.
|
|
91
|
+
*
|
|
92
|
+
* This is the sole gate between the raw driver row and `ClientDocument`'s
|
|
93
|
+
* public `value_date` shape, so a malformed `DATE` string must not fall
|
|
94
|
+
* through as a silent `Invalid Date` — it throws instead, naming the raw
|
|
95
|
+
* value.
|
|
96
|
+
*
|
|
97
|
+
* The `value instanceof Date` branch is a defensive passthrough for a row
|
|
98
|
+
* assembled in-process rather than round-tripped through the driver — not
|
|
99
|
+
* exercised through the live UNION ALL read path today (mysql2's
|
|
100
|
+
* `typeCast` override always hands this function text, per the module
|
|
101
|
+
* docblock above). If a future driver change ever made this column arrive
|
|
102
|
+
* as a `Date` already, this function would trust it unchanged rather than
|
|
103
|
+
* re-normalising it to UTC midnight — and there's no safe way to
|
|
104
|
+
* re-normalise after the fact, because a bare `Date` carries no record of
|
|
105
|
+
* which midnight convention (UTC or host-local) produced it, so
|
|
106
|
+
* re-deriving the calendar day from either its UTC or local getters could
|
|
107
|
+
* silently pick the wrong one depending on the host's offset. Flagging
|
|
108
|
+
* this explicitly rather than adding a "normalisation" that would only be
|
|
109
|
+
* correct for some host timezones — mirrors the identical hazard comment
|
|
110
|
+
* on pg's `toDateOnly`.
|
|
111
|
+
*/
|
|
112
|
+
function toDateOnly(value) {
|
|
113
|
+
if (value == null)
|
|
114
|
+
return null;
|
|
115
|
+
if (value instanceof Date)
|
|
116
|
+
return value;
|
|
117
|
+
const date = new Date(`${value}T00:00:00.000Z`);
|
|
118
|
+
if (Number.isNaN(date.getTime())) {
|
|
119
|
+
throw new Error(`normalizeRow: value_date is not a parseable date — got ${JSON.stringify(value)}`);
|
|
120
|
+
}
|
|
121
|
+
return date;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* `TINYINT(1)` → `boolean`, tolerant of the value already being a real
|
|
125
|
+
* boolean (a row assembled in-process rather than round-tripped through the
|
|
126
|
+
* driver) or `null`/`undefined` (column absent for this row's field type —
|
|
127
|
+
* only one value column is ever populated per UNION ALL row).
|
|
128
|
+
*/
|
|
129
|
+
function normalizeTinyIntBoolean(value) {
|
|
130
|
+
if (value == null)
|
|
131
|
+
return null;
|
|
132
|
+
if (typeof value === 'boolean')
|
|
133
|
+
return value;
|
|
134
|
+
return value !== 0;
|
|
135
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { describe, expect, it } from 'vitest';
|
|
9
|
+
import { normalizeRow } from './normalize-row.js';
|
|
10
|
+
describe('normalizeRow (mysql)', () => {
|
|
11
|
+
it('coerces boolean_value from a driver TINYINT(1) number to boolean', () => {
|
|
12
|
+
expect(normalizeRow({ boolean_value: 1 }).boolean_value).toBe(true);
|
|
13
|
+
expect(normalizeRow({ boolean_value: 0 }).boolean_value).toBe(false);
|
|
14
|
+
});
|
|
15
|
+
it('coerces thumbnail_generated and cascade_delete the same way', () => {
|
|
16
|
+
const row = normalizeRow({ thumbnail_generated: 1, cascade_delete: 0 });
|
|
17
|
+
expect(row.thumbnail_generated).toBe(true);
|
|
18
|
+
expect(row.cascade_delete).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
it('passes through null for absent tinyint(1) columns (only one value column is populated per row)', () => {
|
|
21
|
+
const row = normalizeRow({
|
|
22
|
+
boolean_value: null,
|
|
23
|
+
thumbnail_generated: null,
|
|
24
|
+
cascade_delete: null,
|
|
25
|
+
});
|
|
26
|
+
expect(row.boolean_value).toBeNull();
|
|
27
|
+
expect(row.thumbnail_generated).toBeNull();
|
|
28
|
+
expect(row.cascade_delete).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
it('tolerates a value already coerced to a real boolean', () => {
|
|
31
|
+
expect(normalizeRow({ boolean_value: true }).boolean_value).toBe(true);
|
|
32
|
+
expect(normalizeRow({ boolean_value: false }).boolean_value).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
it('leaves a DECIMAL string untouched (decimalNumbers: false at the pool)', () => {
|
|
35
|
+
const row = normalizeRow({ value_decimal: '299.99' });
|
|
36
|
+
expect(row.value_decimal).toBe('299.99');
|
|
37
|
+
expect(typeof row.value_decimal).toBe('string');
|
|
38
|
+
});
|
|
39
|
+
it('leaves an already-parsed JSON value untouched (the driver parses JSON columns itself)', () => {
|
|
40
|
+
const value = { a: 1, b: [1, 2, 3] };
|
|
41
|
+
const row = normalizeRow({ json_value: value });
|
|
42
|
+
expect(row.json_value).toBe(value);
|
|
43
|
+
});
|
|
44
|
+
it('leaves an already-Date value_timestamp_tz untouched (defensive — not expected on the live driver path)', () => {
|
|
45
|
+
const date = new Date('2024-01-15T10:30:00.123Z');
|
|
46
|
+
const row = normalizeRow({ value_timestamp_tz: date });
|
|
47
|
+
expect(row.value_timestamp_tz).toBe(date);
|
|
48
|
+
});
|
|
49
|
+
it('coerces value_timestamp_tz from the driver DATETIME string to a real Date', () => {
|
|
50
|
+
// The shape drizzle-orm's mysql2 driver actually hands back on the raw
|
|
51
|
+
// `db.execute()` path (its own typeCast forces DATETIME to a string) —
|
|
52
|
+
// confirmed live against `packages/db-mysql/scripts` probes for the
|
|
53
|
+
// Task 11 report. Space-separated, no timezone marker (UTC by schema
|
|
54
|
+
// convention).
|
|
55
|
+
const row = normalizeRow({ value_timestamp_tz: '2026-01-15 10:30:00.123456' });
|
|
56
|
+
expect(row.value_timestamp_tz).toBeInstanceOf(Date);
|
|
57
|
+
expect(row.value_timestamp_tz.toISOString()).toBe('2026-01-15T10:30:00.123Z');
|
|
58
|
+
});
|
|
59
|
+
it('passes through null for an absent value_timestamp_tz column', () => {
|
|
60
|
+
expect(normalizeRow({ value_timestamp_tz: null }).value_timestamp_tz).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
it('coerces value_date from the driver DATE string to a UTC-midnight Date', () => {
|
|
63
|
+
const row = normalizeRow({ value_date: '2026-01-15' });
|
|
64
|
+
expect(row.value_date).toBeInstanceOf(Date);
|
|
65
|
+
expect(row.value_date.toISOString()).toBe('2026-01-15T00:00:00.000Z');
|
|
66
|
+
});
|
|
67
|
+
it('leaves an already-Date value_date untouched (defensive)', () => {
|
|
68
|
+
const date = new Date('2026-01-15T00:00:00.000Z');
|
|
69
|
+
const row = normalizeRow({ value_date: date });
|
|
70
|
+
expect(row.value_date).toBe(date);
|
|
71
|
+
});
|
|
72
|
+
it('passes through null for an absent value_date column', () => {
|
|
73
|
+
expect(normalizeRow({ value_date: null }).value_date).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
it('passes through every other column unchanged', () => {
|
|
76
|
+
const row = normalizeRow({
|
|
77
|
+
field_path: 'title',
|
|
78
|
+
field_name: 'title',
|
|
79
|
+
locale: 'en',
|
|
80
|
+
text_value: 'hello',
|
|
81
|
+
});
|
|
82
|
+
expect(row).toMatchObject({
|
|
83
|
+
field_path: 'title',
|
|
84
|
+
field_name: 'title',
|
|
85
|
+
locale: 'en',
|
|
86
|
+
text_value: 'hello',
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|