// Local compiled-worker smoke. Uses a dedicated queue and only safe missing-job input. import { spawn } from "node:child_process"; import { createServer } from "node:net"; import { resolve } from "node:path"; import { Client, Connection, WorkflowExecutionAlreadyStartedError } from "@temporalio/client"; import { randomUUID } from "node:crypto"; if (!process.env.PUBLISHING_IT_DATABASE_URL) throw new Error("PUBLISHING_IT_DATABASE_URL is required (migrate dedicated local DB first)"); const probe = createServer(); await new Promise((done) => probe.listen(0, "127.0.0.1", done)); const port = probe.address().port; await new Promise((done) => probe.close(done)); const queue = `wp3-sol-${randomUUID()}`; const child = spawn(process.execPath, [resolve("dist/worker.js")], { env: { ...process.env, SCHEDULER_DATABASE_URL: process.env.PUBLISHING_IT_DATABASE_URL, SCHEDULER_HEALTH_PORT: String(port), TEMPORAL_TASK_QUEUE: queue, TEMPORAL_ADDRESS: "127.0.0.1:7233" }, stdio: ["ignore", "pipe", "pipe"] }); let output = ""; child.stdout.on("data", (chunk) => { output += chunk.toString(); }); child.stderr.on("data", (chunk) => { output += chunk.toString(); }); const exit = new Promise((done) => child.once("exit", (code) => done(code))); let connection; try { let ready = false; for (let count = 0; count < 100; count++) { if (child.exitCode !== null) throw new Error("compiled worker exited before readiness"); try { const response = await fetch(`http://127.0.0.1:${port}/ready`, { signal: AbortSignal.timeout(1000) }); ready = response.status === 200; } catch {} if (ready) break; await new Promise((done) => setTimeout(done, 300)); } if (!ready) throw new Error("compiled worker readiness timed out"); connection = await Connection.connect({ address: "127.0.0.1:7233" }); const client = new Client({ connection, namespace: process.env.TEMPORAL_NAMESPACE ?? "default" }); const ref = { workspaceId: randomUUID(), scheduleId: randomUUID(), generation: "1", idempotencyKey: `wp3-smoke-${randomUUID()}` }; // Use an existing, empty fixture workspace so claim returns a safe missing-fence // outcome. This smoke does not acquire any credential or call a provider. const { Pool } = await import("pg"); const pool = new Pool({ connectionString: process.env.PUBLISHING_IT_SUPER_DATABASE_URL }); const workspace = await pool.query("INSERT INTO workspaces(name,slug) VALUES('Temporal Smoke',$1) RETURNING id", [queue]); ref.workspaceId = workspace.rows[0].id; try { const workflowId = `scheduled-publication-${ref.idempotencyKey}`; const handle = await client.workflow.start("scheduledPublish", { taskQueue: queue, workflowId, args: [ref] }); const result = await handle.result(); if (result.kind !== "failed" || result.code !== "EXECUTION_FENCE_MISSING") throw new Error("unexpected workflow result"); let duplicate = false; try { await client.workflow.start("scheduledPublish", { taskQueue: queue, workflowId, args: [ref], workflowIdReusePolicy: "REJECT_DUPLICATE" }); } catch (error) { if (!(error instanceof WorkflowExecutionAlreadyStartedError)) throw error; duplicate = true; } if (!duplicate) throw new Error("duplicate start was not fenced"); console.log("PASS: compiled worker readiness, registry/workflow bundle, real Temporal activity, structured outcome, duplicate start, graceful shutdown. No provider sends."); } finally { await pool.query("DELETE FROM workspaces WHERE id=$1", [ref.workspaceId]); await pool.end(); } } catch (error) { console.error(output.slice(-6000)); throw error; } finally { child.kill("SIGTERM"); const code = await Promise.race([exit, new Promise((done) => setTimeout(() => done("timeout"), 15_000))]); if (code === "timeout") { child.kill("SIGKILL"); throw new Error("worker did not drain"); } if (code !== 0) throw new Error(`worker shutdown exit code ${code}`); await connection?.close(); }