/** * component-loader:`code` 零件解析測試(#29 發現的白名單缺口)。 * * 背景:workflow 寫 `component: code` 時,component-loader 解析鏈 step 1-6 都不命中 * (非 builtin / 非 URL / 非 hash / 非邏輯零件 binding / 非 recipe),必須靠 step 7 的 * WASM_HTTP_RUNNER_IDS 白名單 → wasmWorkerUrl 通用推導。白名單漏 `code` 就會落到 * step 8 直接丟「找不到零件」——graph_neighbors 等用 code 節點的 workflow 全滅。 * * 驗證: * 1. `code` 能被解析成 runner(不 throw)。 * 2. runner 打的 URL = arcrun-code.{WORKER_SUBDOMAIN}.workers.dev(通用推導、無寫死 * 官方 code.arcrun.dev → self-hosted 換 WORKER_SUBDOMAIN 即落地)。 */ import { describe, it, expect, vi, afterEach } from 'vitest'; import { env } from 'cloudflare:test'; import { createComponentLoader, wasmWorkerUrl } from '../src/lib/component-loader'; import type { Bindings } from '../src/types'; afterEach(() => { vi.unstubAllGlobals(); }); describe('component-loader:code 零件解析(WASM HTTP runner 白名單)', () => { it('wasmWorkerUrl 通用推導:code → arcrun-code.{subdomain}.workers.dev(self-hosted 換 subdomain 即成立)', () => { expect(wasmWorkerUrl('code', 'uncle6-me')).toBe('https://arcrun-code.uncle6-me.workers.dev'); expect(wasmWorkerUrl('code', 'my-selfhosted-sub')).toBe('https://arcrun-code.my-selfhosted-sub.workers.dev'); }); it('component: code 能被解析成 runner,且 fetch 打 subdomain 推導的 URL', async () => { const fakeEnv = { ...env, WORKER_SUBDOMAIN: 'test-sub', } as unknown as Bindings; const loader = createComponentLoader(fakeEnv); // 白名單漏 `code` 時這裡就 throw「找不到零件 "code"」(#29 的故障型態) const runner = await loader('code'); expect(typeof runner).toBe('function'); // stub fetch 捕 URL:證明 runner 打的是通用推導 URL,非寫死官方域名 const calledUrls: string[] = []; const fetchSpy = vi.fn(async (url: unknown) => { calledUrls.push(String(url)); return new Response(JSON.stringify({ success: true, data: { ok: true } }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }); vi.stubGlobal('fetch', fetchSpy); const result = await runner({ code: 'return {ok:true}', input: {} }) as Record; expect(fetchSpy).toHaveBeenCalledTimes(1); expect(calledUrls[0]).toBe('https://arcrun-code.test-sub.workers.dev'); expect(result.success).toBe(true); }); });