4aacd79e00
出貨線的版本號由內容指紋算出(內容一變版本必變),build 站核對官方成品指紋 也要它。此前 registry 裡只有 code 節點提到 hash,沒有專門零件——純計算本該是 一顆零件,不是一段腳本(D70)。 - registry/components/hash/:TinyGo 實作,sha256/sha1/md5,hex/base64 輸出 - .component-builds/hash/:部署包(component.wasm 已 commit,比照 rule 05) - cypher-executor/src/lib/component-loader.ts:WASM_HTTP_RUNNER_IDS 白名單加 'hash',否則 `component: hash` 在任何實例上都會落到 step 8「找不到零件」 (與 #29 的 code 缺口同形狀);對應單元測試 cypher-executor/tests/component-loader-hash.test.ts - pending-human-gate/README.md:hash 部分已落地移除,#89 維持原狀 驗證(見 PR 說明附完整輸出): - wasmtime 本地跑六種 gherkin 情境,結果與系統 shasum/md5 逐位元一致 - 部署到 youlin 測試場(arcrun-hash.youlin-hsieh-dev.workers.dev),POST 直打 六種情境同樣一致 - 在真實工作流(hash-e2e-test-91,youlin 測試場觸發)跑過,含 component-loader 白名單修法的單元測試(vitest + cloudflare:test,本地綠燈) 查證「零件投稿要人工互動解閘」一事:scripts/component-arm.sh、 .claude/hooks/component-guard.sh 均不存在;pre-write-guard.sh 對 registry/components/ 的限制只擋 TS 檔與 auth_* 命名放錯目錄,不擋本次改動。 沒有那個「只有人做得到」的具體物件,故未卡關直接走完。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
60 lines
2.7 KiB
TypeScript
60 lines
2.7 KiB
TypeScript
/**
|
||
* component-loader:`hash` 零件解析測試(Arcrun#91)。
|
||
*
|
||
* 背景:workflow 寫 `component: hash` 時,component-loader 解析鏈 step 1-6 都不命中
|
||
* (非 builtin / 非 URL / 非 hash-id / 非 LOGIC_BINDING_MAP 邏輯零件 / 非 recipe),
|
||
* 必須靠 step 7 的 WASM_HTTP_RUNNER_IDS 白名單 → wasmWorkerUrl 通用推導。
|
||
* 白名單漏 `hash` 就會落到 step 8 直接丟「找不到零件」——與 #29 的 `code` 缺口同形狀。
|
||
*
|
||
* 驗證:
|
||
* 1. `hash` 能被解析成 runner(不 throw)。
|
||
* 2. runner 打的 URL = arcrun-hash.{WORKER_SUBDOMAIN}.workers.dev(通用推導、無寫死
|
||
* 官方 hash.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:hash 零件解析(WASM HTTP runner 白名單,Arcrun#91)', () => {
|
||
it('wasmWorkerUrl 通用推導:hash → arcrun-hash.{subdomain}.workers.dev(self-hosted 換 subdomain 即成立)', () => {
|
||
expect(wasmWorkerUrl('hash', 'uncle6-me')).toBe('https://arcrun-hash.uncle6-me.workers.dev');
|
||
expect(wasmWorkerUrl('hash', 'my-selfhosted-sub')).toBe('https://arcrun-hash.my-selfhosted-sub.workers.dev');
|
||
});
|
||
|
||
it('component: hash 能被解析成 runner,且 fetch 打 subdomain 推導的 URL', async () => {
|
||
const fakeEnv = {
|
||
...env,
|
||
WORKER_SUBDOMAIN: 'test-sub',
|
||
} as unknown as Bindings;
|
||
|
||
const loader = createComponentLoader(fakeEnv);
|
||
// 白名單漏 `hash` 時這裡就 throw「找不到零件 "hash"」
|
||
const runner = await loader('hash');
|
||
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: { result: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824', algorithm: 'sha256', encoding: 'hex' },
|
||
}),
|
||
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
||
);
|
||
});
|
||
vi.stubGlobal('fetch', fetchSpy);
|
||
|
||
const result = await runner({ algorithm: 'sha256', input: 'hello', encoding: 'hex' }) as Record<string, unknown>;
|
||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||
expect(calledUrls[0]).toBe('https://arcrun-hash.test-sub.workers.dev');
|
||
expect(result.success).toBe(true);
|
||
});
|
||
});
|