diff --git a/.changeset/date-bucket-parity-call-sites-drop-as-never.md b/.changeset/date-bucket-parity-call-sites-drop-as-never.md new file mode 100644 index 0000000000..ed809bde72 --- /dev/null +++ b/.changeset/date-bucket-parity-call-sites-drop-as-never.md @@ -0,0 +1,17 @@ +--- +"@objectstack/driver-turso": patch +--- + +test(verify): `checkDateBucketParity` / `checkReadCoercion` 的调用点不再 `as never`,替身的编译期检查恢复生效 (#6354) + +`@objectstack/verify` 用 `BucketableDriver` / `CoercibleDriver` 两个**结构替身**表达「被测驱动确实具备这组方法」。这是一个**已发布**的契约面——仓外驱动(cloud 的 `driver-turso`)照着它实现自己的一致性测试。但全仓 **10 个**调用点无一例外把驱动 `as never` 之后再传进去,于是这件事**一次也没有被检查过**:替身存在的全部意义,被 100% 的调用点关掉了。 + +本次逐处删掉这 10 个 cast,一个不留: + +- `packages/qa/dogfood/test/date-bucket-parity-conformance.test.ts` **6 处**(真实 `SqlDriver` / `SqliteWasmDriver` 1 处,负向控制的假驱动 5 处) +- `packages/drivers/driver-turso/src/date-bucket-parity.test.ts` **2 处**(`TursoDriver` 本地模式 + 那条 `week` 绊线) +- `packages/qa/dogfood/test/read-coercion-conformance.test.ts` **2 处**(`checkReadCoercion` 同族,同形且同样是死 cast) + +**零运行时改动,零新增逃逸口**——只删不加,全程未引入任何 `as any` / `as unknown as` / `@ts-expect-error` / `as never`。三个包 typecheck 全绿:这些 cast 每一个都是死的,替身与真实驱动的形状本来就一致,被抹掉的只是**说出这件事**的能力。 + +代价原本是休眠的,也正因为休眠才值得修:哪天某个驱动少掉替身要求的一个方法、或替身自身长出新成员,10 个调用点一个都不会红,`checkDateBucketParity` 会在运行期抛 `driver.aggregate is not a function`,而不是在 `tsc` 里被拦下。对仓外驱动作者而言,这个替身是他们唯一能对照的形状说明书,而说明书此前从不校验。 diff --git a/packages/drivers/driver-turso/src/date-bucket-parity.test.ts b/packages/drivers/driver-turso/src/date-bucket-parity.test.ts index 6ae25fe8c5..59ac080d29 100644 --- a/packages/drivers/driver-turso/src/date-bucket-parity.test.ts +++ b/packages/drivers/driver-turso/src/date-bucket-parity.test.ts @@ -40,7 +40,7 @@ describe('TursoDriver date-bucket parity (framework#3773)', () => { const driver = new TursoDriver({ url: ':memory:' }); expect(driver.transportMode).toBe('local'); - const problems = await checkDateBucketParity(driver as never, { + const problems = await checkDateBucketParity(driver, { createOptions: { bypassTenantAudit: true }, }); expect(problems).toEqual([]); @@ -125,7 +125,7 @@ describe('TursoDriver date-bucket parity (framework#3773)', () => { configurable: true, }); - const problems = await checkDateBucketParity(driver as never, { + const problems = await checkDateBucketParity(driver, { createOptions: { bypassTenantAudit: true }, }); expect(problems.join('\n')).toMatch(/advertises this granularity but aggregate\(\) threw/); diff --git a/packages/qa/dogfood/test/date-bucket-parity-conformance.test.ts b/packages/qa/dogfood/test/date-bucket-parity-conformance.test.ts index 45f2c38127..01feac2b6e 100644 --- a/packages/qa/dogfood/test/date-bucket-parity-conformance.test.ts +++ b/packages/qa/dogfood/test/date-bucket-parity-conformance.test.ts @@ -46,7 +46,7 @@ const DRIVERS = [ describe.each(DRIVERS)('date-bucket parity conformance: $name', ({ make }) => { it('buckets identically pushed-down and in-memory, on both storage forms', async () => { - const problems = await checkDateBucketParity(make() as never, { + const problems = await checkDateBucketParity(make(), { createOptions: { bypassTenantAudit: true }, }); expect(problems).toEqual([]); @@ -87,7 +87,7 @@ describe('checkDateBucketParity detects a driver whose SQL bucketing is wrong', } it('flags every advertised granularity on both columns', async () => { - const problems = await checkDateBucketParity(brokenDriver() as never); + const problems = await checkDateBucketParity(brokenDriver()); // 4 advertised granularities × 2 storage forms = 8 disagreements, and the // cross-column pass stays quiet because both columns are broken the same way. expect(problems).toHaveLength(8); @@ -102,7 +102,7 @@ describe('checkDateBucketParity detects a driver whose SQL bucketing is wrong', async aggregate() { throw new Error("dateGranularity 'month' not supported on dialect"); }, - }) as never, + }) ); expect(problems.join('\n')).toMatch(/advertises this granularity but aggregate\(\) threw/); }); @@ -124,7 +124,7 @@ describe('checkDateBucketParity detects a driver whose SQL bucketing is wrong', return [{ at: null, n: 8 }]; }, supports: { queryDateGranularity: { year: true } }, - }) as never, + }) ); expect(problems.join('\n')).toMatch(/Field\.datetime 'at' @ year/); expect(problems.join('\n')).toMatch( @@ -153,7 +153,7 @@ describe('checkDateBucketParity detects a driver whose SQL bucketing is wrong', ]; }, supports: { queryDateGranularity: { year: true } }, - }) as never, + }) ); expect(problems.join('\n')).toMatch(/pushed-down SQL and in-memory bucketing disagree/); expect(problems.join('\n')).toContain(sentinel); @@ -161,7 +161,7 @@ describe('checkDateBucketParity detects a driver whose SQL bucketing is wrong', it('stays quiet on a driver that advertises nothing', async () => { const problems = await checkDateBucketParity( - brokenDriver({ supports: { queryDateGranularity: {} } }) as never, + brokenDriver({ supports: { queryDateGranularity: {} } }) ); // Nothing advertised ⇒ the engine buckets everything in-memory ⇒ nothing to // disagree about. A driver is never faulted for declining. diff --git a/packages/qa/dogfood/test/read-coercion-conformance.test.ts b/packages/qa/dogfood/test/read-coercion-conformance.test.ts index af5d4b4426..302291ed08 100644 --- a/packages/qa/dogfood/test/read-coercion-conformance.test.ts +++ b/packages/qa/dogfood/test/read-coercion-conformance.test.ts @@ -37,7 +37,7 @@ const DRIVERS = [ describe.each(DRIVERS)('read-coercion conformance: $name', ({ make }) => { it('reads a stored row back as its declared types (boolean/json/number)', async () => { - const problems = await checkReadCoercion(make() as never); + const problems = await checkReadCoercion(make()); expect(problems).toEqual([]); }); }); @@ -53,7 +53,7 @@ describe('checkReadCoercion detects a non-coercing driver', () => { return [{ id: '1', name: 'Widget', active: 1, meta: '{"k":1,"arr":[1,2]}', count: '5' }]; }, }; - const problems = await checkReadCoercion(raw as never); + const problems = await checkReadCoercion(raw); expect(problems).toHaveLength(3); expect(problems.join('\n')).toMatch(/boolean not coerced/); expect(problems.join('\n')).toMatch(/json not coerced/);