mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
85134578b4
* fix(cli): route fileset children to their parent resource on sync push Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): scope fileset pointer validation to sync pushes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): error on script push of file/fileset resource content files Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): enforce server-canonical fileset pointers and fail fast before apply Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): resolve ws-specific fileset metadata and validate pointers before dry-run Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): prefer workspace-specific fileset metadata over base file Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(cli): make fileset metadata lookup assertions platform-separator safe Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): stop dropping fileset children whose names look like typed metadata Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(cli): exempt fileset children from the current-workspace classifier too Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
156 lines
5.6 KiB
TypeScript
156 lines
5.6 KiB
TypeScript
/**
|
|
* Unit tests for fileset resource sync routing and pointer validation.
|
|
* These tests require no backend — they test standalone logic.
|
|
*/
|
|
|
|
import { expect, test, describe } from "bun:test";
|
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join, sep as SEP } from "node:path";
|
|
import { handleFile } from "../src/commands/script/script.ts";
|
|
import { validateFilesetPointer } from "../src/commands/resource/resource.ts";
|
|
import { findFilesetResourceFile } from "../src/commands/sync/sync.ts";
|
|
import {
|
|
isCurrentWorkspaceFile,
|
|
isWorkspaceSpecificFile,
|
|
} from "../src/core/specific_items.ts";
|
|
|
|
describe("handleFile routing", () => {
|
|
test("returns false for fileset children with script extensions", async () => {
|
|
// A fileset child belongs to its parent resource's value, never to a
|
|
// standalone script: bare `.sql` has no script language (only `.pg.sql`
|
|
// etc. do), so routing it to the script pusher aborts the whole push.
|
|
for (const p of [
|
|
"f/resources/data.fileset/energy/queries/report.sql",
|
|
"f/resources/data.fileset/scripts/main.ts",
|
|
"f/resources/data.fileset/scripts/main.py",
|
|
]) {
|
|
expect(
|
|
await handleFile(p, {} as any, [], undefined, undefined, {}, []),
|
|
).toBe(false);
|
|
}
|
|
});
|
|
|
|
test("returns false for single-file resource content files", async () => {
|
|
expect(
|
|
await handleFile(
|
|
"f/resources/query.resource.file.sql",
|
|
{} as any,
|
|
[],
|
|
undefined,
|
|
undefined,
|
|
{},
|
|
[],
|
|
),
|
|
).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("workspace-specific classification", () => {
|
|
test("fileset children are never workspace-specific", () => {
|
|
// The workspace-name segment of the pattern spans `/`, so a child whose
|
|
// own name looks like a typed metadata file would otherwise be read as
|
|
// belonging to another workspace and dropped from every diff — i.e.
|
|
// silently never deployed.
|
|
for (const p of [
|
|
"f/resources/data.fileset/edge/q.resource.file.sql",
|
|
"f/resources/data.fileset/edge/inner.resource.yaml",
|
|
"f/resources/data.fileset/queries/report.sql",
|
|
]) {
|
|
expect(isWorkspaceSpecificFile(p)).toBe(false);
|
|
}
|
|
// A child carrying the active workspace's own suffix must not be remapped
|
|
// onto the unsuffixed sibling key, which would deploy over that sibling.
|
|
expect(
|
|
isCurrentWorkspaceFile(
|
|
"f/resources/data.fileset/edge/inner.ws_main.resource.yaml",
|
|
"ws_main",
|
|
),
|
|
).toBe(false);
|
|
// The parent's own metadata still carries the suffix.
|
|
expect(isWorkspaceSpecificFile("f/resources/data.ws_main.resource.yaml")).toBe(
|
|
true,
|
|
);
|
|
expect(
|
|
isCurrentWorkspaceFile("f/resources/data.ws_main.resource.yaml", "ws_main"),
|
|
).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("validateFilesetPointer", () => {
|
|
test("accepts the server-canonical pointer", () => {
|
|
expect(() =>
|
|
validateFilesetPointer("f/resources/data.fileset", "f/resources/data"),
|
|
).not.toThrow();
|
|
});
|
|
|
|
test("normalizes trailing slashes", () => {
|
|
expect(() =>
|
|
validateFilesetPointer("f/resources/data.fileset/", "f/resources/data"),
|
|
).not.toThrow();
|
|
});
|
|
|
|
test("rejects a custom pointer with an actionable message", () => {
|
|
expect(() =>
|
|
validateFilesetPointer(
|
|
"f/queries.fileset",
|
|
"f/resources/analytics_data",
|
|
),
|
|
).toThrow(/must live next to its resource file, at 'f\/resources\/analytics_data\.fileset'/);
|
|
});
|
|
|
|
test("resolves workspace-specific metadata for canonical children", async () => {
|
|
// The metadata file carries the workspace suffix while children stay at
|
|
// the server-canonical `<base>.fileset/` directory.
|
|
const dir = mkdtempSync(join(tmpdir(), "fileset-ws-"));
|
|
const cwd = process.cwd();
|
|
try {
|
|
mkdirSync(join(dir, "f/res"), { recursive: true });
|
|
writeFileSync(
|
|
join(dir, "f/res/data.ws_main.resource.yaml"),
|
|
"resource_type: c_files\nvalue: '!inline_fileset f/res/data.fileset'\n",
|
|
);
|
|
process.chdir(dir);
|
|
// findFilesetResourceFile derives the metadata path from the child
|
|
// path, so both use the platform separator.
|
|
const childPath = ["f", "res", "data.fileset", "q.sql"].join(SEP);
|
|
const wsMetadataPath = ["f", "res", "data.ws_main.resource.yaml"].join(
|
|
SEP,
|
|
);
|
|
const baseMetadataPath = ["f", "res", "data.resource.yaml"].join(SEP);
|
|
expect(await findFilesetResourceFile(childPath, "ws_main")).toBe(
|
|
wsMetadataPath,
|
|
);
|
|
await expect(findFilesetResourceFile(childPath, null)).rejects.toThrow(
|
|
/No resource metadata file found/,
|
|
);
|
|
// The suffixed file is the workspace's authoritative metadata: it wins
|
|
// even when a base metadata file coexists with it.
|
|
writeFileSync(
|
|
join(dir, "f/res/data.resource.yaml"),
|
|
"resource_type: c_files\nvalue: '!inline_fileset f/res/data.fileset'\n",
|
|
);
|
|
expect(await findFilesetResourceFile(childPath, "ws_main")).toBe(
|
|
wsMetadataPath,
|
|
);
|
|
expect(await findFilesetResourceFile(childPath, null)).toBe(
|
|
baseMetadataPath,
|
|
);
|
|
} finally {
|
|
process.chdir(cwd);
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("rejects a branch-suffixed directory for a workspace-specific resource", () => {
|
|
// The remote exporter always renders children at the server-canonical
|
|
// location, so a `.ws_<branch>.fileset` directory would never round-trip.
|
|
expect(() =>
|
|
validateFilesetPointer(
|
|
"f/resources/data.ws_main.fileset",
|
|
"f/resources/data",
|
|
),
|
|
).toThrow(/at 'f\/resources\/data\.fileset'/);
|
|
});
|
|
});
|