Skip to content

Commit 6842674

Browse files
committed
fix(driver-turso): satisfy the #4918 query-options-erasure ratchet on the migrated tests (#4645)
The 15 turso test files arrived from cloud, which predates this repo's `query-options/no-any-erasure` rule (#5600, landed on main after the first merge), and added 52 sites — test surface 267 -> 319. Every one is inside `packages/drivers/driver-turso/src/*.test.ts`; nothing outside turso moved. Both dispositions the rule names, chosen per site, with no assertion touched: - 14 sites TYPED. Ordinary on-contract reads whose only defect was the missing required `QueryAST.object` key (`driver.count('users', { where: { age: 17 } })`, the TEMPORAL_CASES/TEMPORAL_TIME_CASES conformance reads, and the `$not`/`$or` spellings the transport is asserted to ANSWER). Declaring `object` is what driver-memory's conformance suite already does, and it puts `where` back under tsc. - 38 sites `as unknown as QueryAST`. Deliberately off-contract input: values flowing through the suites' `unknown`-typed helpers (`compile`, `refusalOf`, `ids`) and inline literals whose whole point is refusal — `where: 42`, `[]`, `''`, a class instance, bare AST arrays, `{ $or: [null] }`, `{ $not: null }`, non-boolean `$null` comparands. These assert the transport REJECTS them, so the type must be bypassed by name rather than erased. Baseline untouched: 267 is met, not raised. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
1 parent 51a22e8 commit 6842674

6 files changed

Lines changed: 56 additions & 52 deletions

packages/drivers/driver-turso/src/remote-transport-boolean-identity.test.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest';
44
import { RemoteTransport } from './remote-transport.js';
55
import { TursoDriver } from './turso-driver.js';
66
import { makeLibsqlSqliteStub, type LibsqlSqliteStub } from './libsql-sqlite-stub.testkit.js';
7+
import type { QueryAST } from '@objectstack/spec/data';
78

89
/**
910
* Regression: `$and`/`$or` sub-filters that compile to nothing must get the
@@ -64,7 +65,7 @@ function transportWithCapturingClient() {
6465
/** The SQL a `find` compiled to, plus its bind list. */
6566
async function compile(where: unknown): Promise<{ sql: string; args: any[] }> {
6667
const { t, calls } = transportWithCapturingClient();
67-
await t.find('deal', { where } as any);
68+
await t.find('deal', { where } as unknown as QueryAST);
6869
return calls[0];
6970
}
7071

