Files
windmill/cli/test/list_get_new_commands.test.ts
hugocasa d6c642b170 feat: add Azure Event Grid triggers (#8888)
* feat: add Azure Event Grid triggers (EE)

Introduces a new enterprise trigger kind `azure` that supports three
modes via a single unified trigger type:
- basic_push: Azure Event Grid basic — custom topics, system topics
  (Storage, Resource Manager, Key Vault, etc.), domains (push only)
- namespace_push: Event Grid Namespace topics (CloudEvents over HTTP push)
- namespace_pull: Event Grid Namespace topics (HTTP pull with lock-token
  ack/reject for dead-lettering)

Auth uses a Service Principal resource (tenant_id, client_id,
client_secret, subscription_id). Subscriptions are created in
CloudEvents 1.0 schema so the push webhook handler and the pull listener
share one payload parser.

Backend
- New crate `windmill-trigger-azure` (OSS stubs + EE impl symlinked from
  windmill-ee-private)
- Migration `azure_trigger` table with CHECK constraints enforcing
  mode/columns coherence
- `TriggerKind::Azure`, `JobTriggerKind::Azure`,
  `DeployedObject::AzureTrigger` variants
- Push route `/api/azure/w/{workspace}/*path` handles classic
  Event Grid SubscriptionValidation handshake and CloudEvents 1.0
  abuse-protection OPTIONS handshake
- Optional inbound JWT validation (audience check only for v1)
- Feature flag `azure_trigger` propagated through windmill-api,
  windmill-store (resource helper), and added to ee_core

Frontend
- `triggers/azure/` editor with mode toggle (basic/namespace-push/
  namespace-pull) and per-mode config (topic ARM id / namespace +
  topic name / subscription / filters / push auth / pull options)
- Registered in icon map, display names, save functions, badge,
  wrapper, editor, add-trigger menu

OpenAPI
- `AzureTrigger`, `AzureTriggerData`, `AzureMode`,
  `AzureSubscriptionMode`, `AzureDeliveryConfig`, `TestAzureConnection`
  schemas; `/azure_triggers/*` endpoints; client regenerated

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to eaa7c3a9cb37a9ccc93f10a2535d929365acd2d8

This commit updates the EE repository reference after PR #541 was merged in windmill-ee-private.

Previous ee-repo-ref: 9689014e8c12c36c1059fd8fa5758d550b8b8bc9

New ee-repo-ref: eaa7c3a9cb37a9ccc93f10a2535d929365acd2d8

Automated by sync-ee-ref workflow.

* feat(azure-trigger): secret-auth push, ARM discovery, capture isolation, CLI + parity

Frontend:
- Split mode selector into Namespace/Basic + Pull/Push
- ARM resource dropdowns (namespaces, Basic topics, namespace topics)
  populated from the service principal; cascade with stale-selection
  reset on SP / edition change
- Remove stale authenticate toggle + audience input (server-managed
  push_auth_config has replaced them)
- Azure listing page: "Create from template" button; "Also delete Azure
  subscription" toggle in the delete modal; simplified trigger label
  falling back to path
- AzureCapture.svelte: "Test subscription name" with -wm-capture suffix
- CompareWorkspaces.svelte: wire Azure for fork/compare
- Drop Trigger-deployed/event-loss warning (capture subscription is
  isolated with -wm-capture)

Backend:
- Shared-secret push auth (see EE crate for detail)
- JSONB push_auth_config column (renamed from delivery_config), #[serde(skip)]
  so clients/CLI/exports never see it
- Drop redundant enabled column; mode supersedes
- Azure capture infra: AzureTriggerConfig + set_azure_trigger_config +
  azure_payload route + TriggerKind::Azure arm; PT15M queue TTL on
  capture subscriptions so they bound storage after tab close
- Granular ACLs, users offboarding, trash, git-sync deployed-object:
  all include azure_trigger

CLI:
- Add azure to TRIGGER_TYPES, pushObj dispatch, getTypeStrFromPath,
  trigger commands (get/update/create/list/template), sync delete
  switch + regex; e2e test for `trigger new --kind azure`
- system_prompts: SCHEMA_MAPPINGS + schema_names include AzureTrigger;
  auto-generated/* regenerated

Skill:
- .claude/skills/adding-a-trigger/ checklist covering every file that
  needs editing when wiring a new trigger type (learned from this PR)

ee-repo-ref bumped to b0e490cbf3724b7b64c6a5b010e3bdf24acd873c.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(azure-trigger): ci — ShareModal Kind + regenerated system_prompts

- frontend/src/lib/components/ShareModal.svelte: add 'azure_trigger'
  to the Kind type so the listing page's "Permissions" action compiles
  (ts2345 — caught by npm_check on CI, missed by fast-check locally).
- system_prompts/auto-generated/: regenerate to drop the stale
  delivery_config / AzureDeliveryConfig fields from the Azure schema
  (check-freshness on CI).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(azure-trigger): use workspace constant_time_eq crate

Drop hand-rolled constant-time compare in favour of the workspace
constant_time_eq crate (same one used by http_trigger_auth).

ee-repo-ref bumped to 9659382d47286e7f7f66d01b6f5dd8d4ed34848b.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(azure-trigger): pass placeholder + disabled via inputProps

`TextInput`'s `placeholder` and `disabled` go through its `inputProps`
prop — CI's `npm run check` caught the stale top-level passing that
`npm run check:fast` missed. Align with the DefaultEmailConfigSection
pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(azure-trigger): correct LATEST_GIT_SYNC_SCRIPT_PATH version to 28213

The hub deploy of the azure-aware sync-script is version 28213, not
28214. Backend was pinning a non-existent hub script, which broke the
git_sync_e2e suite (every deploy's sync step 404'd).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(azure-trigger): add azure_triggers to token scope selector + skill

- windmill-api/src/token.rs: `build_trigger_scope_domains` was missing
  `("azure_triggers", "Azure Event Grid")`, so the CreateToken UI's scope
  selector didn't surface azure_triggers:read/write. Backend already had
  `ScopeDomain::AzureTriggers` wired (scopes.rs), this just exposes it.
- .claude/skills/adding-a-trigger/SKILL.md: capture both scope-related
  files under the hardcoded-arrays section so future triggers don't miss
  the UI surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(adding-a-trigger-skill): clarify token.rs scope effect

Not a regression — nothing was working before. Skipping TRIGGER_DOMAINS
just means the scope works via API/CLI but has no UI checkbox.

* docs(adding-a-trigger-skill): trim token.rs bullet

* fix(azure-trigger): regen openapi-deref + swap textarea for TextInput

- Run build_openapi.sh to regenerate openapi-deref.{yaml,json} with the
  12 azure_triggers paths + schemas. These files are served by the
  runtime (include_str! in windmill-api/src/lib.rs) to external SDK
  consumers; without this regen the new endpoints wouldn't be advertised.
- Replace the raw <textarea> for event type filters with the
  design-system TextInput in textarea mode (frontend/CLAUDE.md bans raw
  HTML elements).

Addresses cubic + claude PR review items.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-04-23 16:30:18 +00:00

700 lines
21 KiB
TypeScript

/**
* Integration tests for the new list/get/new CLI commands.
*
* Tests:
* - `list --json` for all item types
* - `get <path>` and `get <path> --json` for all item types
* - `new` (bootstrap) for script, flow, resource, resource-type, variable, schedule, folder, trigger
* - `bootstrap` alias for script and flow
*/
import { expect, test, describe } from "bun:test";
import { writeFile, mkdir, stat, readFile } from "node:fs/promises";
import { join } from "node:path";
import { withTestBackend, type TestBackend } from "./test_backend.ts";
import { addWorkspace } from "../workspace.ts";
async function setupWorkspaceProfile(backend: TestBackend): Promise<void> {
await addWorkspace(
{
remote: backend.baseUrl,
workspaceId: backend.workspace,
name: "localhost_test",
token: backend.token!,
},
{ force: true, configDir: backend.testConfigDir }
);
}
async function createRemoteScript(
backend: TestBackend,
scriptPath: string,
content: string = 'export async function main() { return "hello"; }'
): Promise<void> {
const resp = await backend.apiRequest!(
`/api/w/${backend.workspace}/scripts/create`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
path: scriptPath,
content,
language: "bun",
summary: "Test script summary",
description: "Test script description",
schema: {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {},
required: [],
},
}),
}
);
expect(resp.status).toBeLessThan(300);
await resp.text();
}
// =============================================================================
// list --json
// =============================================================================
describe("list --json flag", () => {
test("script list --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const uniqueId = Date.now();
const scriptPath = `f/test/list_json_script_${uniqueId}`;
await createRemoteScript(backend, scriptPath);
const result = await backend.runCLICommand(
["script", "list", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
expect(parsed.some((s: any) => s.path === scriptPath)).toBe(true);
});
});
test("flow list --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["flow", "list", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
});
});
test("resource list --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["resource", "list", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
// seedTestData creates f/test/my_resource
expect(parsed.some((r: any) => r.path === "f/test/my_resource")).toBe(
true
);
});
});
test("variable list --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["variable", "list", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
});
});
test("folder list --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["folder", "list", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
expect(parsed.some((f: any) => f.name === "test")).toBe(true);
});
});
test("schedule list --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["schedule", "list", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
});
});
test("resource-type list --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["resource-type", "list", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
});
});
test("trigger list --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["trigger", "list", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
});
});
test("app list --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["app", "list", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
});
});
test("default action with --json works (e.g. wmill script --json)", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["script", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(Array.isArray(parsed)).toBe(true);
});
});
});
// =============================================================================
// get <path> and get <path> --json
// =============================================================================
describe("get command", () => {
test("script get pretty-prints details", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const uniqueId = Date.now();
const scriptPath = `f/test/get_script_${uniqueId}`;
await createRemoteScript(backend, scriptPath);
const result = await backend.runCLICommand(
["script", "get", scriptPath],
tempDir
);
expect(result.code).toEqual(0);
const output = result.stdout;
expect(output).toContain("Path:");
expect(output).toContain(scriptPath);
expect(output).toContain("Summary:");
expect(output).toContain("Language:");
expect(output).toContain("bun");
});
});
test("script get --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const uniqueId = Date.now();
const scriptPath = `f/test/get_json_script_${uniqueId}`;
await createRemoteScript(backend, scriptPath);
const result = await backend.runCLICommand(
["script", "get", scriptPath, "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(parsed.path).toBe(scriptPath);
expect(parsed.language).toBe("bun");
expect(parsed.summary).toBe("Test script summary");
});
});
test("resource get --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["resource", "get", "f/test/my_resource", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(parsed.path).toBe("f/test/my_resource");
expect(parsed.resource_type).toBe("any");
});
});
test("resource get pretty-prints details", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["resource", "get", "f/test/my_resource"],
tempDir
);
expect(result.code).toEqual(0);
const output = result.stdout;
expect(output).toContain("Path:");
expect(output).toContain("f/test/my_resource");
expect(output).toContain("Resource Type:");
});
});
test("variable get --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["variable", "get", "f/test/my_variable", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(parsed.path).toBe("f/test/my_variable");
});
});
test("folder get --json outputs valid JSON", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["folder", "get", "test", "--json"],
tempDir
);
expect(result.code).toEqual(0);
const parsed = JSON.parse(result.stdout);
expect(parsed.name).toBe("test");
});
});
test("folder get pretty-prints details", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["folder", "get", "test"],
tempDir
);
expect(result.code).toEqual(0);
const output = result.stdout;
expect(output).toContain("Name:");
expect(output).toContain("test");
});
});
});
// =============================================================================
// new command
// =============================================================================
describe("new command", () => {
test("script new creates files (same as bootstrap)", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await writeFile(
join(tempDir, "wmill.yaml"),
`defaultTs: bun\nincludes:\n - "**"\nexcludes: []\n`,
"utf-8"
);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["script", "new", "f/test/new_cmd_script", "bun", "--summary", "Test new"],
tempDir
);
expect(result.code).toEqual(0);
const codeStat = await stat(join(tempDir, "f/test/new_cmd_script.ts"));
expect(codeStat.isFile()).toBe(true);
const metaStat = await stat(
join(tempDir, "f/test/new_cmd_script.script.yaml")
);
expect(metaStat.isFile()).toBe(true);
const metaContent = await readFile(
join(tempDir, "f/test/new_cmd_script.script.yaml"),
"utf-8"
);
expect(metaContent).toContain("Test new");
});
});
test("script bootstrap still works as alias", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await writeFile(
join(tempDir, "wmill.yaml"),
`defaultTs: bun\nincludes:\n - "**"\nexcludes: []\n`,
"utf-8"
);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["script", "bootstrap", "f/test/alias_script", "bun"],
tempDir
);
expect(result.code).toEqual(0);
const codeStat = await stat(join(tempDir, "f/test/alias_script.ts"));
expect(codeStat.isFile()).toBe(true);
});
});
test("flow new creates flow directory and flow.yaml", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["flow", "new", "f/test/new_flow", "--summary", "My flow"],
tempDir
);
expect(result.code).toEqual(0);
const flowYamlStat = await stat(
join(tempDir, "f/test/new_flow.flow/flow.yaml")
);
expect(flowYamlStat.isFile()).toBe(true);
const flowContent = await readFile(
join(tempDir, "f/test/new_flow.flow/flow.yaml"),
"utf-8"
);
expect(flowContent).toContain("My flow");
});
});
test("flow new respects nonDottedPaths: true", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await writeFile(
join(tempDir, "wmill.yaml"),
`defaultTs: bun\nnonDottedPaths: true\nincludes:\n - "**"\nexcludes: []\n`,
"utf-8"
);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["flow", "new", "f/test/nondot_flow", "--summary", "Non-dotted flow"],
tempDir
);
expect(result.code).toEqual(0);
// Should use __flow suffix, not .flow
const flowYamlStat = await stat(
join(tempDir, "f/test/nondot_flow__flow/flow.yaml")
);
expect(flowYamlStat.isFile()).toBe(true);
const flowContent = await readFile(
join(tempDir, "f/test/nondot_flow__flow/flow.yaml"),
"utf-8"
);
expect(flowContent).toContain("Non-dotted flow");
});
});
test("flow bootstrap still works as alias", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["flow", "bootstrap", "f/test/alias_flow"],
tempDir
);
expect(result.code).toEqual(0);
const flowYamlStat = await stat(
join(tempDir, "f/test/alias_flow.flow/flow.yaml")
);
expect(flowYamlStat.isFile()).toBe(true);
});
});
test("resource new creates resource yaml template", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["resource", "new", "f/test/new_resource"],
tempDir
);
expect(result.code).toEqual(0);
const filePath = join(tempDir, "f/test/new_resource.resource.yaml");
const fileStat = await stat(filePath);
expect(fileStat.isFile()).toBe(true);
const content = await readFile(filePath, "utf-8");
expect(content).toContain("resource_type");
expect(content).toContain("value");
});
});
test("resource-type new creates resource-type yaml template", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["resource-type", "new", "my_custom_type"],
tempDir
);
expect(result.code).toEqual(0);
const filePath = join(tempDir, "my_custom_type.resource-type.yaml");
const fileStat = await stat(filePath);
expect(fileStat.isFile()).toBe(true);
const content = await readFile(filePath, "utf-8");
expect(content).toContain("schema");
expect(content).toContain("description");
});
});
test("variable new creates variable yaml template", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["variable", "new", "f/test/new_var"],
tempDir
);
expect(result.code).toEqual(0);
const filePath = join(tempDir, "f/test/new_var.variable.yaml");
const fileStat = await stat(filePath);
expect(fileStat.isFile()).toBe(true);
const content = await readFile(filePath, "utf-8");
expect(content).toContain("is_secret");
expect(content).toContain("value");
});
});
test("schedule new creates schedule yaml template", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["schedule", "new", "f/test/new_sched"],
tempDir
);
expect(result.code).toEqual(0);
const filePath = join(tempDir, "f/test/new_sched.schedule.yaml");
const fileStat = await stat(filePath);
expect(fileStat.isFile()).toBe(true);
const content = await readFile(filePath, "utf-8");
expect(content).toContain("schedule");
expect(content).toContain("script_path");
expect(content).toContain("timezone");
});
});
test("folder new creates folder.meta.yaml in f/<name>/", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
const result = await backend.runCLICommand(
["folder", "new", "new_folder"],
tempDir
);
expect(result.code).toEqual(0);
const filePath = join(tempDir, "f/new_folder/folder.meta.yaml");
const fileStat = await stat(filePath);
expect(fileStat.isFile()).toBe(true);
const content = await readFile(filePath, "utf-8");
expect(content).toContain("owners");
expect(content).toContain("extra_perms");
});
});
test("trigger new --kind http creates http trigger yaml template", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["trigger", "new", "f/test/new_trigger", "--kind", "http"],
tempDir
);
expect(result.code).toEqual(0);
const filePath = join(
tempDir,
"f/test/new_trigger.http_trigger.yaml"
);
const fileStat = await stat(filePath);
expect(fileStat.isFile()).toBe(true);
const content = await readFile(filePath, "utf-8");
expect(content).toContain("script_path");
expect(content).toContain("route_path");
});
});
test("trigger new without --kind fails with error", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["trigger", "new", "f/test/fail_trigger"],
tempDir
);
expect(result.code).not.toEqual(0);
});
});
test("trigger new --kind kafka creates kafka trigger yaml template", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["trigger", "new", "f/test/kafka_trigger", "--kind", "kafka"],
tempDir
);
expect(result.code).toEqual(0);
const filePath = join(
tempDir,
"f/test/kafka_trigger.kafka_trigger.yaml"
);
const fileStat = await stat(filePath);
expect(fileStat.isFile()).toBe(true);
const content = await readFile(filePath, "utf-8");
expect(content).toContain("kafka_resource_path");
expect(content).toContain("topics");
});
});
test("trigger new --kind azure creates azure trigger yaml template", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspaceProfile(backend);
await mkdir(join(tempDir, "f", "test"), { recursive: true });
const result = await backend.runCLICommand(
["trigger", "new", "f/test/azure_trigger", "--kind", "azure"],
tempDir
);
expect(result.code).toEqual(0);
const filePath = join(
tempDir,
"f/test/azure_trigger.azure_trigger.yaml"
);
const fileStat = await stat(filePath);
expect(fileStat.isFile()).toBe(true);
const content = await readFile(filePath, "utf-8");
expect(content).toContain("azure_resource_path");
expect(content).toContain("azure_mode");
expect(content).toContain("scope_resource_id");
expect(content).toContain("subscription_name");
});
});
});