From 257f17851068827e8a2fea3639504a6358ece100 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 12:54:44 +0200 Subject: [PATCH 01/12] test: scope multi-package describes with beforeAll/afterAll to free RAM Top-level `await` inside `describe` built every result during discovery and pinned it via closure for the file's lifetime, causing OOM during sql-on-fhir.test.ts on ubuntu-latest. Move generation into `beforeAll` and null out the result in `afterAll` (+ Bun.gc) so each section's ~tens-of-MB of in-memory generated files can be reclaimed between sections. --- .../write-generator/multi-package/cda.test.ts | 69 +++++++---- .../multi-package/local-package.test.ts | 108 ++++++++++++------ .../multi-package/sql-on-fhir.test.ts | 69 +++++++---- 3 files changed, 168 insertions(+), 78 deletions(-) diff --git a/test/api/write-generator/multi-package/cda.test.ts b/test/api/write-generator/multi-package/cda.test.ts index cb4c73aa..ba6dc2eb 100644 --- a/test/api/write-generator/multi-package/cda.test.ts +++ b/test/api/write-generator/multi-package/cda.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, it } from "bun:test"; -import { APIBuilder } from "@root/api/builder"; +import { afterAll, beforeAll, describe, expect, it } from "bun:test"; +import { APIBuilder, type GenerationReport } from "@root/api/builder"; import type { CanonicalUrl } from "@root/typeschema/types"; import { mkSilentLogger } from "@typeschema-test/utils"; @@ -7,7 +7,7 @@ import { mkSilentLogger } from "@typeschema-test/utils"; * Tests for CDA package generation. * Package: hl7.cda.uv.core@2.0.1-sd */ -describe("CDA", async () => { +describe("CDA", () => { const treeShakeConfig = { "hl7.cda.uv.core": { "http://hl7.org/cda/stds/core/StructureDefinition/ClinicalDocument": {}, @@ -18,12 +18,21 @@ describe("CDA", async () => { "hl7.cda.uv.core": ["http://hl7.org/cda/stds/core/StructureDefinition/ClinicalDocument" as CanonicalUrl], }; - describe("TypeScript Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackage("hl7.cda.uv.core", "2.0.1-sd") - .typeSchema({ treeShake: treeShakeConfig }) - .typescript({ inMemoryOnly: true }) - .generate(); + describe("TypeScript Generation", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackage("hl7.cda.uv.core", "2.0.1-sd") + .typeSchema({ treeShake: treeShakeConfig }) + .typescript({ inMemoryOnly: true }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -45,12 +54,21 @@ describe("CDA", async () => { }); }); - describe("Python Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackage("hl7.cda.uv.core", "2.0.1-sd") - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .python({ inMemoryOnly: true }) - .generate(); + describe("Python Generation", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackage("hl7.cda.uv.core", "2.0.1-sd") + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .python({ inMemoryOnly: true }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -68,12 +86,21 @@ describe("CDA", async () => { }); }); - describe("C# Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackage("hl7.cda.uv.core", "2.0.1-sd") - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .csharp({ inMemoryOnly: true }) - .generate(); + describe("C# Generation", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackage("hl7.cda.uv.core", "2.0.1-sd") + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .csharp({ inMemoryOnly: true }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); diff --git a/test/api/write-generator/multi-package/local-package.test.ts b/test/api/write-generator/multi-package/local-package.test.ts index 5b13c3e2..b5e018a5 100644 --- a/test/api/write-generator/multi-package/local-package.test.ts +++ b/test/api/write-generator/multi-package/local-package.test.ts @@ -1,6 +1,6 @@ -import { describe, expect, it } from "bun:test"; +import { afterAll, beforeAll, describe, expect, it } from "bun:test"; import * as Path from "node:path"; -import { APIBuilder } from "@root/api/builder"; +import { APIBuilder, type GenerationReport } from "@root/api/builder"; import type { CanonicalUrl } from "@root/typeschema/types"; import { mkSilentLogger } from "@typeschema-test/utils"; @@ -9,7 +9,7 @@ const LOCAL_PACKAGE_PATH = Path.join(__dirname, "../../../../examples/local-pack /** * Tests for local package folder functionality with multi-package dependency resolution. * */ -describe("Local Package Folder - Multi-Package Generation", async () => { +describe("Local Package Folder - Multi-Package Generation", () => { const localPackageConfig = { package: { name: "example.folder.structures", version: "0.0.1" }, path: LOCAL_PACKAGE_PATH, @@ -29,12 +29,21 @@ describe("Local Package Folder - Multi-Package Generation", async () => { "example.folder.structures": ["http://example.org/fhir/StructureDefinition/ExampleNotebook" as CanonicalUrl], }; - describe("TypeScript Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .localStructureDefinitions(localPackageConfig) - .typeSchema({ treeShake: treeShakeConfig }) - .typescript({ inMemoryOnly: true }) - .generate(); + describe("TypeScript Generation", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .localStructureDefinitions(localPackageConfig) + .typeSchema({ treeShake: treeShakeConfig }) + .typescript({ inMemoryOnly: true }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -62,22 +71,31 @@ describe("Local Package Folder - Multi-Package Generation", async () => { }); }); - describe("TypeScript Generation with type-discriminated profile", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .localStructureDefinitions(localPackageConfig) - .typeSchema({ - treeShake: { - "example.folder.structures": { - "http://example.org/fhir/StructureDefinition/ExampleTypedBundle": {}, + describe("TypeScript Generation with type-discriminated profile", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .localStructureDefinitions(localPackageConfig) + .typeSchema({ + treeShake: { + "example.folder.structures": { + "http://example.org/fhir/StructureDefinition/ExampleTypedBundle": {}, + }, + "hl7.fhir.r4.core": { + "http://hl7.org/fhir/StructureDefinition/Patient": {}, + "http://hl7.org/fhir/StructureDefinition/Organization": {}, + }, }, - "hl7.fhir.r4.core": { - "http://hl7.org/fhir/StructureDefinition/Patient": {}, - "http://hl7.org/fhir/StructureDefinition/Organization": {}, - }, - }, - }) - .typescript({ inMemoryOnly: true, generateProfile: true, withDebugComment: false }) - .generate(); + }) + .typescript({ inMemoryOnly: true, generateProfile: true, withDebugComment: false }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -93,12 +111,21 @@ describe("Local Package Folder - Multi-Package Generation", async () => { }); }); - describe("Python Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .localStructureDefinitions(localPackageConfig) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .python({ inMemoryOnly: true }) - .generate(); + describe("Python Generation", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .localStructureDefinitions(localPackageConfig) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .python({ inMemoryOnly: true }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -129,12 +156,21 @@ describe("Local Package Folder - Multi-Package Generation", async () => { }); }); - describe("C# Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .localStructureDefinitions(localPackageConfig) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .csharp({ inMemoryOnly: true }) - .generate(); + describe("C# Generation", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .localStructureDefinitions(localPackageConfig) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .csharp({ inMemoryOnly: true }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); diff --git a/test/api/write-generator/multi-package/sql-on-fhir.test.ts b/test/api/write-generator/multi-package/sql-on-fhir.test.ts index 0c2bd722..eb5f2ad8 100644 --- a/test/api/write-generator/multi-package/sql-on-fhir.test.ts +++ b/test/api/write-generator/multi-package/sql-on-fhir.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, it } from "bun:test"; -import { APIBuilder } from "@root/api/builder"; +import { afterAll, beforeAll, describe, expect, it } from "bun:test"; +import { APIBuilder, type GenerationReport } from "@root/api/builder"; import type { CanonicalUrl } from "@root/typeschema/types"; import { mkSilentLogger } from "@typeschema-test/utils"; @@ -7,7 +7,7 @@ import { mkSilentLogger } from "@typeschema-test/utils"; * Tests for SQL-on-FHIR package. * The SQL-on-FHIR package depends on hl7.fhir.r5.core. */ -describe("SQL-on-FHIR", async () => { +describe("SQL-on-FHIR", () => { const packageUrl = "https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/package.tgz"; const treeShakeConfig = { @@ -20,12 +20,21 @@ describe("SQL-on-FHIR", async () => { "org.sql-on-fhir.ig": ["https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition" as CanonicalUrl], }; - describe("TypeScript Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackageRef(packageUrl) - .typeSchema({ treeShake: treeShakeConfig }) - .typescript({ inMemoryOnly: true }) - .generate(); + describe("TypeScript Generation", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackageRef(packageUrl) + .typeSchema({ treeShake: treeShakeConfig }) + .typescript({ inMemoryOnly: true }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -55,12 +64,21 @@ describe("SQL-on-FHIR", async () => { }); }); - describe("Python Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackageRef(packageUrl) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .python({ inMemoryOnly: true }) - .generate(); + describe("Python Generation", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackageRef(packageUrl) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .python({ inMemoryOnly: true }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -85,12 +103,21 @@ describe("SQL-on-FHIR", async () => { }); }); - describe("C# Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackageRef(packageUrl) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .csharp({ inMemoryOnly: true }) - .generate(); + describe("C# Generation", () => { + let result: GenerationReport; + + beforeAll(async () => { + result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackageRef(packageUrl) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .csharp({ inMemoryOnly: true }) + .generate(); + }); + + afterAll(() => { + result = undefined as unknown as GenerationReport; + Bun.gc(true); + }); it("should succeed", () => { expect(result.success).toBeTrue(); From 3a31e7cbc6080ac1067f4d03d68ce2ef1e4e97dd Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 13:07:44 +0200 Subject: [PATCH 02/12] test: bump SQL-on-FHIR beforeAll timeout to 180s The previous commit moved generation from describe-discovery (no timeout) into `beforeAll`, which inherits bunfig's `timeout = 30000`. SQL-on-FHIR fetches `https://build.fhir.org/.../package.tgz` on cold cache from GH runners and routinely exceeds 30s; the hook timed out, got retried, and OOM-killed the suite. --- test/api/write-generator/multi-package/sql-on-fhir.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/api/write-generator/multi-package/sql-on-fhir.test.ts b/test/api/write-generator/multi-package/sql-on-fhir.test.ts index eb5f2ad8..30907e12 100644 --- a/test/api/write-generator/multi-package/sql-on-fhir.test.ts +++ b/test/api/write-generator/multi-package/sql-on-fhir.test.ts @@ -29,7 +29,7 @@ describe("SQL-on-FHIR", () => { .typeSchema({ treeShake: treeShakeConfig }) .typescript({ inMemoryOnly: true }) .generate(); - }); + }, 180_000); afterAll(() => { result = undefined as unknown as GenerationReport; @@ -73,7 +73,7 @@ describe("SQL-on-FHIR", () => { .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) .python({ inMemoryOnly: true }) .generate(); - }); + }, 180_000); afterAll(() => { result = undefined as unknown as GenerationReport; @@ -112,7 +112,7 @@ describe("SQL-on-FHIR", () => { .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) .csharp({ inMemoryOnly: true }) .generate(); - }); + }, 180_000); afterAll(() => { result = undefined as unknown as GenerationReport; From 7d31c921ecbd16890ac58ad40a522a5cda5de09a Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 13:11:20 +0200 Subject: [PATCH 03/12] Revert "test: scope multi-package describes with beforeAll/afterAll to free RAM" This reverts commit 257f17851068827e8a2fea3639504a6358ece100. --- .../write-generator/multi-package/cda.test.ts | 69 ++++------- .../multi-package/local-package.test.ts | 108 ++++++------------ .../multi-package/sql-on-fhir.test.ts | 69 ++++------- 3 files changed, 78 insertions(+), 168 deletions(-) diff --git a/test/api/write-generator/multi-package/cda.test.ts b/test/api/write-generator/multi-package/cda.test.ts index ba6dc2eb..cb4c73aa 100644 --- a/test/api/write-generator/multi-package/cda.test.ts +++ b/test/api/write-generator/multi-package/cda.test.ts @@ -1,5 +1,5 @@ -import { afterAll, beforeAll, describe, expect, it } from "bun:test"; -import { APIBuilder, type GenerationReport } from "@root/api/builder"; +import { describe, expect, it } from "bun:test"; +import { APIBuilder } from "@root/api/builder"; import type { CanonicalUrl } from "@root/typeschema/types"; import { mkSilentLogger } from "@typeschema-test/utils"; @@ -7,7 +7,7 @@ import { mkSilentLogger } from "@typeschema-test/utils"; * Tests for CDA package generation. * Package: hl7.cda.uv.core@2.0.1-sd */ -describe("CDA", () => { +describe("CDA", async () => { const treeShakeConfig = { "hl7.cda.uv.core": { "http://hl7.org/cda/stds/core/StructureDefinition/ClinicalDocument": {}, @@ -18,21 +18,12 @@ describe("CDA", () => { "hl7.cda.uv.core": ["http://hl7.org/cda/stds/core/StructureDefinition/ClinicalDocument" as CanonicalUrl], }; - describe("TypeScript Generation", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackage("hl7.cda.uv.core", "2.0.1-sd") - .typeSchema({ treeShake: treeShakeConfig }) - .typescript({ inMemoryOnly: true }) - .generate(); - }); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + describe("TypeScript Generation", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackage("hl7.cda.uv.core", "2.0.1-sd") + .typeSchema({ treeShake: treeShakeConfig }) + .typescript({ inMemoryOnly: true }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -54,21 +45,12 @@ describe("CDA", () => { }); }); - describe("Python Generation", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackage("hl7.cda.uv.core", "2.0.1-sd") - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .python({ inMemoryOnly: true }) - .generate(); - }); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + describe("Python Generation", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackage("hl7.cda.uv.core", "2.0.1-sd") + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .python({ inMemoryOnly: true }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -86,21 +68,12 @@ describe("CDA", () => { }); }); - describe("C# Generation", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackage("hl7.cda.uv.core", "2.0.1-sd") - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .csharp({ inMemoryOnly: true }) - .generate(); - }); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + describe("C# Generation", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackage("hl7.cda.uv.core", "2.0.1-sd") + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .csharp({ inMemoryOnly: true }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); diff --git a/test/api/write-generator/multi-package/local-package.test.ts b/test/api/write-generator/multi-package/local-package.test.ts index b5e018a5..5b13c3e2 100644 --- a/test/api/write-generator/multi-package/local-package.test.ts +++ b/test/api/write-generator/multi-package/local-package.test.ts @@ -1,6 +1,6 @@ -import { afterAll, beforeAll, describe, expect, it } from "bun:test"; +import { describe, expect, it } from "bun:test"; import * as Path from "node:path"; -import { APIBuilder, type GenerationReport } from "@root/api/builder"; +import { APIBuilder } from "@root/api/builder"; import type { CanonicalUrl } from "@root/typeschema/types"; import { mkSilentLogger } from "@typeschema-test/utils"; @@ -9,7 +9,7 @@ const LOCAL_PACKAGE_PATH = Path.join(__dirname, "../../../../examples/local-pack /** * Tests for local package folder functionality with multi-package dependency resolution. * */ -describe("Local Package Folder - Multi-Package Generation", () => { +describe("Local Package Folder - Multi-Package Generation", async () => { const localPackageConfig = { package: { name: "example.folder.structures", version: "0.0.1" }, path: LOCAL_PACKAGE_PATH, @@ -29,21 +29,12 @@ describe("Local Package Folder - Multi-Package Generation", () => { "example.folder.structures": ["http://example.org/fhir/StructureDefinition/ExampleNotebook" as CanonicalUrl], }; - describe("TypeScript Generation", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .localStructureDefinitions(localPackageConfig) - .typeSchema({ treeShake: treeShakeConfig }) - .typescript({ inMemoryOnly: true }) - .generate(); - }); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + describe("TypeScript Generation", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .localStructureDefinitions(localPackageConfig) + .typeSchema({ treeShake: treeShakeConfig }) + .typescript({ inMemoryOnly: true }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -71,31 +62,22 @@ describe("Local Package Folder - Multi-Package Generation", () => { }); }); - describe("TypeScript Generation with type-discriminated profile", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .localStructureDefinitions(localPackageConfig) - .typeSchema({ - treeShake: { - "example.folder.structures": { - "http://example.org/fhir/StructureDefinition/ExampleTypedBundle": {}, - }, - "hl7.fhir.r4.core": { - "http://hl7.org/fhir/StructureDefinition/Patient": {}, - "http://hl7.org/fhir/StructureDefinition/Organization": {}, - }, + describe("TypeScript Generation with type-discriminated profile", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .localStructureDefinitions(localPackageConfig) + .typeSchema({ + treeShake: { + "example.folder.structures": { + "http://example.org/fhir/StructureDefinition/ExampleTypedBundle": {}, }, - }) - .typescript({ inMemoryOnly: true, generateProfile: true, withDebugComment: false }) - .generate(); - }); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + "hl7.fhir.r4.core": { + "http://hl7.org/fhir/StructureDefinition/Patient": {}, + "http://hl7.org/fhir/StructureDefinition/Organization": {}, + }, + }, + }) + .typescript({ inMemoryOnly: true, generateProfile: true, withDebugComment: false }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -111,21 +93,12 @@ describe("Local Package Folder - Multi-Package Generation", () => { }); }); - describe("Python Generation", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .localStructureDefinitions(localPackageConfig) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .python({ inMemoryOnly: true }) - .generate(); - }); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + describe("Python Generation", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .localStructureDefinitions(localPackageConfig) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .python({ inMemoryOnly: true }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -156,21 +129,12 @@ describe("Local Package Folder - Multi-Package Generation", () => { }); }); - describe("C# Generation", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .localStructureDefinitions(localPackageConfig) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .csharp({ inMemoryOnly: true }) - .generate(); - }); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + describe("C# Generation", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .localStructureDefinitions(localPackageConfig) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .csharp({ inMemoryOnly: true }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); diff --git a/test/api/write-generator/multi-package/sql-on-fhir.test.ts b/test/api/write-generator/multi-package/sql-on-fhir.test.ts index 30907e12..0c2bd722 100644 --- a/test/api/write-generator/multi-package/sql-on-fhir.test.ts +++ b/test/api/write-generator/multi-package/sql-on-fhir.test.ts @@ -1,5 +1,5 @@ -import { afterAll, beforeAll, describe, expect, it } from "bun:test"; -import { APIBuilder, type GenerationReport } from "@root/api/builder"; +import { describe, expect, it } from "bun:test"; +import { APIBuilder } from "@root/api/builder"; import type { CanonicalUrl } from "@root/typeschema/types"; import { mkSilentLogger } from "@typeschema-test/utils"; @@ -7,7 +7,7 @@ import { mkSilentLogger } from "@typeschema-test/utils"; * Tests for SQL-on-FHIR package. * The SQL-on-FHIR package depends on hl7.fhir.r5.core. */ -describe("SQL-on-FHIR", () => { +describe("SQL-on-FHIR", async () => { const packageUrl = "https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/package.tgz"; const treeShakeConfig = { @@ -20,21 +20,12 @@ describe("SQL-on-FHIR", () => { "org.sql-on-fhir.ig": ["https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition" as CanonicalUrl], }; - describe("TypeScript Generation", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackageRef(packageUrl) - .typeSchema({ treeShake: treeShakeConfig }) - .typescript({ inMemoryOnly: true }) - .generate(); - }, 180_000); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + describe("TypeScript Generation", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackageRef(packageUrl) + .typeSchema({ treeShake: treeShakeConfig }) + .typescript({ inMemoryOnly: true }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -64,21 +55,12 @@ describe("SQL-on-FHIR", () => { }); }); - describe("Python Generation", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackageRef(packageUrl) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .python({ inMemoryOnly: true }) - .generate(); - }, 180_000); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + describe("Python Generation", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackageRef(packageUrl) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .python({ inMemoryOnly: true }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); @@ -103,21 +85,12 @@ describe("SQL-on-FHIR", () => { }); }); - describe("C# Generation", () => { - let result: GenerationReport; - - beforeAll(async () => { - result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackageRef(packageUrl) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .csharp({ inMemoryOnly: true }) - .generate(); - }, 180_000); - - afterAll(() => { - result = undefined as unknown as GenerationReport; - Bun.gc(true); - }); + describe("C# Generation", async () => { + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackageRef(packageUrl) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .csharp({ inMemoryOnly: true }) + .generate(); it("should succeed", () => { expect(result.success).toBeTrue(); From 79110c807a251c17bd12871a0819a6b266e2b07c Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 13:12:28 +0200 Subject: [PATCH 04/12] Force GC --- test/api/write-generator/csharp.test.ts | 2 ++ test/api/write-generator/introspection.test.ts | 2 ++ test/api/write-generator/python.test.ts | 2 ++ test/api/write-generator/typescript.test.ts | 2 ++ 4 files changed, 8 insertions(+) diff --git a/test/api/write-generator/csharp.test.ts b/test/api/write-generator/csharp.test.ts index 03a2532f..cc8471fb 100644 --- a/test/api/write-generator/csharp.test.ts +++ b/test/api/write-generator/csharp.test.ts @@ -20,3 +20,5 @@ describe("C# Writer Generator", async () => { expect(files["generated/types/Helper.cs"]).toMatchSnapshot(); }); }); + +Bun.gc(true); diff --git a/test/api/write-generator/introspection.test.ts b/test/api/write-generator/introspection.test.ts index 5c8814a6..12bf7f5e 100644 --- a/test/api/write-generator/introspection.test.ts +++ b/test/api/write-generator/introspection.test.ts @@ -150,3 +150,5 @@ describe("IntrospectionWriter - StructureDefinition output", async () => { expect(files["generated/structure-definitions.ndjson"]).toMatchSnapshot(); }); }); + +Bun.gc(true); diff --git a/test/api/write-generator/python.test.ts b/test/api/write-generator/python.test.ts index 26ac7f2b..2ead47c4 100644 --- a/test/api/write-generator/python.test.ts +++ b/test/api/write-generator/python.test.ts @@ -40,3 +40,5 @@ describe("Python Writer Generator", async () => { expect(basePy).toContain("T = TypeVar('T', bound=str, default=str)"); }); }); + +Bun.gc(true); diff --git a/test/api/write-generator/typescript.test.ts b/test/api/write-generator/typescript.test.ts index cd51945e..604d106c 100644 --- a/test/api/write-generator/typescript.test.ts +++ b/test/api/write-generator/typescript.test.ts @@ -171,3 +171,5 @@ describe("TypeScript US Core Example", async () => { expect(files["generated/types/hl7-fhir-us-core/profiles/index.ts"]).toMatchSnapshot(); }); }); + +Bun.gc(true); From c84880f5cb4ab778f3deccfce3a8936477d04413 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 13:16:54 +0200 Subject: [PATCH 05/12] Force GC --- test/api/write-generator/multi-package/cda.test.ts | 2 ++ test/api/write-generator/multi-package/local-package.test.ts | 2 ++ test/api/write-generator/multi-package/sql-on-fhir.test.ts | 2 ++ 3 files changed, 6 insertions(+) diff --git a/test/api/write-generator/multi-package/cda.test.ts b/test/api/write-generator/multi-package/cda.test.ts index cb4c73aa..9d808f1d 100644 --- a/test/api/write-generator/multi-package/cda.test.ts +++ b/test/api/write-generator/multi-package/cda.test.ts @@ -91,3 +91,5 @@ describe("CDA", async () => { }); }); }); + +Bun.gc(true); diff --git a/test/api/write-generator/multi-package/local-package.test.ts b/test/api/write-generator/multi-package/local-package.test.ts index 5b13c3e2..c7608f0b 100644 --- a/test/api/write-generator/multi-package/local-package.test.ts +++ b/test/api/write-generator/multi-package/local-package.test.ts @@ -172,3 +172,5 @@ describe("Local Package Folder - Multi-Package Generation", async () => { }); }); }); + +Bun.gc(true); diff --git a/test/api/write-generator/multi-package/sql-on-fhir.test.ts b/test/api/write-generator/multi-package/sql-on-fhir.test.ts index 0c2bd722..ea13b23d 100644 --- a/test/api/write-generator/multi-package/sql-on-fhir.test.ts +++ b/test/api/write-generator/multi-package/sql-on-fhir.test.ts @@ -116,3 +116,5 @@ describe("SQL-on-FHIR", async () => { }); }); }); + +Bun.gc(true); From 1b039be7f8925f62f1b974be9065d146290059f5 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 13:30:27 +0200 Subject: [PATCH 06/12] wip --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6bcf8fb9..c9409f7e 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ typecheck: $(TYPECHECK) test: typecheck - bun test + bun test --smol prepare-aidbox-runme: @if [ ! -f "example/docker-compose.yaml" ]; then \ From 5200b1db9dae4a93209750ee3fb77f66244e2551 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 13:41:20 +0200 Subject: [PATCH 07/12] test: inline ccdaManager into its single consumer `ccdaManager` was a module-level `await mkCCDARegister()` that loaded the CDA package into memory on test process start and pinned it for the whole run, even though only one describe in typescript.test.ts uses it. Inline the call so CDA is constructed (and reclaimable) inside that describe's scope instead. --- test/api/write-generator/typescript.test.ts | 4 ++-- test/unit/typeschema/utils.ts | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/api/write-generator/typescript.test.ts b/test/api/write-generator/typescript.test.ts index 604d106c..be62d9f6 100644 --- a/test/api/write-generator/typescript.test.ts +++ b/test/api/write-generator/typescript.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "bun:test"; import { APIBuilder } from "@root/api/builder"; import type { CanonicalUrl } from "@root/typeschema/types"; -import { ccdaManager, mkErrorLogger, r4Manager } from "@typeschema-test/utils"; +import { mkCCDARegister, mkErrorLogger, r4Manager } from "@typeschema-test/utils"; describe("TypeScript Writer Generator", async () => { const result = await new APIBuilder({ register: r4Manager, logger: mkErrorLogger() }) @@ -57,7 +57,7 @@ describe("TypeScript Writer Generator", async () => { }); describe("TypeScript CDA with Logical Model Promotion to Resource", async () => { - const result = await new APIBuilder({ register: ccdaManager, logger: mkErrorLogger() }) + const result = await new APIBuilder({ register: await mkCCDARegister(), logger: mkErrorLogger() }) .typeSchema({ promoteLogical: { "hl7.cda.uv.core": ["http://hl7.org/cda/stds/core/StructureDefinition/Material" as CanonicalUrl], diff --git a/test/unit/typeschema/utils.ts b/test/unit/typeschema/utils.ts index 219a1ae5..73cd88e3 100644 --- a/test/unit/typeschema/utils.ts +++ b/test/unit/typeschema/utils.ts @@ -43,8 +43,6 @@ export const mkCCDARegister = async () => // logger: createLogger({ verbose: true, prefix: "TEST" }) }); -export const ccdaManager = await mkCCDARegister(); - export const registerFs = (register: Register, fs: PFS) => { const pkg = fs.package_meta ?? { name: "mypackage", version: "0.0.0" }; const rfs = enrichFHIRSchema(fs as FHIRSchema, pkg); From 4c6dd674bcd3ba082bfd7a9ab98adb6e081f3c7a Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 13:46:46 +0200 Subject: [PATCH 08/12] ci: run sql-on-fhir.test.ts in its own bun process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `make test` was OOM-killed on ubuntu-latest during SQL-on-FHIR's build: the test fetches the remote tarball and runs the TS + Python + C# generators against R5 + SQL-on-FHIR, and its peak alone — on top of whatever the rest of the suite already had resident — exceeded the runner's ~7 GiB. Split it into a separate `bun test` invocation so its heap starts fresh, independent of the other test files' module-level state. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c9409f7e..06bc02fa 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,8 @@ typecheck: $(TYPECHECK) test: typecheck - bun test --smol + bun test --smol --path-ignore-patterns '**/sql-on-fhir.test.ts' + bun test --smol test/api/write-generator/multi-package/sql-on-fhir.test.ts prepare-aidbox-runme: @if [ ! -f "example/docker-compose.yaml" ]; then \ From 7d605aadaa433db6288d4b2b8857f2852ecf031d Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 14:36:58 +0200 Subject: [PATCH 09/12] ci: drop --smol; --smol + parallel tests caused 47m hang on CI --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 06bc02fa..5661d1c6 100644 --- a/Makefile +++ b/Makefile @@ -31,8 +31,8 @@ typecheck: $(TYPECHECK) test: typecheck - bun test --smol --path-ignore-patterns '**/sql-on-fhir.test.ts' - bun test --smol test/api/write-generator/multi-package/sql-on-fhir.test.ts + bun test --path-ignore-patterns '**/sql-on-fhir.test.ts' + bun test test/api/write-generator/multi-package/sql-on-fhir.test.ts prepare-aidbox-runme: @if [ ! -f "example/docker-compose.yaml" ]; then \ From 3a1d2a1189a52d793a96d942a44fc64a0051c672 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 14:47:17 +0200 Subject: [PATCH 10/12] ci: split sql-on-fhir.test.ts per-language to bound CI memory peak Even in its own bun process, sql-on-fhir.test.ts was OOM-killed: its three describes (TypeScript / Python / C#) each built a full generator output against R5 + the SQL-on-FHIR IG, and the three results held by closure simultaneously exceeded the ubuntu-latest runner's ~7 GiB. Split into three per-language files (matching the layout of the existing typescript.test.ts / python.test.ts / csharp.test.ts unit tests) and run each as a separate bun invocation in the Makefile, so peak memory is bounded to one generator's output at a time. Snapshots split alongside; describe paths are flat ("SQL-on-FHIR Generation") so snap keys are unchanged. --- Makefile | 6 +- .../sql-on-fhir-csharp.test.ts.snap | 120 +++++++++++ ...s.snap => sql-on-fhir-python.test.ts.snap} | 198 ------------------ .../sql-on-fhir-typescript.test.ts.snap | 80 +++++++ .../multi-package/sql-on-fhir-csharp.test.ts | 51 +++++ .../multi-package/sql-on-fhir-python.test.ts | 50 +++++ .../sql-on-fhir-typescript.test.ts | 48 +++++ .../multi-package/sql-on-fhir.test.ts | 120 ----------- 8 files changed, 353 insertions(+), 320 deletions(-) create mode 100644 test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-csharp.test.ts.snap rename test/api/write-generator/multi-package/__snapshots__/{sql-on-fhir.test.ts.snap => sql-on-fhir-python.test.ts.snap} (54%) create mode 100644 test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-typescript.test.ts.snap create mode 100644 test/api/write-generator/multi-package/sql-on-fhir-csharp.test.ts create mode 100644 test/api/write-generator/multi-package/sql-on-fhir-python.test.ts create mode 100644 test/api/write-generator/multi-package/sql-on-fhir-typescript.test.ts delete mode 100644 test/api/write-generator/multi-package/sql-on-fhir.test.ts diff --git a/Makefile b/Makefile index 5661d1c6..abc23426 100644 --- a/Makefile +++ b/Makefile @@ -31,8 +31,10 @@ typecheck: $(TYPECHECK) test: typecheck - bun test --path-ignore-patterns '**/sql-on-fhir.test.ts' - bun test test/api/write-generator/multi-package/sql-on-fhir.test.ts + bun test --path-ignore-patterns '**/sql-on-fhir-*.test.ts' + bun test test/api/write-generator/multi-package/sql-on-fhir-typescript.test.ts + bun test test/api/write-generator/multi-package/sql-on-fhir-python.test.ts + bun test test/api/write-generator/multi-package/sql-on-fhir-csharp.test.ts prepare-aidbox-runme: @if [ ! -f "example/docker-compose.yaml" ]; then \ diff --git a/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-csharp.test.ts.snap b/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-csharp.test.ts.snap new file mode 100644 index 00000000..843d44cf --- /dev/null +++ b/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-csharp.test.ts.snap @@ -0,0 +1,120 @@ +// Bun Snapshot v1, https://bun.sh/docs/test/snapshots + +exports[`SQL-on-FHIR C# Generation should generate ViewDefinition type (promoted logical) 1`] = ` +"// WARNING: This file is autogenerated by @atomic-ehr/codegen. +// GitHub: https://github.com/atomic-ehr/codegen +// Any manual changes made to this file may be overwritten. + + +namespace Fhir.Types.OrgSqlOnFhirIg; + +public class ViewDefinition : CanonicalResource { + public ViewDefinitionConstant[]? Constant { get; set; } + public ViewDefinitionFhirVersionBindingEnum[]? FhirVersion { get; set; } + public string? Name { get; set; } + public string[]? Profile { get; set; } + public required string Resource { get; set; } + public required ViewDefinitionSelect[] Select { get; set; } + public ViewDefinitionWhere[]? Where { get; set; } + + public class ViewDefinitionConstant : BackboneElement { + public required string Name { get; set; } + public string? ValueBase64binary { get; set; } + public bool? ValueBoolean { get; set; } + public string? ValueCanonical { get; set; } + public string? ValueCode { get; set; } + public string? ValueDate { get; set; } + public string? ValueDateTime { get; set; } + public decimal? ValueDecimal { get; set; } + public string? ValueId { get; set; } + public string? ValueInstant { get; set; } + public int? ValueInteger { get; set; } + public long? ValueInteger64 { get; set; } + public string? ValueOid { get; set; } + public long? ValuePositiveInt { get; set; } + public string? ValueString { get; set; } + public string? ValueTime { get; set; } + public long? ValueUnsignedInt { get; set; } + public string? ValueUri { get; set; } + public string? ValueUrl { get; set; } + public string? ValueUuid { get; set; } + + public override string ToString() => + JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); + + } + + public class ViewDefinitionSelect : BackboneElement { + public ViewDefinitionSelectColumn[]? Column { get; set; } + public string? ForEach { get; set; } + public string? ForEachOrNull { get; set; } + public string[]? Repeat { get; set; } + public ViewDefinitionSelect[]? Select { get; set; } + public ViewDefinitionSelect[]? UnionAll { get; set; } + + public override string ToString() => + JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); + + } + + public class ViewDefinitionSelectColumn : BackboneElement { + public bool? Collection { get; set; } + public string? Description { get; set; } + public required string Name { get; set; } + public required string Path { get; set; } + public ViewDefinitionSelectColumnTag[]? Tag { get; set; } + public string? Type { get; set; } + + public override string ToString() => + JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); + + } + + public class ViewDefinitionSelectColumnTag : BackboneElement { + public required string Name { get; set; } + public required string Value { get; set; } + + public override string ToString() => + JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); + + } + + public class ViewDefinitionWhere : BackboneElement { + public string? Description { get; set; } + public required string Path { get; set; } + + public override string ToString() => + JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); + + } + + + public override string ToString() => + JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); + +} + +" +`; + +exports[`SQL-on-FHIR C# Generation should generate DomainResource for R5 1`] = ` +"// WARNING: This file is autogenerated by @atomic-ehr/codegen. +// GitHub: https://github.com/atomic-ehr/codegen +// Any manual changes made to this file may be overwritten. + + +namespace Fhir.Types.Hl7FhirR5Core; + +public class DomainResource : Resource { + public Resource[]? Contained { get; set; } + public Extension[]? Extension { get; set; } + public Extension[]? ModifierExtension { get; set; } + public Narrative? Text { get; set; } + + public override string ToString() => + JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); + +} + +" +`; diff --git a/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir.test.ts.snap b/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-python.test.ts.snap similarity index 54% rename from test/api/write-generator/multi-package/__snapshots__/sql-on-fhir.test.ts.snap rename to test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-python.test.ts.snap index 12427c37..e6bd07cd 100644 --- a/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir.test.ts.snap +++ b/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-python.test.ts.snap @@ -1,84 +1,5 @@ // Bun Snapshot v1, https://bun.sh/docs/test/snapshots -exports[`SQL-on-FHIR TypeScript Generation should generate ViewDefinition type 1`] = ` -"// WARNING: This file is autogenerated by @atomic-ehr/codegen. -// GitHub: https://github.com/atomic-ehr/codegen -// Any manual changes made to this file may be overwritten. - -import type { BackboneElement } from "../hl7-fhir-r5-core/BackboneElement"; -import type { CanonicalResource } from "../hl7-fhir-r5-core/CanonicalResource"; - -import type { Element } from "../hl7-fhir-r5-core/Element"; -export type { BackboneElement } from "../hl7-fhir-r5-core/BackboneElement"; - -export interface ViewDefinitionConstant extends BackboneElement { - name: string; - valueBase64Binary?: string; - valueBoolean?: boolean; - valueCanonical?: string; - valueCode?: string; - valueDate?: string; - valueDateTime?: string; - valueDecimal?: number; - valueId?: string; - valueInstant?: string; - valueInteger?: number; - valueInteger64?: number; - valueOid?: string; - valuePositiveInt?: number; - valueString?: string; - valueTime?: string; - valueUnsignedInt?: number; - valueUri?: string; - valueUrl?: string; - valueUuid?: string; -} - -export interface ViewDefinitionSelect extends BackboneElement { - column?: ViewDefinitionSelectColumn[]; - forEach?: string; - forEachOrNull?: string; - repeat?: string[]; - select?: ViewDefinitionSelect[]; - unionAll?: ViewDefinitionSelect[]; -} - -export interface ViewDefinitionSelectColumn extends BackboneElement { - collection?: boolean; - description?: string; - name: string; - path: string; - tag?: ViewDefinitionSelectColumnTag[]; - type?: string; -} - -export interface ViewDefinitionSelectColumnTag extends BackboneElement { - name: string; - value: string; -} - -export interface ViewDefinitionWhere extends BackboneElement { - description?: string; - path: string; -} - -// CanonicalURL: https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition (pkg: org.sql-on-fhir.ig#2.1.0-pre) -export interface ViewDefinition extends CanonicalResource { - constant?: ViewDefinitionConstant[]; - fhirVersion?: ("0.01" | "0.05" | "0.06" | "0.11" | "0.0" | "0.0.80" | "0.0.81" | "0.0.82" | "0.4" | "0.4.0" | "0.5" | "0.5.0" | "1.0" | "1.0.0" | "1.0.1" | "1.0.2" | "1.1" | "1.1.0" | "1.4" | "1.4.0" | "1.6" | "1.6.0" | "1.8" | "1.8.0" | "3.0" | "3.0.0" | "3.0.1" | "3.0.2" | "3.3" | "3.3.0" | "3.5" | "3.5.0" | "4.0" | "4.0.0" | "4.0.1" | "4.1" | "4.1.0" | "4.2" | "4.2.0" | "4.3" | "4.3.0" | "4.3.0-cibuild" | "4.3.0-snapshot1" | "4.4" | "4.4.0" | "4.5" | "4.5.0" | "4.6" | "4.6.0" | "5.0" | "5.0.0" | "5.0.0-cibuild" | "5.0.0-snapshot1" | "5.0.0-snapshot2" | "5.0.0-ballot" | "5.0.0-snapshot3" | "5.0.0-draft-final")[]; - _fhirVersion?: (Element | null)[]; - name?: string; - _name?: Element; - profile?: string[]; - _profile?: (Element | null)[]; - resource: string; - _resource?: Element; - select: ViewDefinitionSelect[]; - where?: ViewDefinitionWhere[]; -} -" -`; - exports[`SQL-on-FHIR Python Generation should generate ViewDefinition type (promoted logical) 1`] = ` "# WARNING: This file is autogenerated by @atomic-ehr/codegen. # GitHub: https://github.com/atomic-ehr/codegen @@ -215,122 +136,3 @@ class DomainResource(Resource): " `; - -exports[`SQL-on-FHIR C# Generation should generate ViewDefinition type (promoted logical) 1`] = ` -"// WARNING: This file is autogenerated by @atomic-ehr/codegen. -// GitHub: https://github.com/atomic-ehr/codegen -// Any manual changes made to this file may be overwritten. - - -namespace Fhir.Types.OrgSqlOnFhirIg; - -public class ViewDefinition : CanonicalResource { - public ViewDefinitionConstant[]? Constant { get; set; } - public ViewDefinitionFhirVersionBindingEnum[]? FhirVersion { get; set; } - public string? Name { get; set; } - public string[]? Profile { get; set; } - public required string Resource { get; set; } - public required ViewDefinitionSelect[] Select { get; set; } - public ViewDefinitionWhere[]? Where { get; set; } - - public class ViewDefinitionConstant : BackboneElement { - public required string Name { get; set; } - public string? ValueBase64binary { get; set; } - public bool? ValueBoolean { get; set; } - public string? ValueCanonical { get; set; } - public string? ValueCode { get; set; } - public string? ValueDate { get; set; } - public string? ValueDateTime { get; set; } - public decimal? ValueDecimal { get; set; } - public string? ValueId { get; set; } - public string? ValueInstant { get; set; } - public int? ValueInteger { get; set; } - public long? ValueInteger64 { get; set; } - public string? ValueOid { get; set; } - public long? ValuePositiveInt { get; set; } - public string? ValueString { get; set; } - public string? ValueTime { get; set; } - public long? ValueUnsignedInt { get; set; } - public string? ValueUri { get; set; } - public string? ValueUrl { get; set; } - public string? ValueUuid { get; set; } - - public override string ToString() => - JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); - - } - - public class ViewDefinitionSelect : BackboneElement { - public ViewDefinitionSelectColumn[]? Column { get; set; } - public string? ForEach { get; set; } - public string? ForEachOrNull { get; set; } - public string[]? Repeat { get; set; } - public ViewDefinitionSelect[]? Select { get; set; } - public ViewDefinitionSelect[]? UnionAll { get; set; } - - public override string ToString() => - JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); - - } - - public class ViewDefinitionSelectColumn : BackboneElement { - public bool? Collection { get; set; } - public string? Description { get; set; } - public required string Name { get; set; } - public required string Path { get; set; } - public ViewDefinitionSelectColumnTag[]? Tag { get; set; } - public string? Type { get; set; } - - public override string ToString() => - JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); - - } - - public class ViewDefinitionSelectColumnTag : BackboneElement { - public required string Name { get; set; } - public required string Value { get; set; } - - public override string ToString() => - JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); - - } - - public class ViewDefinitionWhere : BackboneElement { - public string? Description { get; set; } - public required string Path { get; set; } - - public override string ToString() => - JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); - - } - - - public override string ToString() => - JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); - -} - -" -`; - -exports[`SQL-on-FHIR C# Generation should generate DomainResource for R5 1`] = ` -"// WARNING: This file is autogenerated by @atomic-ehr/codegen. -// GitHub: https://github.com/atomic-ehr/codegen -// Any manual changes made to this file may be overwritten. - - -namespace Fhir.Types.Hl7FhirR5Core; - -public class DomainResource : Resource { - public Resource[]? Contained { get; set; } - public Extension[]? Extension { get; set; } - public Extension[]? ModifierExtension { get; set; } - public Narrative? Text { get; set; } - - public override string ToString() => - JsonSerializer.Serialize(this, Helper.JsonSerializerOptions); - -} - -" -`; diff --git a/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-typescript.test.ts.snap b/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-typescript.test.ts.snap new file mode 100644 index 00000000..36d703cd --- /dev/null +++ b/test/api/write-generator/multi-package/__snapshots__/sql-on-fhir-typescript.test.ts.snap @@ -0,0 +1,80 @@ +// Bun Snapshot v1, https://bun.sh/docs/test/snapshots + +exports[`SQL-on-FHIR TypeScript Generation should generate ViewDefinition type 1`] = ` +"// WARNING: This file is autogenerated by @atomic-ehr/codegen. +// GitHub: https://github.com/atomic-ehr/codegen +// Any manual changes made to this file may be overwritten. + +import type { BackboneElement } from "../hl7-fhir-r5-core/BackboneElement"; +import type { CanonicalResource } from "../hl7-fhir-r5-core/CanonicalResource"; + +import type { Element } from "../hl7-fhir-r5-core/Element"; +export type { BackboneElement } from "../hl7-fhir-r5-core/BackboneElement"; + +export interface ViewDefinitionConstant extends BackboneElement { + name: string; + valueBase64Binary?: string; + valueBoolean?: boolean; + valueCanonical?: string; + valueCode?: string; + valueDate?: string; + valueDateTime?: string; + valueDecimal?: number; + valueId?: string; + valueInstant?: string; + valueInteger?: number; + valueInteger64?: number; + valueOid?: string; + valuePositiveInt?: number; + valueString?: string; + valueTime?: string; + valueUnsignedInt?: number; + valueUri?: string; + valueUrl?: string; + valueUuid?: string; +} + +export interface ViewDefinitionSelect extends BackboneElement { + column?: ViewDefinitionSelectColumn[]; + forEach?: string; + forEachOrNull?: string; + repeat?: string[]; + select?: ViewDefinitionSelect[]; + unionAll?: ViewDefinitionSelect[]; +} + +export interface ViewDefinitionSelectColumn extends BackboneElement { + collection?: boolean; + description?: string; + name: string; + path: string; + tag?: ViewDefinitionSelectColumnTag[]; + type?: string; +} + +export interface ViewDefinitionSelectColumnTag extends BackboneElement { + name: string; + value: string; +} + +export interface ViewDefinitionWhere extends BackboneElement { + description?: string; + path: string; +} + +// CanonicalURL: https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition (pkg: org.sql-on-fhir.ig#2.1.0-pre) +export interface ViewDefinition extends CanonicalResource { + constant?: ViewDefinitionConstant[]; + fhirVersion?: ("0.01" | "0.05" | "0.06" | "0.11" | "0.0" | "0.0.80" | "0.0.81" | "0.0.82" | "0.4" | "0.4.0" | "0.5" | "0.5.0" | "1.0" | "1.0.0" | "1.0.1" | "1.0.2" | "1.1" | "1.1.0" | "1.4" | "1.4.0" | "1.6" | "1.6.0" | "1.8" | "1.8.0" | "3.0" | "3.0.0" | "3.0.1" | "3.0.2" | "3.3" | "3.3.0" | "3.5" | "3.5.0" | "4.0" | "4.0.0" | "4.0.1" | "4.1" | "4.1.0" | "4.2" | "4.2.0" | "4.3" | "4.3.0" | "4.3.0-cibuild" | "4.3.0-snapshot1" | "4.4" | "4.4.0" | "4.5" | "4.5.0" | "4.6" | "4.6.0" | "5.0" | "5.0.0" | "5.0.0-cibuild" | "5.0.0-snapshot1" | "5.0.0-snapshot2" | "5.0.0-ballot" | "5.0.0-snapshot3" | "5.0.0-draft-final")[]; + _fhirVersion?: (Element | null)[]; + name?: string; + _name?: Element; + profile?: string[]; + _profile?: (Element | null)[]; + resource: string; + _resource?: Element; + select: ViewDefinitionSelect[]; + where?: ViewDefinitionWhere[]; +} +" +`; diff --git a/test/api/write-generator/multi-package/sql-on-fhir-csharp.test.ts b/test/api/write-generator/multi-package/sql-on-fhir-csharp.test.ts new file mode 100644 index 00000000..7b8fb10a --- /dev/null +++ b/test/api/write-generator/multi-package/sql-on-fhir-csharp.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from "bun:test"; +import { APIBuilder } from "@root/api/builder"; +import type { CanonicalUrl } from "@root/typeschema/types"; +import { mkSilentLogger } from "@typeschema-test/utils"; + +/** + * C# generation for the SQL-on-FHIR package. + * Split into its own file so peak memory stays bounded to one generator on CI. + */ +describe("SQL-on-FHIR C# Generation", async () => { + const packageUrl = "https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/package.tgz"; + + const treeShakeConfig = { + "org.sql-on-fhir.ig": { + "https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition": {}, + }, + }; + + const promoteLogicalConfig = { + "org.sql-on-fhir.ig": ["https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition" as CanonicalUrl], + }; + + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackageRef(packageUrl) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .csharp({ inMemoryOnly: true }) + .generate(); + + it("should succeed", () => { + expect(result.success).toBeTrue(); + }); + + it("should generate ViewDefinition type (promoted logical)", () => { + const viewDef = result.filesGenerated.csharp!["generated/types/OrgSqlOnFhirIg/ViewDefinition.cs"]; + expect(viewDef).toBeDefined(); + expect(viewDef).toMatchSnapshot(); + }); + + it("should generate R5 dependency namespace", () => { + // C# generator creates R5 types for dependencies + const files = Object.keys(result.filesGenerated.csharp!); + const r5Files = files.filter((f) => f.includes("Hl7FhirR5Core")); + expect(r5Files.length).toBe(5); + }); + + it("should generate DomainResource for R5", () => { + const domainResource = result.filesGenerated.csharp!["generated/types/Hl7FhirR5Core/DomainResource.cs"]; + expect(domainResource).toBeDefined(); + expect(domainResource).toMatchSnapshot(); + }); +}); diff --git a/test/api/write-generator/multi-package/sql-on-fhir-python.test.ts b/test/api/write-generator/multi-package/sql-on-fhir-python.test.ts new file mode 100644 index 00000000..2a327d54 --- /dev/null +++ b/test/api/write-generator/multi-package/sql-on-fhir-python.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from "bun:test"; +import { APIBuilder } from "@root/api/builder"; +import type { CanonicalUrl } from "@root/typeschema/types"; +import { mkSilentLogger } from "@typeschema-test/utils"; + +/** + * Python generation for the SQL-on-FHIR package. + * Split into its own file so peak memory stays bounded to one generator on CI. + */ +describe("SQL-on-FHIR Python Generation", async () => { + const packageUrl = "https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/package.tgz"; + + const treeShakeConfig = { + "org.sql-on-fhir.ig": { + "https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition": {}, + }, + }; + + const promoteLogicalConfig = { + "org.sql-on-fhir.ig": ["https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition" as CanonicalUrl], + }; + + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackageRef(packageUrl) + .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) + .python({ inMemoryOnly: true }) + .generate(); + + it("should succeed", () => { + expect(result.success).toBeTrue(); + }); + + it("should generate ViewDefinition type (promoted logical)", () => { + const viewDef = result.filesGenerated.python!["generated/org_sql_on_fhir_ig/view_definition.py"]; + expect(viewDef).toBeDefined(); + expect(viewDef).toMatchSnapshot(); + }); + + it("should generate R5 dependency package", () => { + // Python generator creates R5 base types for dependencies + expect(result.filesGenerated.python!["generated/hl7_fhir_r5_core/__init__.py"]).toBeDefined(); + expect(result.filesGenerated.python!["generated/hl7_fhir_r5_core/domain_resource.py"]).toBeDefined(); + }); + + it("should generate domain_resource for R5", () => { + const domainResource = result.filesGenerated.python!["generated/hl7_fhir_r5_core/domain_resource.py"]; + expect(domainResource).toBeDefined(); + expect(domainResource).toMatchSnapshot(); + }); +}); diff --git a/test/api/write-generator/multi-package/sql-on-fhir-typescript.test.ts b/test/api/write-generator/multi-package/sql-on-fhir-typescript.test.ts new file mode 100644 index 00000000..ac561f2e --- /dev/null +++ b/test/api/write-generator/multi-package/sql-on-fhir-typescript.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from "bun:test"; +import { APIBuilder } from "@root/api/builder"; +import { mkSilentLogger } from "@typeschema-test/utils"; + +/** + * TypeScript generation for the SQL-on-FHIR package. + * Split into its own file so peak memory stays bounded to one generator on CI. + */ +describe("SQL-on-FHIR TypeScript Generation", async () => { + const packageUrl = "https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/package.tgz"; + + const treeShakeConfig = { + "org.sql-on-fhir.ig": { + "https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition": {}, + }, + }; + + const result = await new APIBuilder({ logger: mkSilentLogger() }) + .fromPackageRef(packageUrl) + .typeSchema({ treeShake: treeShakeConfig }) + .typescript({ inMemoryOnly: true }) + .generate(); + + it("should succeed", () => { + expect(result.success).toBeTrue(); + }); + + it("should generate ViewDefinition type", () => { + const viewDef = result.filesGenerated.typescript!["generated/types/org-sql-on-fhir-ig/ViewDefinition.ts"]; + expect(viewDef).toBeDefined(); + expect(viewDef).toMatchSnapshot(); + }); + + it("should resolve R5 dependencies (required by SQL-on-FHIR)", () => { + const files = Object.keys(result.filesGenerated.typescript!); + const r5Files = files.filter((f) => f.includes("hl7-fhir-r5-core")); + expect(r5Files.length).toBe(45); + + // Core R5 types should be included + expect(result.filesGenerated.typescript!["generated/types/hl7-fhir-r5-core/Element.ts"]).toBeDefined(); + expect(result.filesGenerated.typescript!["generated/types/hl7-fhir-r5-core/DomainResource.ts"]).toBeDefined(); + }); + + it("should generate package index files", () => { + expect(result.filesGenerated.typescript!["generated/types/org-sql-on-fhir-ig/index.ts"]).toBeDefined(); + expect(result.filesGenerated.typescript!["generated/types/hl7-fhir-r5-core/index.ts"]).toBeDefined(); + }); +}); diff --git a/test/api/write-generator/multi-package/sql-on-fhir.test.ts b/test/api/write-generator/multi-package/sql-on-fhir.test.ts deleted file mode 100644 index ea13b23d..00000000 --- a/test/api/write-generator/multi-package/sql-on-fhir.test.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { describe, expect, it } from "bun:test"; -import { APIBuilder } from "@root/api/builder"; -import type { CanonicalUrl } from "@root/typeschema/types"; -import { mkSilentLogger } from "@typeschema-test/utils"; - -/** - * Tests for SQL-on-FHIR package. - * The SQL-on-FHIR package depends on hl7.fhir.r5.core. - */ -describe("SQL-on-FHIR", async () => { - const packageUrl = "https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/package.tgz"; - - const treeShakeConfig = { - "org.sql-on-fhir.ig": { - "https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition": {}, - }, - }; - - const promoteLogicalConfig = { - "org.sql-on-fhir.ig": ["https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition" as CanonicalUrl], - }; - - describe("TypeScript Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackageRef(packageUrl) - .typeSchema({ treeShake: treeShakeConfig }) - .typescript({ inMemoryOnly: true }) - .generate(); - - it("should succeed", () => { - expect(result.success).toBeTrue(); - }); - - it("should generate ViewDefinition type", () => { - const viewDef = result.filesGenerated.typescript!["generated/types/org-sql-on-fhir-ig/ViewDefinition.ts"]; - expect(viewDef).toBeDefined(); - expect(viewDef).toMatchSnapshot(); - }); - - it("should resolve R5 dependencies (required by SQL-on-FHIR)", () => { - const files = Object.keys(result.filesGenerated.typescript!); - const r5Files = files.filter((f) => f.includes("hl7-fhir-r5-core")); - expect(r5Files.length).toBe(45); - - // Core R5 types should be included - expect(result.filesGenerated.typescript!["generated/types/hl7-fhir-r5-core/Element.ts"]).toBeDefined(); - expect( - result.filesGenerated.typescript!["generated/types/hl7-fhir-r5-core/DomainResource.ts"], - ).toBeDefined(); - }); - - it("should generate package index files", () => { - expect(result.filesGenerated.typescript!["generated/types/org-sql-on-fhir-ig/index.ts"]).toBeDefined(); - expect(result.filesGenerated.typescript!["generated/types/hl7-fhir-r5-core/index.ts"]).toBeDefined(); - }); - }); - - describe("Python Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackageRef(packageUrl) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .python({ inMemoryOnly: true }) - .generate(); - - it("should succeed", () => { - expect(result.success).toBeTrue(); - }); - - it("should generate ViewDefinition type (promoted logical)", () => { - const viewDef = result.filesGenerated.python!["generated/org_sql_on_fhir_ig/view_definition.py"]; - expect(viewDef).toBeDefined(); - expect(viewDef).toMatchSnapshot(); - }); - - it("should generate R5 dependency package", () => { - // Python generator creates R5 base types for dependencies - expect(result.filesGenerated.python!["generated/hl7_fhir_r5_core/__init__.py"]).toBeDefined(); - expect(result.filesGenerated.python!["generated/hl7_fhir_r5_core/domain_resource.py"]).toBeDefined(); - }); - - it("should generate domain_resource for R5", () => { - const domainResource = result.filesGenerated.python!["generated/hl7_fhir_r5_core/domain_resource.py"]; - expect(domainResource).toBeDefined(); - expect(domainResource).toMatchSnapshot(); - }); - }); - - describe("C# Generation", async () => { - const result = await new APIBuilder({ logger: mkSilentLogger() }) - .fromPackageRef(packageUrl) - .typeSchema({ treeShake: treeShakeConfig, promoteLogical: promoteLogicalConfig }) - .csharp({ inMemoryOnly: true }) - .generate(); - - it("should succeed", () => { - expect(result.success).toBeTrue(); - }); - - it("should generate ViewDefinition type (promoted logical)", () => { - const viewDef = result.filesGenerated.csharp!["generated/types/OrgSqlOnFhirIg/ViewDefinition.cs"]; - expect(viewDef).toBeDefined(); - expect(viewDef).toMatchSnapshot(); - }); - - it("should generate R5 dependency namespace", () => { - // C# generator creates R5 types for dependencies - const files = Object.keys(result.filesGenerated.csharp!); - const r5Files = files.filter((f) => f.includes("Hl7FhirR5Core")); - expect(r5Files.length).toBe(5); - }); - - it("should generate DomainResource for R5", () => { - const domainResource = result.filesGenerated.csharp!["generated/types/Hl7FhirR5Core/DomainResource.cs"]; - expect(domainResource).toBeDefined(); - expect(domainResource).toMatchSnapshot(); - }); - }); -}); - -Bun.gc(true); From c6f434b0108d72bdd3f502f64cce245e402dd802 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 14:56:26 +0200 Subject: [PATCH 11/12] ci: pin Bun to 1.3.13, document test split `oven-sh/setup-bun@v2` defaults to latest stable. Bun 1.3.14 introduced a memory regression that OOM-kills `make test` on `ubuntu-latest`; the same suite passed in 62s on 1.3.13. Pin via `.bun-version` (read by setup-bun automatically) so CI and local dev share the known-good version. Also document the SQL-on-FHIR per-language test split in CLAUDE.md so the structure is not "cleaned up" back into one file. --- .bun-version | 1 + CLAUDE.md | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 .bun-version diff --git a/.bun-version b/.bun-version new file mode 100644 index 00000000..7962dcfd --- /dev/null +++ b/.bun-version @@ -0,0 +1 @@ +1.3.13 diff --git a/CLAUDE.md b/CLAUDE.md index 121b230a..8d48f065 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -130,6 +130,7 @@ FHIR Package → TypeSchema Generator → TypeSchema Format → Code Generators - Unit tests for core functionality (transformers, builders) - Tests mirror source structure in `test/unit/` - API tests for high-level generators +- SQL-on-FHIR is split into per-language files (`sql-on-fhir-typescript.test.ts`, `sql-on-fhir-python.test.ts`, `sql-on-fhir-csharp.test.ts`) and the `test` Makefile target runs each as a separate `bun test` invocation. The combined file ran out of memory on `ubuntu-latest` (~7 GiB) because all three generators built simultaneously against R5 + the SQL-on-FHIR IG. Each file gets its own bun process so peak memory is bounded to one generator's output at a time. Do not recombine. ### Example Test Structure @@ -354,6 +355,7 @@ Detection uses `mkIsFamilyType(tsIndex)` which checks `schema.typeFamily.resourc 3. **Choice Types**: Supported but representation differs by language 4. **Circular References**: Handled but may affect tree shaking 5. **Large Packages**: May require increased Node.js memory (`--max-old-space-size`) +6. **Bun version pinned via `.bun-version`**: Bun 1.3.14 introduced a memory regression that OOM-kills `make test` on `ubuntu-latest`. Stick to the version pinned in `.bun-version` until a fixed Bun release is verified to pass CI. ## Performance Optimization Tips From 189979282a89cba8386424c6f07abd6755d30f2c Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 20 May 2026 15:00:06 +0200 Subject: [PATCH 12/12] ci: wire setup-bun@v2 to .bun-version The bare `oven-sh/setup-bun@v2` step does NOT auto-detect .bun-version; the previous push still ran Bun 1.3.14. Add explicit `bun-version-file: .bun-version` to every setup-bun invocation so the file actually pins the version. --- .github/workflows/ci.yml | 8 ++++++++ .github/workflows/release.yml | 4 ++++ .github/workflows/sdk-tests.yml | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1d03e8c..f577766c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Install dependencies run: bun install --frozen-lockfile @@ -37,6 +39,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Install dependencies run: bun install --frozen-lockfile @@ -52,6 +56,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Build and pack run: | @@ -83,6 +89,8 @@ jobs: - name: Setup Bun if: matrix.package-manager == 'bun' uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Setup Node.js if: matrix.package-manager != 'bun' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f6b3980..c70fbd88 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,6 +25,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Install dependencies run: bun install --frozen-lockfile @@ -76,6 +78,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/sdk-tests.yml b/.github/workflows/sdk-tests.yml index 3c04dc45..735710d8 100644 --- a/.github/workflows/sdk-tests.yml +++ b/.github/workflows/sdk-tests.yml @@ -15,6 +15,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Setup .NET uses: actions/setup-dotnet@v5 @@ -50,6 +52,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Install dependencies run: bun install --frozen-lockfile @@ -78,6 +82,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Install dependencies run: bun install --frozen-lockfile @@ -107,6 +113,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Install dependencies run: bun install --frozen-lockfile @@ -122,6 +130,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - uses: actions/setup-python@v6 with: @@ -156,6 +166,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - uses: actions/setup-python@v6 with: @@ -190,6 +202,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Install dependencies run: bun install --frozen-lockfile @@ -205,6 +219,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Install dependencies run: bun install --frozen-lockfile @@ -224,6 +240,8 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version - name: Setup Java uses: actions/setup-java@v5