test(application): skip dispo-import suites when NDA sample xlsx fixtures absent
CI / Typecheck (push) Failing after 3m15s
CI / Architecture Guardrails (push) Successful in 3m52s
CI / Build (push) Has been skipped
CI / E2E Tests (push) Has been skipped
CI / Assistant Split Regression (push) Successful in 4m23s
CI / Lint (push) Successful in 4m53s
CI / Unit Tests (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been skipped
CI / Typecheck (push) Failing after 3m15s
CI / Architecture Guardrails (push) Successful in 3m52s
CI / Build (push) Has been skipped
CI / E2E Tests (push) Has been skipped
CI / Assistant Split Regression (push) Successful in 4m23s
CI / Lint (push) Successful in 4m53s
CI / Unit Tests (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { existsSync } from "node:fs";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import {
|
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 () => {
|
it("parses the mandatory reference workbook into normalized master data", async () => {
|
||||||
const parsed = await parseMandatoryDispoReferenceWorkbook(mandatoryWorkbookPath);
|
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 () => {
|
it("parses the cost workbook into exact rates and level averages", async () => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { existsSync } from "node:fs";
|
||||||
import { cp, mkdtemp, rm, writeFile } from "node:fs/promises";
|
import { cp, mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
@@ -23,6 +24,13 @@ const planningWorkbookPath = fileURLToPath(
|
|||||||
new URL("../../../../samples/Dispov2/DISPO_2026.xlsx", import.meta.url),
|
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[] = [];
|
const tempDirectories: string[] = [];
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -39,7 +47,11 @@ async function makeTempDirectory(): Promise<string> {
|
|||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function writeWorkbook(filePath: string, rows: unknown[][], sheetName = "Sheet1"): Promise<void> {
|
async function writeWorkbook(
|
||||||
|
filePath: string,
|
||||||
|
rows: unknown[][],
|
||||||
|
sheetName = "Sheet1",
|
||||||
|
): Promise<void> {
|
||||||
const ExcelJS = await import("exceljs");
|
const ExcelJS = await import("exceljs");
|
||||||
const workbook = new ExcelJS.Workbook();
|
const workbook = new ExcelJS.Workbook();
|
||||||
const worksheet = workbook.addWorksheet(sheetName);
|
const worksheet = workbook.addWorksheet(sheetName);
|
||||||
@@ -52,35 +64,41 @@ async function writeWorkbook(filePath: string, rows: unknown[][], sheetName = "S
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("readWorksheetMatrix", () => {
|
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");
|
const rows = await readWorksheetMatrix(referenceWorkbookPath, "EID-Attr");
|
||||||
|
|
||||||
expect(rows.length).toBeGreaterThan(0);
|
expect(rows.length).toBeGreaterThan(0);
|
||||||
expect(rows.some((row) => row.length > 0)).toBe(true);
|
expect(rows.some((row) => row.length > 0)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("tolerates workbook tables that contain unsupported exceljs date group filters", async () => {
|
itIfSamples(
|
||||||
const rows = await readWorksheetMatrix(chargeabilityWorkbookPath, "ChgFC");
|
"tolerates workbook tables that contain unsupported exceljs date group filters",
|
||||||
|
async () => {
|
||||||
|
const rows = await readWorksheetMatrix(chargeabilityWorkbookPath, "ChgFC");
|
||||||
|
|
||||||
expect(rows.length).toBeGreaterThan(300);
|
expect(rows.length).toBeGreaterThan(300);
|
||||||
expect(rows[0]?.length).toBeGreaterThan(5);
|
expect(rows[0]?.length).toBeGreaterThan(5);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it("accepts real dispo planning worksheets within the supported width envelope", async () => {
|
itIfSamples(
|
||||||
const rows = await readWorksheetMatrix(planningWorkbookPath, "Dispo");
|
"accepts real dispo planning worksheets within the supported width envelope",
|
||||||
|
async () => {
|
||||||
|
const rows = await readWorksheetMatrix(planningWorkbookPath, "Dispo");
|
||||||
|
|
||||||
expect(rows.length).toBeGreaterThan(500);
|
expect(rows.length).toBeGreaterThan(500);
|
||||||
expect(rows.some((row) => row.length > 256)).toBe(true);
|
expect(rows.some((row) => row.length > 256)).toBe(true);
|
||||||
expect(rows.every((row) => row.length <= MAX_DISPO_WORKBOOK_COLUMNS)).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 directory = await makeTempDirectory();
|
||||||
const legacyPath = path.join(directory, "legacy-input.xls");
|
const legacyPath = path.join(directory, "legacy-input.xls");
|
||||||
await cp(referenceWorkbookPath, legacyPath);
|
await cp(referenceWorkbookPath, legacyPath);
|
||||||
|
|
||||||
await expect(readWorksheetMatrix(legacyPath, "EID-Attr")).rejects.toThrow(
|
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 () => {
|
it("rejects worksheets that exceed the column limit", async () => {
|
||||||
const directory = await makeTempDirectory();
|
const directory = await makeTempDirectory();
|
||||||
const workbookPath = path.join(directory, "too-many-columns.xlsx");
|
const workbookPath = path.join(directory, "too-many-columns.xlsx");
|
||||||
await writeWorkbook(
|
await writeWorkbook(workbookPath, [
|
||||||
workbookPath,
|
Array.from({ length: MAX_DISPO_WORKBOOK_COLUMNS + 1 }, (_, index) => `col-${index + 1}`),
|
||||||
[Array.from({ length: MAX_DISPO_WORKBOOK_COLUMNS + 1 }, (_, index) => `col-${index + 1}`)],
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
await expect(readWorksheetMatrix(workbookPath, "Sheet1")).rejects.toThrow(
|
await expect(readWorksheetMatrix(workbookPath, "Sheet1")).rejects.toThrow(
|
||||||
`exceeds the ${MAX_DISPO_WORKBOOK_COLUMNS} column import limit`,
|
`exceeds the ${MAX_DISPO_WORKBOOK_COLUMNS} column import limit`,
|
||||||
|
|||||||
Reference in New Issue
Block a user