From 6828fb8074d8e5960d870e8ef0e53def624f4627 Mon Sep 17 00:00:00 2001 From: EmaToplek Date: Wed, 26 Aug 2026 10:57:52 +0200 Subject: [PATCH 1/5] test: add coverage for entity-kinds.ts index/check hydration branches Signed-off-by: EmaToplek --- .../1-core/contract/test/entity-kinds.test.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts index 3fd6f4223194..a849716562bb 100644 --- a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts +++ b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts @@ -1,8 +1,13 @@ import { hydrateNamespaceEntities, UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; +import { parseNaming } from '@internal/sql-schema-ir/naming'; import { describe, expect, it } from 'vitest'; import { composeSqlEntityKinds, tableEntityKind, valueSetEntityKind } from '../src/entity-kinds'; +import { CheckConstraint } from '../src/ir/check-constraint'; +import { Index } from '../src/ir/sql-index'; import { StorageTable } from '../src/ir/storage-table'; import { StorageValueSet } from '../src/ir/storage-value-set'; +import type { SerializedCheckConstraint } from '../src/serialized-check-constraint'; +import type { SerializedIndex } from '../src/serialized-index'; const emptyTableInput = { columns: {}, @@ -102,3 +107,65 @@ describe('hydrateNamespaceEntities with SQL kinds (carry)', () => { expect(result[UNBOUND_NAMESPACE_ID]).toBeDefined(); }); }); + +describe('tableEntityKind — construct index/check hydration', () => { + it('passes through indexes that are already Index instances unchanged', () => { + const idx = new Index({ + naming: parseNaming('idx_users_email', undefined), + columns: ['email'], + where: undefined, + unique: true, + type: undefined, + options: undefined, + }); + const result = tableEntityKind.construct({ + ...emptyTableInput, + indexes: [idx], + }); + expect(result.indexes).toEqual([idx]); + }); + + it('hydrates serialized indexes via indexInputFromSerialized', () => { + const serialized: SerializedIndex = { + name: 'idx_users_name', + unique: false, + columns: ['name'], + }; + const result = tableEntityKind.construct({ + ...emptyTableInput, + indexes: [serialized], + }); + expect(result.indexes[0]).toBeInstanceOf(Index); + expect(result.indexes[0]?.name).toBe('idx_users_name'); + }); + + it('passes through checks that are already CheckConstraint instances unchanged', () => { + const check = new CheckConstraint({ + naming: parseNaming('chk_users_age', undefined), + expression: 'age >= 0', + }); + const result = tableEntityKind.construct({ + ...emptyTableInput, + checks: [check], + }); + expect(result.checks).toEqual([check]); + }); + + it('hydrates serialized checks via checkConstraintInputFromSerialized', () => { + const serialized: SerializedCheckConstraint = { + name: 'chk_users_email', + expression: "email <> ''", + }; + const result = tableEntityKind.construct({ + ...emptyTableInput, + checks: [serialized], + }); + expect(result.checks?.[0]).toBeInstanceOf(CheckConstraint); + expect(result.checks?.[0]?.name).toBe('chk_users_email'); + }); + + it('omits checks entirely when the input has none', () => { + const result = tableEntityKind.construct(emptyTableInput); + expect(result.checks).toBeUndefined(); + }); +}); From 7d2988e1a3f8bd674c057e8bd136d6f14e538840 Mon Sep 17 00:00:00 2001 From: EmaToplek Date: Mon, 14 Sep 2026 22:58:40 +0200 Subject: [PATCH 2/5] test: assert instance identity for pass-through indexes and checks Signed-off-by: EmaToplek --- packages/2-sql/1-core/contract/test/entity-kinds.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts index a849716562bb..1125846b1c9c 100644 --- a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts +++ b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts @@ -123,6 +123,7 @@ describe('tableEntityKind — construct index/check hydration', () => { indexes: [idx], }); expect(result.indexes).toEqual([idx]); + expect(result.indexes[0]).toBe(idx); }); it('hydrates serialized indexes via indexInputFromSerialized', () => { @@ -149,6 +150,7 @@ describe('tableEntityKind — construct index/check hydration', () => { checks: [check], }); expect(result.checks).toEqual([check]); + expect(result.checks?.[0]).toBe(check); }); it('hydrates serialized checks via checkConstraintInputFromSerialized', () => { From d8a33490d664ba461c78c17c9ad3a3a85d4b782b Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 20:11:52 +0200 Subject: [PATCH 3/5] test: compare the whole hydrated index and check nodes The two tests for stored records checked only the class and the name, and the name passes through hydration unchanged. They would still pass if hydration dropped columns, unique, the expression or the stored prefix. Both tests now use wire-named records and compare the hydrated array with toStrictEqual against the node built directly (the index() factory for the index, the CheckConstraint constructor for the check). Dropping any of those fields makes the matching test fail. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../1-core/contract/test/entity-kinds.test.ts | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts index 1125846b1c9c..6f98039dc811 100644 --- a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts +++ b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts @@ -2,6 +2,7 @@ import { hydrateNamespaceEntities, UNBOUND_NAMESPACE_ID } from '@internal/framew import { parseNaming } from '@internal/sql-schema-ir/naming'; import { describe, expect, it } from 'vitest'; import { composeSqlEntityKinds, tableEntityKind, valueSetEntityKind } from '../src/entity-kinds'; +import { index } from '../src/factories'; import { CheckConstraint } from '../src/ir/check-constraint'; import { Index } from '../src/ir/sql-index'; import { StorageTable } from '../src/ir/storage-table'; @@ -128,16 +129,18 @@ describe('tableEntityKind — construct index/check hydration', () => { it('hydrates serialized indexes via indexInputFromSerialized', () => { const serialized: SerializedIndex = { - name: 'idx_users_name', - unique: false, - columns: ['name'], + name: 'users_email_idx_ab12cd34', + prefix: 'users_email_idx', + columns: ['email'], + unique: true, }; const result = tableEntityKind.construct({ ...emptyTableInput, indexes: [serialized], }); - expect(result.indexes[0]).toBeInstanceOf(Index); - expect(result.indexes[0]?.name).toBe('idx_users_name'); + expect(result.indexes).toStrictEqual([ + index('users_email_idx_ab12cd34', ['email'], { prefix: 'users_email_idx', unique: true }), + ]); }); it('passes through checks that are already CheckConstraint instances unchanged', () => { @@ -155,15 +158,20 @@ describe('tableEntityKind — construct index/check hydration', () => { it('hydrates serialized checks via checkConstraintInputFromSerialized', () => { const serialized: SerializedCheckConstraint = { - name: 'chk_users_email', + name: 'users_email_check_ab12cd34', + prefix: 'users_email_check', expression: "email <> ''", }; const result = tableEntityKind.construct({ ...emptyTableInput, checks: [serialized], }); - expect(result.checks?.[0]).toBeInstanceOf(CheckConstraint); - expect(result.checks?.[0]?.name).toBe('chk_users_email'); + expect(result.checks).toStrictEqual([ + new CheckConstraint({ + naming: { kind: 'wire', prefix: 'users_email_check', hash: 'ab12cd34' }, + expression: "email <> ''", + }), + ]); }); it('omits checks entirely when the input has none', () => { From 2736e71091316e5dabd40ddc656549cd73131b5c Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 20:12:01 +0200 Subject: [PATCH 4/5] test: remove the absent-checks test that entity-kinds.ts cannot fail The StorageTable constructor already drops checks when they are undefined or empty, so this test passes whatever tableEntityKind.construct does with checks. check-constraint.test.ts already covers that constructor behaviour, and the existing construct(emptyTableInput) test already runs the no-checks branch. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- packages/2-sql/1-core/contract/test/entity-kinds.test.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts index 6f98039dc811..3776454c079c 100644 --- a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts +++ b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts @@ -173,9 +173,4 @@ describe('tableEntityKind — construct index/check hydration', () => { }), ]); }); - - it('omits checks entirely when the input has none', () => { - const result = tableEntityKind.construct(emptyTableInput); - expect(result.checks).toBeUndefined(); - }); }); From 968b0b8649d136578e889c9b31fdcf90226e99fe Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 20:12:44 +0200 Subject: [PATCH 5/5] test: group the hydration tests under tableEntityKind and build nodes with factories The new tests had their own top-level describe for the same subject. They now sit inside the existing tableEntityKind describe, grouped by input form: stored records from contract.json, and nodes that are already built. Titles say what happens instead of naming internal helpers. The pass-through Index is built with the index() factory, and the check uses an exact naming literal, so the parseNaming and Index imports go. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../1-core/contract/test/entity-kinds.test.ts | 129 +++++++++--------- 1 file changed, 61 insertions(+), 68 deletions(-) diff --git a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts index 3776454c079c..a516f6094d75 100644 --- a/packages/2-sql/1-core/contract/test/entity-kinds.test.ts +++ b/packages/2-sql/1-core/contract/test/entity-kinds.test.ts @@ -1,10 +1,8 @@ import { hydrateNamespaceEntities, UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; -import { parseNaming } from '@internal/sql-schema-ir/naming'; import { describe, expect, it } from 'vitest'; import { composeSqlEntityKinds, tableEntityKind, valueSetEntityKind } from '../src/entity-kinds'; import { index } from '../src/factories'; import { CheckConstraint } from '../src/ir/check-constraint'; -import { Index } from '../src/ir/sql-index'; import { StorageTable } from '../src/ir/storage-table'; import { StorageValueSet } from '../src/ir/storage-value-set'; import type { SerializedCheckConstraint } from '../src/serialized-check-constraint'; @@ -24,6 +22,67 @@ describe('tableEntityKind', () => { const result = tableEntityKind.construct(emptyTableInput); expect(result).toBeInstanceOf(StorageTable); }); + + describe('given stored records from contract.json', () => { + it('hydrates a stored index record into an Index node', () => { + const serialized: SerializedIndex = { + name: 'users_email_idx_ab12cd34', + prefix: 'users_email_idx', + columns: ['email'], + unique: true, + }; + const result = tableEntityKind.construct({ + ...emptyTableInput, + indexes: [serialized], + }); + expect(result.indexes).toStrictEqual([ + index('users_email_idx_ab12cd34', ['email'], { prefix: 'users_email_idx', unique: true }), + ]); + }); + + it('hydrates a stored check record into a CheckConstraint node', () => { + const serialized: SerializedCheckConstraint = { + name: 'users_email_check_ab12cd34', + prefix: 'users_email_check', + expression: "email <> ''", + }; + const result = tableEntityKind.construct({ + ...emptyTableInput, + checks: [serialized], + }); + expect(result.checks).toStrictEqual([ + new CheckConstraint({ + naming: { kind: 'wire', prefix: 'users_email_check', hash: 'ab12cd34' }, + expression: "email <> ''", + }), + ]); + }); + }); + + describe('given IR nodes from authoring or an earlier hydration pass', () => { + it('keeps an existing Index node as the same instance', () => { + const idx = index('idx_users_email', ['email'], { unique: true }); + const result = tableEntityKind.construct({ + ...emptyTableInput, + indexes: [idx], + }); + expect(result.indexes).toEqual([idx]); + expect(result.indexes[0]).toBe(idx); + }); + + it('keeps an existing CheckConstraint node as the same instance', () => { + const check = new CheckConstraint({ + naming: { kind: 'exact', name: 'chk_users_age' }, + expression: 'age >= 0', + }); + const result = tableEntityKind.construct({ + ...emptyTableInput, + checks: [check], + }); + expect(result.checks).toEqual([check]); + expect(result.checks?.[0]).toBe(check); + }); + }); }); describe('valueSetEntityKind', () => { @@ -108,69 +167,3 @@ describe('hydrateNamespaceEntities with SQL kinds (carry)', () => { expect(result[UNBOUND_NAMESPACE_ID]).toBeDefined(); }); }); - -describe('tableEntityKind — construct index/check hydration', () => { - it('passes through indexes that are already Index instances unchanged', () => { - const idx = new Index({ - naming: parseNaming('idx_users_email', undefined), - columns: ['email'], - where: undefined, - unique: true, - type: undefined, - options: undefined, - }); - const result = tableEntityKind.construct({ - ...emptyTableInput, - indexes: [idx], - }); - expect(result.indexes).toEqual([idx]); - expect(result.indexes[0]).toBe(idx); - }); - - it('hydrates serialized indexes via indexInputFromSerialized', () => { - const serialized: SerializedIndex = { - name: 'users_email_idx_ab12cd34', - prefix: 'users_email_idx', - columns: ['email'], - unique: true, - }; - const result = tableEntityKind.construct({ - ...emptyTableInput, - indexes: [serialized], - }); - expect(result.indexes).toStrictEqual([ - index('users_email_idx_ab12cd34', ['email'], { prefix: 'users_email_idx', unique: true }), - ]); - }); - - it('passes through checks that are already CheckConstraint instances unchanged', () => { - const check = new CheckConstraint({ - naming: parseNaming('chk_users_age', undefined), - expression: 'age >= 0', - }); - const result = tableEntityKind.construct({ - ...emptyTableInput, - checks: [check], - }); - expect(result.checks).toEqual([check]); - expect(result.checks?.[0]).toBe(check); - }); - - it('hydrates serialized checks via checkConstraintInputFromSerialized', () => { - const serialized: SerializedCheckConstraint = { - name: 'users_email_check_ab12cd34', - prefix: 'users_email_check', - expression: "email <> ''", - }; - const result = tableEntityKind.construct({ - ...emptyTableInput, - checks: [serialized], - }); - expect(result.checks).toStrictEqual([ - new CheckConstraint({ - naming: { kind: 'wire', prefix: 'users_email_check', hash: 'ab12cd34' }, - expression: "email <> ''", - }), - ]); - }); -});