mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
12c08cc95c
* fix: populate wmill.d.ts schemas in wmill app dev Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: seed inferred schemas at wmill app dev startup Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
116 lines
4.3 KiB
TypeScript
116 lines
4.3 KiB
TypeScript
/**
|
|
* Integration test for `wmill app dev`'s schema-fetch path.
|
|
*
|
|
* Verifies that path-based runnables (type: script) get a real schema in the
|
|
* generated wmill.d.ts after fetchPathRunnableSchemas() pulls it from the
|
|
* Windmill API. Without this fix the runnable would always render as
|
|
* `args: {}` because the local YAML doesn't carry the schema.
|
|
*/
|
|
|
|
import { expect, test } from "bun:test";
|
|
import { withTestBackend } from "./test_backend.ts";
|
|
import { addWorkspace } from "../workspace.ts";
|
|
import * as fs from "node:fs";
|
|
import * as path from "node:path";
|
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { setClient } from "../src/core/client.ts";
|
|
import { APP_BACKEND_FOLDER } from "../src/commands/app/app_metadata.ts";
|
|
import { loadRunnablesFromBackend } from "../src/commands/app/raw_apps.ts";
|
|
import {
|
|
buildWmillTs,
|
|
fetchPathRunnableSchemas,
|
|
} from "../src/commands/app/dev.ts";
|
|
|
|
const TYPED_SCRIPT_CONTENT =
|
|
`export async function main(userId: string, includeProfile: boolean) {\n` +
|
|
` return { userId, includeProfile };\n` +
|
|
`}\n`;
|
|
|
|
test("fetchPathRunnableSchemas pulls schema for a path-based script and feeds wmill.d.ts", async () => {
|
|
await withTestBackend(async (backend, tempDir) => {
|
|
// Bind CLI workspace + arm the OpenAPI client
|
|
await addWorkspace(
|
|
{
|
|
remote: backend.baseUrl,
|
|
workspaceId: backend.workspace,
|
|
name: "app_dev_schema_fetch",
|
|
token: backend.token,
|
|
},
|
|
{ force: true, configDir: backend.testConfigDir },
|
|
);
|
|
setClient(backend.token, backend.baseUrl);
|
|
|
|
// Create the folder + script that the raw app will reference by path
|
|
await backend
|
|
.apiRequest!(`/api/w/${backend.workspace}/folders/create`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ name: "test" }),
|
|
})
|
|
.then((r) => r.text());
|
|
|
|
const scriptPath = "f/test/fetch_user";
|
|
const createResp = await backend.apiRequest!(
|
|
`/api/w/${backend.workspace}/scripts/create`,
|
|
{
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
path: scriptPath,
|
|
content: TYPED_SCRIPT_CONTENT,
|
|
language: "bun",
|
|
summary: "Fetch user",
|
|
description: "Test script with typed args",
|
|
schema: {
|
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
type: "object",
|
|
properties: {
|
|
userId: { type: "string", description: "user id" },
|
|
includeProfile: { type: "boolean" },
|
|
},
|
|
required: ["userId", "includeProfile"],
|
|
},
|
|
}),
|
|
},
|
|
);
|
|
expect(createResp.status).toBeLessThan(300);
|
|
|
|
// Lay out a raw app with a backend folder that references the path script
|
|
const appDir = path.join(tempDir, "f", "test", "schema_fetch_app.raw_app");
|
|
const backendDir = path.join(appDir, APP_BACKEND_FOLDER);
|
|
await mkdir(backendDir, { recursive: true });
|
|
await writeFile(
|
|
path.join(backendDir, "fetchUser.yaml"),
|
|
`type: script\npath: ${scriptPath}\nfields: {}\nname: fetchUser\n`,
|
|
"utf-8",
|
|
);
|
|
|
|
// Drive the dev pipeline directly: load -> enrich from API -> render
|
|
const runnables = await loadRunnablesFromBackend(backendDir);
|
|
expect(runnables.fetchUser?.type).toBe("path");
|
|
expect(runnables.fetchUser?.runType).toBe("script");
|
|
expect(runnables.fetchUser?.schema).toBeUndefined();
|
|
|
|
const pathSchemas = await fetchPathRunnableSchemas(
|
|
backend.workspace,
|
|
runnables,
|
|
);
|
|
expect(pathSchemas.fetchUser).toBeDefined();
|
|
expect(pathSchemas.fetchUser.properties.userId).toBeDefined();
|
|
expect(pathSchemas.fetchUser.properties.includeProfile).toBeDefined();
|
|
|
|
const ts = buildWmillTs(runnables, {}, pathSchemas);
|
|
|
|
// Without the fix the line would be `fetchUser: (args: {}) => ...`
|
|
expect(ts).toContain("userId");
|
|
expect(ts).toContain("includeProfile");
|
|
expect(ts).toMatch(/fetchUser:\s*\(args:\s*\{[^}]*userId/);
|
|
expect(ts).not.toMatch(/fetchUser:\s*\(args:\s*\{\s*\}\)/);
|
|
|
|
// Sanity: the generated d.ts is syntactically what the dev server writes
|
|
const outPath = path.join(tempDir, "wmill.d.ts.out");
|
|
fs.writeFileSync(outPath, ts);
|
|
expect(fs.statSync(outPath).size).toBeGreaterThan(0);
|
|
});
|
|
});
|