Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions packages/spec/src/data/filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe('ComparisonOperatorSchema', () => {
// rather than an ISO refinement.
// ==========================================================================

describe('string comparands (#5685)', () => {
describe('string comparands — the ordering slots take a string, which is what the date macros emit', () => {
const OPS = ['$gt', '$gte', '$lt', '$lte'] as const;

/** `resolveFilterTokens` returns `asYmd(...)` for every calendar-day token. */
Expand Down Expand Up @@ -163,7 +163,7 @@ describe('ComparisonOperatorSchema', () => {
// `filter-comparand-shape.test.ts`.
// ==========================================================================

describe('null comparand (#14080)', () => {
describe('null comparand — refused in the four ordering slots, like a null list member', () => {
const OPS = ['$gt', '$gte', '$lt', '$lte'] as const;
const messagesOf = (result: { error?: { issues: Array<{ message: string }> } }): string =>
(result.error?.issues ?? []).map((i) => i.message).join('\n');
Expand Down Expand Up @@ -194,7 +194,7 @@ describe('ComparisonOperatorSchema', () => {
expect(nested.success).toBe(false);
});

it('the null PREDICATES are untouched — $eq: null / $ne: null keep parsing (#5332)', () => {
it('the null PREDICATES are untouched — $eq: null / $ne: null keep parsing', () => {
expect(FieldOperatorsSchema.safeParse({ $eq: null }).success).toBe(true);
expect(FieldOperatorsSchema.safeParse({ $ne: null }).success).toBe(true);
// The normalized AST is a logical group at its root (a bare field
Expand Down Expand Up @@ -256,7 +256,7 @@ describe('SetOperatorSchema', () => {
// on a missing prescription is not coverage of this ruling.
// ==========================================================================

describe('$field members are refused (#7596)', () => {
describe('$field members are refused', () => {
it('refuses a $field member of $in, naming the index and the alternative', () => {
const result = SetOperatorSchema.safeParse({ $in: ['won', { $field: 'budget' }] });
expect(result.success).toBe(false);
Expand Down Expand Up @@ -295,7 +295,7 @@ describe('SetOperatorSchema', () => {
expect(SetOperatorSchema.safeParse({ $in: [] }).success).toBe(true);
});

it('refuses a null member of $in / $nin — ruled 2026-08-31 (#13357)', () => {
it('refuses a null member of $in / $nin — ruled 2026-08-31', () => {
for (const op of ['$in', '$nin'] as const) {
const result = SetOperatorSchema.safeParse({ [op]: ['won', null] });
expect(result.success, op).toBe(false);
Expand Down Expand Up @@ -361,7 +361,7 @@ describe('RangeOperatorSchema', () => {
// parse verdicts on a declaration surface, not runtime refusals.)
// ==========================================================================

describe('string endpoints (#6571)', () => {
describe('string endpoints — `$between` takes the ISO and clock strings the platform produces', () => {
/**
* `resolveFilterTokens`' `walk` descends into arrays, and every branch of
* the resolver returns a string, so a token range resolves to two strings:
Expand Down Expand Up @@ -469,7 +469,7 @@ describe('RangeOperatorSchema', () => {
// was.
// ==========================================================================

describe('$field endpoints are refused (#7596)', () => {
describe('$field endpoints are refused', () => {
it('refuses a $field LOWER bound, naming index 0 and the alternative', () => {
const result = RangeOperatorSchema.safeParse({
$between: [{ $field: 'a.min' }, '2026-12-31'],
Expand Down Expand Up @@ -546,7 +546,7 @@ describe('RangeOperatorSchema', () => {
* always rejected both), and the pair above them is what moved — which is
* exactly the evidence that the two faces now agree.
*/
it('the whole-filter face refuses these field conditions too — #7711 (was green pre-#7711)', () => {
it('the whole-filter face refuses these field conditions too — green while the group branch was a non-strict catch-all', () => {
const withReference = NormalizedFilterSchema.safeParse({
$and: [{ amount: { $between: [{ $field: 'budget' }, 100] } }],
});
Expand All @@ -573,7 +573,7 @@ describe('RangeOperatorSchema', () => {
* is the SCALAR comparand, and it is also what the refusal above prescribes.
* If this went red the refusal message would be sending authors nowhere.
*/
it('leaves the four ORDERING slots taking a reference — #5222, and the prescribed alternative', () => {
it('leaves the four ORDERING slots taking a reference — a column-to-column comparison, and the prescribed alternative', () => {
expect(ComparisonOperatorSchema.safeParse({ $gt: { $field: 'budget' } }).success).toBe(true);
expect(FieldOperatorsSchema.safeParse({
$gte: { $field: 'a.min' }, $lte: { $field: 'a.max' },
Expand Down Expand Up @@ -605,7 +605,7 @@ describe('RangeOperatorSchema', () => {
// (`filter-comparand-shape.test.ts`).
// ==========================================================================

describe('blank $between endpoints are refused (#18012)', () => {
describe('blank $between endpoints are refused', () => {
const issuesOf = (result: { error?: { issues: Array<{ path: PropertyKey[]; message: string }> } }) =>
result.error?.issues ?? [];

Expand Down Expand Up @@ -676,7 +676,7 @@ describe('RangeOperatorSchema', () => {
* face about each slot, so both are refused on save, at the endpoint, in the
* sentence the enforced operator slot prints for the same pair.
*/
it('is refused by FilterConditionSchema too, at the endpoint, in the operator slot\'s words (#20116)', () => {
it('is refused by FilterConditionSchema too, at the endpoint, in the operator slot\'s words', () => {
const blank = FilterConditionSchema.safeParse({ age: { $between: [18, ''] } });
expect(blank.success).toBe(false);
expect(issuesOf(blank).map((i) => i.path)).toEqual([['age', '$between', 1]]);
Expand Down Expand Up @@ -1069,7 +1069,7 @@ describe('TypeScript Type System', () => {
* (the second reads `undefined`, not `never`: the old guard's `never` meets
* the slot's own `?`, and an optional `never` IS `undefined`.)
*/
it('accepts an ISO string on a Date field and orders string fields (#5685)', () => {
it('accepts an ISO string on a Date field and orders string fields', () => {
interface Deal {
close_date: Date; // resolved date macro arrives as 'YYYY-MM-DD'
shift_start: string; // Field.time — 'HH:MM[:SS[.fff]]'
Expand Down Expand Up @@ -1098,7 +1098,7 @@ describe('TypeScript Type System', () => {
* recorded: the old guard's `never` meets the slot's own `?`, and an optional
* `never` IS `undefined`.)
*/
it('accepts a resolved macro range on a Date field and ranges string fields (#6571)', () => {
it('accepts a resolved macro range on a Date field and ranges string fields', () => {
interface Deal {
close_date: Date; // a resolved token range arrives as two 'YYYY-MM-DD'
shift_start: string; // Field.time — 'HH:MM[:SS[.fff]]'
Expand Down Expand Up @@ -1459,7 +1459,7 @@ describe('NormalizedFilterSchema', () => {
// as well: the catch-all returned `{}` for the member it admitted, so the
// parse output no longer carried the condition it was asked about.
// ==========================================================================
describe('#7711 — a member the field-condition branch refuses has nowhere to land', () => {
describe('a member the field-condition branch refuses has nowhere to land', () => {
/** The card's clean control: `$null` has never been anything but a boolean. */
it('rejects a $and member whose operator map FieldOperatorsSchema refuses', () => {
const result = NormalizedFilterSchema.safeParse({
Expand Down Expand Up @@ -1538,7 +1538,7 @@ describe('NormalizedFilterSchema', () => {
* the field-condition branch as an empty record, which is what lets the
* group branch be `.strict()` without touching any of them.
*/
it('leaves the #5322 empty-combinator identities accepted', () => {
it('leaves the empty-combinator identities accepted — each reduces to its boolean unit', () => {
for (const identity of [{}, { $and: [] }, { $or: [] }, { $or: [{}] }, { $not: {} }]) {
expect(NormalizedFilterSchema.safeParse(identity).success).toBe(true);
}
Expand Down Expand Up @@ -1848,7 +1848,7 @@ describe('VALID_AST_OPERATORS', () => {
// corpus and the memory evaluator's pins — not here.
// ============================================================================

describe('FieldReferenceSchema.addDays (#14104)', () => {
describe('FieldReferenceSchema.addDays — a whole-day offset on a reference, an integer or another column', () => {
const firstIssue = (result: { error?: { issues: Array<{ code: string; path: PropertyKey[]; message: string }> } }) =>
result.error?.issues[0];

Expand Down Expand Up @@ -1959,7 +1959,7 @@ describe('FieldReferenceSchema.addDays (#14104)', () => {
expect(firstIssue(enforced)?.message.startsWith('addDays must be an integer or a { "$field" } reference')).toBe(true);
});

it('adds no LIST position — a reference carrying addDays is refused where a bare one is (#7596)', () => {
it('adds no LIST position — a reference carrying addDays is refused where a bare one is', () => {
const result = SetOperatorSchema.safeParse({ $in: [{ $field: 'due_date', addDays: 1 }] });
expect(result.success).toBe(false);
expect(firstIssue(result)?.path).toEqual(['$in', 0]);
Expand Down Expand Up @@ -1995,7 +1995,7 @@ describe('FieldReferenceSchema.addDays (#14104)', () => {
* and refuses `.` and `/` alike, so a slash-separated or unbackticked
* respelling trips it too.
*/
describe('filter.zod.ts docblock @examples (#16923)', () => {
describe('filter.zod.ts docblock @examples — a `$field` comparand names a column of the same row', () => {
const HERE = dirname(fileURLToPath(import.meta.url));
const SOURCE = readFileSync(resolve(HERE, 'filter.zod.ts'), 'utf8');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ const FORMS = [

// ────────────────────────────────────────────────────────────────────────────

describe('#11410 — `deleteBehavior` is never offered a value the schema refuses', () => {
describe('`deleteBehavior` is never offered a value the schema refuses', () => {
describe('the derived option source, which is why an explicit list is required', () => {
it('offers all three values, and defaults to the one a master_detail refuses', () => {
const node = derivedDeleteBehaviorNode();
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/src/data/form-return-type-options.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function formulaFieldWith(returnType: string): Record<string, unknown> {

// ────────────────────────────────────────────────────────────────────────────

describe('#19677 — `returnType` is never offered a value the schema refuses', () => {
describe('`returnType` is never offered a value the schema refuses', () => {
it('derives a non-empty closed member set from FieldSchema', () => {
// The floor for every assertion below: an empty or unreadable derivation
// would make the set-equality checks vacuously true.
Expand Down
8 changes: 4 additions & 4 deletions packages/spec/src/data/hook-body.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('HookBody', () => {
// #4391. These assertions are what must go red if any of them comes back
// WITHOUT an implementation behind it.

it('does not offer `crypto.hash` as a capability token (#4391)', () => {
it('does not offer `crypto.hash` as a capability token', () => {
expect(HookBodyCapability.options).toEqual([
'api.read',
'api.write',
Expand All @@ -73,7 +73,7 @@ describe('HookBody', () => {
expect(HookBodyCapability.options).not.toContain('crypto.hash');
});

it('rejects a body declaring `crypto.hash`, with the retirement prescription (#4391)', () => {
it('rejects a body declaring `crypto.hash`, with the retirement prescription', () => {
const r = ScriptBodySchema.safeParse({
language: 'js',
source: "ctx.input.fp = await ctx.crypto.hash('sha256', ctx.input.email);",
Expand All @@ -90,7 +90,7 @@ describe('HookBody', () => {
expect(message).toMatch(/Delete the capability/s);
});

it('still accepts `crypto.uuid` — the sibling that IS implemented (#4391)', () => {
it('still accepts `crypto.uuid` — the sibling that IS implemented', () => {
const r = ScriptBodySchema.safeParse({
language: 'js',
source: 'ctx.input.trace = ctx.crypto.randomUUID();',
Expand All @@ -99,7 +99,7 @@ describe('HookBody', () => {
expect(r.success).toBe(true);
});

it("gives an UNKNOWN token zod's own message, not the retirement one (#4391)", () => {
it("gives an UNKNOWN token zod's own message, not the retirement one", () => {
// Only the value that used to be legal gets "was removed" — telling the
// author of a typo that their token was retired would misinform.
const r = ScriptBodySchema.safeParse({
Expand Down
20 changes: 10 additions & 10 deletions packages/spec/src/data/hook.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('HookEvent', () => {
});
});

describe('Removed non-dispatched events (#3195)', () => {
describe('Removed non-dispatched events', () => {
it('should reject per-method read and *Many events that the engine never dispatched', () => {
// These were declared but never fired; the engine only ever triggers the
// 8 events above. Removed rather than left as silent no-ops — read
Expand Down Expand Up @@ -349,7 +349,7 @@ describe('HookSchema', () => {
// that the VALUE set is closed (a non-member is a VALUE error located at
// `runAs`, never a top-level unrecognized key).
// ---------------------------------------------------------------------------
describe('runAs (#14010)', () => {
describe('runAs — a hook may run as `system` or `user`, and inherits by default', () => {
const base = {
name: 'stamp_grade',
object: 'account',
Expand Down Expand Up @@ -526,7 +526,7 @@ describe('HookContextSchema', () => {
// it — a parsed context would lose the key and every guard downstream would
// read undefined. The populate and sandbox halves are pinned where they can
// execute (objectql's cascade suite; runtime's QuickJS integration pin).
describe('Referential-Cleanup Marker (#13644)', () => {
describe('Referential-Cleanup Marker — declared, and true only on a reference-cleanup write by the engine', () => {
it('keeps referentialFieldClear through a parse, typed — the guard idiom evaluates on the parsed value', () => {
const context = HookContextSchema.parse({
object: 'note',
Expand Down Expand Up @@ -724,7 +724,7 @@ describe('HookContextSchema', () => {
// #3280 made `organizationId` the blessed developer-facing name; the
// `tenantId` alias was removed from this surface in v16 (#3290). A stray
// `tenantId` key is now stripped by the schema rather than surfaced.
it('exposes session.organizationId and no longer carries the removed tenantId alias (#3290)', () => {
it('exposes session.organizationId and no longer carries the removed tenantId alias', () => {
const context = HookContextSchema.parse({
object: 'account',
event: 'beforeInsert',
Expand Down Expand Up @@ -921,7 +921,7 @@ describe('HookSchema - condition property', () => {
// defineHook factory (#4269)
// ============================================================================

describe('defineHook (#4269)', () => {
describe('defineHook — the authoring factory, so a hook is validated where it is written', () => {
const config: Hook = {
name: 'order_guard',
object: 'order',
Expand Down Expand Up @@ -1003,7 +1003,7 @@ describe('defineHook (#4269)', () => {
* TS2578 "Unused '@ts-expect-error' directive" on the two directives below.
* Direction predicted before running it, and that is what it did.
*/
describe('session.roles retirement (#5050, ADR-0049)', () => {
describe('session.roles retirement (ADR-0049)', () => {
it('REJECTS an authored `roles`, with the prescription in the message', () => {
expect(() =>
HookContextSchema.parse({
Expand Down Expand Up @@ -1131,7 +1131,7 @@ describe('session.roles retirement (#5050, ADR-0049)', () => {
* `session.positions.includes(...)` as an access check is exactly what the
* `roles` tombstone above was written to stop.
*/
describe('session.positions / session.preserveAudit declaration (#5605)', () => {
describe('session.positions / session.preserveAudit declaration', () => {
it('PRESERVES `positions` through a parse instead of stripping it', () => {
const context = HookContextSchema.parse({
object: 'account',
Expand All @@ -1145,7 +1145,7 @@ describe('session.positions / session.preserveAudit declaration (#5605)', () =>
expect(context.session?.positions).toEqual(['sales_manager', 'org_admin']);
});

it('PRESERVES `preserveAudit` through a parse (#3493 has a live consumer)', () => {
it('PRESERVES `preserveAudit` through a parse (the opt-in a historical import uses to keep its audit stamps)', () => {
const context = HookContextSchema.parse({
object: 'account',
event: 'beforeInsert',
Expand Down Expand Up @@ -1283,7 +1283,7 @@ describe('session.positions / session.preserveAudit declaration (#5605)', () =>
* because a green companion test read as a pin is how #5605's third assertion
* nearly got over-credited.
*/
describe('HookContext.api typing (#5945)', () => {
describe('HookContext.api typing — the minimum scoped context the docs teach: `object()` and `transaction()`', () => {
/** Shaped like ObjectQL's `ScopedContext` — a live object, not authored data. */
const liveApi = {
object: (_name: string) => ({
Expand Down Expand Up @@ -1379,7 +1379,7 @@ describe('HookContext.api typing (#5945)', () => {
// old `timeout` is a retiredKey tombstone on this strict shape, so the
// rejection carries the RENAME (the prescription is the payload) rather than a
// bare unrecognized-key error, and the value survives at the same magnitude.
describe('hook.timeout → hook.timeoutMs (#14478, ADR-0087 `hook-timeout-to-timeout-ms`)', () => {
describe('hook.timeout → hook.timeoutMs (ADR-0087 `hook-timeout-to-timeout-ms`)', () => {
const base = { name: 'audit_order', object: 'order', events: ['afterInsert' as const], handler: 'auditOrder' };

it('REFUSES the retired `timeout` spelling with the rename in the message', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/spec/src/data/import-coercion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from './import-coercion';
import { REFERENCE_VALUE_TYPES } from './field-value.zod';

describe('import boolean tokens (#4173)', () => {
describe('import boolean tokens — one table for the server coercion and the Import Wizard preview', () => {
it('both sets are non-empty and disjoint', () => {
expect(IMPORT_BOOLEAN_TRUE_TOKENS.size).toBeGreaterThan(0);
expect(IMPORT_BOOLEAN_FALSE_TOKENS.size).toBeGreaterThan(0);
Expand All @@ -45,7 +45,7 @@ describe('import boolean tokens (#4173)', () => {
});
});

describe('import reference types (#4173)', () => {
describe('import reference types — exported from spec, not copied at each consumer', () => {
it('is the reference value types plus the legacy generic spelling, exactly', () => {
expect([...IMPORT_REFERENCE_TYPES].sort()).toEqual(
[...REFERENCE_VALUE_TYPES, 'reference'].sort(),
Expand Down
4 changes: 2 additions & 2 deletions packages/spec/src/data/import-mapping-target.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ describe('unknownImportMappingTargets', () => {
)).toEqual([]);
});

it('[#20149] is empty for a mapping that writes an address by its declared parts — every transform, split elements too', () => {
it('is empty for a mapping that writes an address by its declared parts — every transform, split elements too', () => {
expect(unknownImportMappingTargets(
[
{ source: 'Name', target: 'full_name' },
Expand All @@ -221,7 +221,7 @@ describe('unknownImportMappingTargets', () => {
)).toEqual([]);
});

it('[#20149] refuses a part of a field the same mapping also writes whole, naming where the whole value is written', () => {
it('refuses a part of a field the same mapping also writes whole, naming where the whole value is written', () => {
const misses = unknownImportMappingTargets(
[
{ source: 'Street', target: 'mailing_address.street' },
Expand Down
Loading
Loading