From 23e68bc1377c1f1940e199395bfab1eb8ad3e118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Sun, 12 Apr 2026 20:11:30 +0200 Subject: [PATCH] test(application): skip dispo-import suites when NDA sample xlsx fixtures absent Co-Authored-By: Claude Opus 4.6 --- .../src/__tests__/dispo-import.test.ts | 16 +++++- .../src/__tests__/read-workbook.test.ts | 55 ++++++++++++------- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/packages/application/src/__tests__/dispo-import.test.ts b/packages/application/src/__tests__/dispo-import.test.ts index dfa7d76..c8198f6 100644 --- a/packages/application/src/__tests__/dispo-import.test.ts +++ b/packages/application/src/__tests__/dispo-import.test.ts @@ -1,3 +1,4 @@ +import { existsSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { describe, expect, it, vi } from "vitest"; import { @@ -37,7 +38,16 @@ const costWorkbookPath = fileURLToPath( ), ); -describe("dispo import", () => { +// Sample xlsx fixtures are gitignored (NDA-protected real data). Skip suite when absent (CI). +const hasSamples = [ + mandatoryWorkbookPath, + chargeabilityWorkbookPath, + planningWorkbookPath, + rosterWorkbookPath, + costWorkbookPath, +].every((p) => existsSync(p)); + +describe.skipIf(!hasSamples)("dispo import", () => { it("parses the mandatory reference workbook into normalized master data", async () => { const parsed = await parseMandatoryDispoReferenceWorkbook(mandatoryWorkbookPath); @@ -196,7 +206,9 @@ describe("dispo import", () => { }), ]), ); - expect(parsed.resources.find((resource) => resource.canonicalExternalId === "antonia.melzer")).toBeUndefined(); + expect( + parsed.resources.find((resource) => resource.canonicalExternalId === "antonia.melzer"), + ).toBeUndefined(); }); it("parses the cost workbook into exact rates and level averages", async () => { diff --git a/packages/application/src/__tests__/read-workbook.test.ts b/packages/application/src/__tests__/read-workbook.test.ts index e38585f..6a546c2 100644 --- a/packages/application/src/__tests__/read-workbook.test.ts +++ b/packages/application/src/__tests__/read-workbook.test.ts @@ -1,3 +1,4 @@ +import { existsSync } from "node:fs"; import { cp, mkdtemp, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -23,6 +24,13 @@ const planningWorkbookPath = fileURLToPath( new URL("../../../../samples/Dispov2/DISPO_2026.xlsx", import.meta.url), ); +// Sample xlsx fixtures are gitignored (NDA-protected real data). Skip when absent (CI). +const hasSamples = + existsSync(referenceWorkbookPath) && + existsSync(chargeabilityWorkbookPath) && + existsSync(planningWorkbookPath); +const itIfSamples = hasSamples ? it : it.skip; + const tempDirectories: string[] = []; afterEach(async () => { @@ -39,7 +47,11 @@ async function makeTempDirectory(): Promise { return directory; } -async function writeWorkbook(filePath: string, rows: unknown[][], sheetName = "Sheet1"): Promise { +async function writeWorkbook( + filePath: string, + rows: unknown[][], + sheetName = "Sheet1", +): Promise { const ExcelJS = await import("exceljs"); const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet(sheetName); @@ -52,35 +64,41 @@ async function writeWorkbook(filePath: string, rows: unknown[][], sheetName = "S } describe("readWorksheetMatrix", () => { - it("reads trusted xlsx worksheets through the hardened reader", async () => { + itIfSamples("reads trusted xlsx worksheets through the hardened reader", async () => { const rows = await readWorksheetMatrix(referenceWorkbookPath, "EID-Attr"); expect(rows.length).toBeGreaterThan(0); expect(rows.some((row) => row.length > 0)).toBe(true); }); - it("tolerates workbook tables that contain unsupported exceljs date group filters", async () => { - const rows = await readWorksheetMatrix(chargeabilityWorkbookPath, "ChgFC"); + itIfSamples( + "tolerates workbook tables that contain unsupported exceljs date group filters", + async () => { + const rows = await readWorksheetMatrix(chargeabilityWorkbookPath, "ChgFC"); - expect(rows.length).toBeGreaterThan(300); - expect(rows[0]?.length).toBeGreaterThan(5); - }); + expect(rows.length).toBeGreaterThan(300); + expect(rows[0]?.length).toBeGreaterThan(5); + }, + ); - it("accepts real dispo planning worksheets within the supported width envelope", async () => { - const rows = await readWorksheetMatrix(planningWorkbookPath, "Dispo"); + itIfSamples( + "accepts real dispo planning worksheets within the supported width envelope", + async () => { + const rows = await readWorksheetMatrix(planningWorkbookPath, "Dispo"); - expect(rows.length).toBeGreaterThan(500); - expect(rows.some((row) => row.length > 256)).toBe(true); - expect(rows.every((row) => row.length <= MAX_DISPO_WORKBOOK_COLUMNS)).toBe(true); - }); + expect(rows.length).toBeGreaterThan(500); + expect(rows.some((row) => row.length > 256)).toBe(true); + expect(rows.every((row) => row.length <= MAX_DISPO_WORKBOOK_COLUMNS)).toBe(true); + }, + ); - it("rejects legacy .xls workbook paths", async () => { + itIfSamples("rejects legacy .xls workbook paths", async () => { const directory = await makeTempDirectory(); const legacyPath = path.join(directory, "legacy-input.xls"); await cp(referenceWorkbookPath, legacyPath); await expect(readWorksheetMatrix(legacyPath, "EID-Attr")).rejects.toThrow( - 'Only .xlsx workbooks are supported for dispo imports', + "Only .xlsx workbooks are supported for dispo imports", ); }); @@ -110,10 +128,9 @@ describe("readWorksheetMatrix", () => { it("rejects worksheets that exceed the column limit", async () => { const directory = await makeTempDirectory(); const workbookPath = path.join(directory, "too-many-columns.xlsx"); - await writeWorkbook( - workbookPath, - [Array.from({ length: MAX_DISPO_WORKBOOK_COLUMNS + 1 }, (_, index) => `col-${index + 1}`)], - ); + await writeWorkbook(workbookPath, [ + Array.from({ length: MAX_DISPO_WORKBOOK_COLUMNS + 1 }, (_, index) => `col-${index + 1}`), + ]); await expect(readWorksheetMatrix(workbookPath, "Sheet1")).rejects.toThrow( `exceeds the ${MAX_DISPO_WORKBOOK_COLUMNS} column import limit`,