@@ -175,7 +176,7 @@ describe('RemoteTransport $and/$or identity elements (#1073)', () => {
175176
// is a real "too many parameter values" failure, not a cosmetic one.
176177
const { t, calls } = transportWithCapturingClient();
177178
const where = { $or: [{ stage: 'won' }, {}] };
178-
await t.count('deal', { where } as any);
179+
await t.count('deal', { where } as unknown as QueryAST);
179180
await t.deleteMany('deal', { where } as any);
180181
await t.updateMany('deal', { where } as any, { stage: 'lost' });
181182
expect(calls[0].args).toEqual([]);
@@ -325,7 +326,7 @@ describe('RemoteTransport $and/$or identity elements (#1073)', () => {
325326
describe('(f) the same answers through every WHERE-building entry point', () => {
326327
it('compiles `$or: []` to FALSE on count / deleteMany / updateMany', async () => {
327328
const { t, calls } = transportWithCapturingClient();
328-
await t.count('deal', { where: { $or: [] } } as any);
329+
await t.count('deal', { object: 'deal', where: { $or: [] } });
329330
await t.deleteMany('deal', { where: { $or: [] } } as any);
330331
await t.updateMany('deal', { where: { $or: [] } } as any, { stage: 'lost' });
331332
// Pre-fix these were an unfiltered COUNT, a DELETE of the whole table and
@@ -380,7 +381,7 @@ describe('TursoDriver remote — identity elements on real rows (#1073)', () =>
380381
});
381382

382383
const ids = async (where: unknown) =>
383-
((await driver.find('deal', { where } as any)) as any[]).map((r) => r.id).sort();
384+
((await driver.find('deal', { where } as unknown as QueryAST)) as any[]).map((r) => r.id).sort();
384385

385386
it('`$or: []` returns ZERO rows', async () => {
386387
// Pre-fix: all three.
@@ -404,8 +405,8 @@ describe('TursoDriver remote — identity elements on real rows (#1073)', () =>
404405
it('`count` with an absorbed `$or` counts every row and binds nothing', async () => {
405406
// Executed, not string-matched: a stray bind left over from the absorbed
406407
// disjunct makes better-sqlite3 reject the statement outright.
407-
expect(await driver.count('deal', { where: { $or: [{ stage: 'won' }, {}] } } as any)).toBe(3);
408-
expect(await driver.count('deal', { where: { $or: [] } } as any)).toBe(0);
408+
expect(await driver.count('deal', { object: 'deal', where: { $or: [{ stage: 'won' }, {}] } })).toBe(3);
409+
expect(await driver.count('deal', { object: 'deal', where: { $or: [] } })).toBe(0);
409410
});
410411

411412
it('a real two-branch `$or` is unchanged', async () => {
@@ -429,7 +430,7 @@ describe('TursoDriver remote — identity elements on real rows (#1073)', () =>
429430
});
430431

431432
it('a non-node branch element is refused through the driver too', async () => {
432-
await expect(driver.find('deal', { where: { $or: [null] } } as any)).rejects.toThrow(
433+
await expect(driver.find('deal', { where: { $or: [null] } } as unknown as QueryAST)).rejects.toThrow(
433434
/not a filter condition/,
434435
);
435436
});

packages/drivers/driver-turso/src/remote-transport-not-operator.test.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest';
44
import { RemoteTransport } from './remote-transport.js';
55
import { TursoDriver } from './turso-driver.js';
66
import { makeLibsqlSqliteStub, type LibsqlSqliteStub } from './libsql-sqlite-stub.testkit.js';
7+
import type { QueryAST } from '@objectstack/spec/data';
78

89
/**
910
* Regression: the remote transport must compile the spec's THIRD logical
@@ -72,7 +73,7 @@ function transportWithCapturingClient() {
7273
/** The SQL a `find` compiled to, plus its bind list. */
7374
async function compile(where: unknown): Promise<{ sql: string; args: any[] }> {
7475
const { t, calls } = transportWithCapturingClient();
75-
await t.find('deal', { where } as any);
76+
await t.find('deal', { where } as unknown as QueryAST);
7677
return calls[0];
7778
}
7879

@@ -295,19 +296,19 @@ describe('RemoteTransport $not (#1076)', () => {
295296
it('keeps refusing what the INNER filter refuses — the negation swallows nothing', async () => {
296297
const { t } = transportWithCapturingClient();
297298
// #1071: an empty operator map on a field.
298-
await expect(t.find('deal', { where: { $not: { closed_at: {} } } } as any)).rejects.toThrow(
299+
await expect(t.find('deal', { where: { $not: { closed_at: {} } } } as unknown as QueryAST)).rejects.toThrow(
299300
/compiles to NO predicate/,
300301
);
301302
// #1004: an unknown operator.
302303
await expect(
303-
t.find('deal', { where: { $not: { stage: { $like: 'w%' } } } } as any),
304+
t.find('deal', { where: { $not: { stage: { $like: 'w%' } } } } as unknown as QueryAST),
304305
).rejects.toThrow(/Unsupported filter operator "\$like"/);
305306
// #1058: an unbindable comparand.
306307
await expect(
307-
t.find('deal', { where: { $not: { amount: { $gt: { $field: 'budget' } } } } } as any),
308+
t.find('deal', { where: { $not: { amount: { $gt: { $field: 'budget' } } } } } as unknown as QueryAST),
308309
).rejects.toThrow(/Cross-field comparison is not supported/);
309310
// #1073: a non-node element of a nested logical array.
310-
await expect(t.find('deal', { where: { $not: { $or: [null] } } } as any)).rejects.toThrow(
311+
await expect(t.find('deal', { where: { $not: { $or: [null] } } } as unknown as QueryAST)).rejects.toThrow(
311312
/\$or\[0\] on 'deal'/,
312313
);
313314
});
@@ -329,7 +330,7 @@ describe('RemoteTransport $not (#1076)', () => {
329330
it('negates on count / deleteMany / updateMany', async () => {
330331
const { t, calls } = transportWithCapturingClient();
331332
const where = { $not: { stage: 'won' } };
332-
await t.count('deal', { where } as any);
333+
await t.count('deal', { where } as unknown as QueryAST);
333334
await t.deleteMany('deal', { where } as any);
334335
await t.updateMany('deal', { where } as any, { stage: 'lost' });
335336
expect(calls[0].sql).toMatch(/COUNT\(\*\).+WHERE NOT \("stage" = \?\)/i);
@@ -402,7 +403,7 @@ describe('TursoDriver remote — $not on real rows (#1076)', () => {
402403
});
403404

404405
const ids = async (where: unknown) =>
405-
((await driver.find('deal', { where } as any)) as any[]).map((r) => r.id).sort();
406+
((await driver.find('deal', { where } as unknown as QueryAST)) as any[]).map((r) => r.id).sort();
406407

407408
it('`$not: { stage: "won" }` returns the other rows, not a `no such column` error', async () => {
408409
// Pre-fix: threw before reaching SQLite; had it compiled, SQLite would have
@@ -476,14 +477,14 @@ describe('TursoDriver remote — $not on real rows (#1076)', () => {
476477
// Un-lowered, the transport (correctly) refuses `$between` and names a
477478
// lowering step that had been skipped for this depth.
478479
await expect(
479-
driver.find('deal', { where: { $not: { amount: { $between: [15, 35] } } } } as any),
480+
driver.find('deal', { object: 'deal', where: { $not: { amount: { $between: [15, 35] } } } }),
480481
).resolves.toBeDefined();
481482
expect(await ids({ $not: { amount: { $between: [15, 35] } } })).toEqual(['d_null', 'd_won']);
482483
});
483484

484485
it('`count` agrees with `find`', async () => {
485-
expect(await driver.count('deal', { where: { $not: { stage: 'won' } } } as any)).toBe(2);
486-
expect(await driver.count('deal', { where: { $not: {} } } as any)).toBe(0);
486+
expect(await driver.count('deal', { object: 'deal', where: { $not: { stage: 'won' } } })).toBe(2);
487+
expect(await driver.count('deal', { object: 'deal', where: { $not: {} } })).toBe(0);
487488
});
488489

489490
it('`deleteMany` with `$not: {}` deletes NOTHING', async () => {
@@ -504,7 +505,7 @@ describe('TursoDriver remote — $not on real rows (#1076)', () => {
504505
});
505506

506507
it('a non-node `$not` operand is refused through the driver too', async () => {
507-
await expect(driver.find('deal', { where: { $not: null } } as any)).rejects.toThrow(
508+
await expect(driver.find('deal', { where: { $not: null } } as unknown as QueryAST)).rejects.toThrow(
508509
/not a filter condition/,
509510
);
510511
});

packages/drivers/driver-turso/src/remote-transport-null-comparand-refusal.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest';
44
import { RemoteTransport } from './remote-transport.js';
55
import { TursoDriver } from './turso-driver.js';
66
import { makeLibsqlSqliteStub, type LibsqlSqliteStub } from './libsql-sqlite-stub.testkit.js';
7+
import type { QueryAST } from '@objectstack/spec/data';
78

89
/**
910
* Regression: `$null` takes a BOOLEAN, and a non-boolean is refused (#1116).
@@ -89,7 +90,7 @@ function transportWithCapturingClient() {
8990
async function refusalOf(where: unknown): Promise<WireBearingError> {
9091
const { t, calls } = transportWithCapturingClient();
9192
try {
92-
await t.find('deal', { where } as any);
93+
await t.find('deal', { where } as unknown as QueryAST);
9394
} catch (e) {
9495
// A refused filter must not have run a statement on the way to throwing.
9596
expect(calls).toEqual([]);
@@ -103,7 +104,7 @@ async function refusalOf(where: unknown): Promise<WireBearingError> {
103104
/** The SQL a `find` compiled to, plus its bind list. */
104105
async function compile(where: unknown): Promise<{ sql: string; args: any[] }> {
105106
const { t, calls } = transportWithCapturingClient();
106-
await t.find('deal', { where } as any);
107+
await t.find('deal', { where } as unknown as QueryAST);
107108
return calls[0];
108109
}
109110

@@ -226,11 +227,11 @@ describe('RemoteTransport $null comparand refusal (#1116)', () => {
226227
it('refuses on find / findOne / count / aggregate with NO statement executed', async () => {
227228
const { t, calls } = transportWithCapturingClient();
228229
const where = { stage: { $null: 'yes' } };
229-
await expect(t.find('deal', { where } as any)).rejects.toThrow(/requires a boolean comparand/);
230-
await expect(t.findOne('deal', { where } as any)).rejects.toThrow(/requires a boolean comparand/);
231-
await expect(t.count('deal', { where } as any)).rejects.toThrow(/requires a boolean comparand/);
230+
await expect(t.find('deal', { where } as unknown as QueryAST)).rejects.toThrow(/requires a boolean comparand/);
231+
await expect(t.findOne('deal', { where } as unknown as QueryAST)).rejects.toThrow(/requires a boolean comparand/);
232+
await expect(t.count('deal', { where } as unknown as QueryAST)).rejects.toThrow(/requires a boolean comparand/);
232233
await expect(
233-
t.aggregate('deal', { where, aggregations: [{ function: 'count' }] } as any),
234+
t.aggregate('deal', { where, aggregations: [{ function: 'count' }] } as unknown as QueryAST),
234235
).rejects.toThrow(/requires a boolean comparand/);
235236
expect(calls).toEqual([]);
236237
});
@@ -352,7 +353,7 @@ describe('TursoDriver remote — `$null` on rows (#1116)', () => {
352353
});
353354

354355
const ids = async (where: unknown): Promise<string[]> =>
355-
((await driver.find('deal', { where } as any)) as any[]).map((r) => String(r.id)).sort();
356+
((await driver.find('deal', { where } as unknown as QueryAST)) as any[]).map((r) => String(r.id)).sort();
356357

357358
const allRows = () =>
358359
stub.raw.prepare('SELECT id, stage FROM "deal" ORDER BY id').all() as Array<{
@@ -373,7 +374,7 @@ describe('TursoDriver remote — `$null` on rows (#1116)', () => {
373374
// Pre-fix every one of these came back as `["2"]`, i.e. the answer
374375
// `$null: true` gives, for a filter that never said `true`.
375376
const err = (await driver
376-
.find('deal', { where: { stage: { $null: value } } } as any)
377+
.find('deal', { where: { stage: { $null: value } } } as unknown as QueryAST)
377378
.catch((e) => e)) as WireBearingError;
378379
expect(err).toBeInstanceOf(Error);
379380
expect(err.code).toBe('INVALID_FILTER');

0 commit comments

Comments
 (0)