|
37 | 37 | * non-empty `encryptionKey` in a local or replica mode, refused on |
38 | 38 | * `encryptionKey` — the fifth copy, one text per mode. Those rows used to |
39 | 39 | * be accepted everywhere, and the driver wrote the file in plaintext: its |
40 | | - * local engine (better-sqlite3) has no encryption codec. |
| 40 | + * local engine (better-sqlite3) has no encryption codec. And for the same |
| 41 | + * key beside a `file:` url or `:memory:` under a forced `mode: 'remote'`, |
| 42 | + * whose libSQL client is handed no key — the sixth copy, one text for a |
| 43 | + * file and one for an in-memory database. The mirror declares no `mode`, so |
| 44 | + * it judges those configs as the local database their url selects (it |
| 45 | + * refuses them on `encryptionKey` in the local arm's words); its own copy |
| 46 | + * of the remote arm is unreachable through it and kept byte-identical, as |
| 47 | + * its other forced-mode branches are. |
41 | 48 | * |
42 | 49 | * ⚠️ The mirror declares no `mode`, so zod strips an authored one before its |
43 | 50 | * refinement runs: rows that FORCE a mode are judged by the constructor and the |
@@ -121,6 +128,12 @@ const ROWS: Row[] = [ |
121 | 128 | // [#25020] An empty key is unset, as the loaders read it. |
122 | 129 | { name: 'an empty encryptionKey (unset) beside a file: url', config: { url: FILE, encryptionKey: '' }, ctor: 'accept' }, |
123 | 130 | { name: 'an empty encryptionKey (unset) on a replica', config: { url: FILE, syncUrl: REMOTE, sync: { onConnect: false }, encryptionKey: '' }, ctor: 'accept' }, |
| 131 | + // [#25020, patch round 1] The forced-remote local-url arm's controls: no key, an empty key, and a |
| 132 | + // remote url with a key — a remote server's storage, which this card does not judge. |
| 133 | + { name: ":memory: under a forced mode: 'remote'", config: { url: ':memory:', mode: 'remote' }, ctor: 'accept' }, |
| 134 | + { name: "an empty encryptionKey (unset) beside file: under a forced mode: 'remote'", config: { url: FILE, mode: 'remote', encryptionKey: '' }, ctor: 'accept' }, |
| 135 | + { name: 'encryptionKey beside a libsql:// url (remote: not judged by this card)', config: { url: REMOTE, encryptionKey: KEY }, ctor: 'accept' }, |
| 136 | + { name: "encryptionKey beside libsql:// under a forced mode: 'remote' (not judged by this card)", config: { url: REMOTE, mode: 'remote', encryptionKey: KEY }, ctor: 'accept' }, |
124 | 137 |
|
125 | 138 | // ── a remote url in a local or replica mode ───────────────────────────── |
126 | 139 | { name: 'libsql:// beside syncUrl', config: { url: REMOTE, syncUrl: REMOTE }, ctor: 'refuse', refusedOn: 'url' }, |
@@ -210,6 +223,19 @@ const ROWS: Row[] = [ |
210 | 223 | { name: 'encryptionKey beside sync with no syncUrl: the sync refusal first', config: { url: FILE, sync: { intervalSeconds: 60 }, encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'sync', issues: 2 }, |
211 | 224 | { name: "encryptionKey under a forced mode: 'replica' with no syncUrl: the mode refusal first", config: { url: FILE, mode: 'replica', encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'mode' }, |
212 | 225 | { name: "encryptionKey + syncUrl under a forced mode: 'local': the mode refusal first", config: { url: FILE, mode: 'local', syncUrl: REMOTE, encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'mode' }, |
| 226 | + |
| 227 | + // ── the same key beside a local url under a forced mode: 'remote' (#25020, patch round 1) ── |
| 228 | + // Judged by where the database lives, not by the mode: `connect()` hands the url to the libSQL |
| 229 | + // client the driver builds, which is handed no key. The mirror declares no `mode`, so these rows |
| 230 | + // are judged by the constructor and the spec contract only (see the header). |
| 231 | + { name: "encryptionKey beside file: under a forced mode: 'remote'", config: { url: FILE, mode: 'remote', encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'encryptionKey' }, |
| 232 | + { name: "encryptionKey beside an uppercase FILE: url under a forced mode: 'remote'", config: { url: `FILE:${DIR}/upper-remote-key.db`, mode: 'remote', encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'encryptionKey' }, |
| 233 | + { name: "encryptionKey beside file: + timeoutMs under a forced mode: 'remote'", config: { url: FILE, mode: 'remote', timeoutMs: 5000, encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'encryptionKey' }, |
| 234 | + { name: "encryptionKey beside :memory: under a forced mode: 'remote'", config: { url: ':memory:', mode: 'remote', encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'encryptionKey' }, |
| 235 | + { name: "encryptionKey beside file::memory: under a forced mode: 'remote'", config: { url: 'file::memory:', mode: 'remote', encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'encryptionKey' }, |
| 236 | + // ORDER against the forced-remote refusals: the spec contract raises BOTH issues (theirs first); the constructor throws the first. |
| 237 | + { name: "encryptionKey + syncUrl beside file: under a forced mode: 'remote': the syncUrl refusal first", config: { url: FILE, mode: 'remote', syncUrl: REMOTE, encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'syncUrl', issues: 2 }, |
| 238 | + { name: "encryptionKey + sync with no syncUrl beside file: under a forced mode: 'remote': the sync refusal first", config: { url: FILE, mode: 'remote', sync: { onConnect: true }, encryptionKey: KEY }, ctor: 'refuse', refusedOn: 'sync', issues: 2 }, |
213 | 239 | ]; |
214 | 240 |
|
215 | 241 | /** |
@@ -285,9 +311,11 @@ describe('turso config: the constructor, the spec contract and this mirror agree |
285 | 311 | expect(ROWS.filter((r) => r.refusedOn === 'mode' && r.config.mode === 'local').length).toBeGreaterThanOrEqual(8); |
286 | 312 | // [#25020] Both modes of the `encryptionKey` refusal, floored apart so the |
287 | 313 | // replica half cannot shrink behind the local half. |
288 | | - expect(ROWS.filter((r) => r.refusedOn === 'encryptionKey').length).toBeGreaterThanOrEqual(8); |
| 314 | + expect(ROWS.filter((r) => r.refusedOn === 'encryptionKey').length).toBeGreaterThanOrEqual(13); |
289 | 315 | expect(ROWS.filter((r) => r.refusedOn === 'encryptionKey' && (r.config.syncUrl || r.config.mode === 'replica')).length).toBeGreaterThanOrEqual(3); |
290 | | - expect(SYNC_KEY_REFUSALS.length).toBeGreaterThanOrEqual(31); |
| 316 | + // [#25020, patch round 1] The forced-remote local-url half, floored on its own. |
| 317 | + expect(ROWS.filter((r) => r.refusedOn === 'encryptionKey' && r.config.mode === 'remote').length).toBeGreaterThanOrEqual(5); |
| 318 | + expect(SYNC_KEY_REFUSALS.length).toBeGreaterThanOrEqual(38); |
291 | 319 | // [#20200] Exactly zero, not a floor: every key the constructor used to |
292 | 320 | // build and ignore is refused at construction now (see the header). |
293 | 321 | expect(ROWS.filter((r) => r.inert).length).toBe(0); |
|
0 commit comments