test(resource-rule): 半殘情境的 D1 名字改用安裝器真正會取的那個(-db,非 -kbdb)

上一筆 commit 之後才發現的:fixture 的 half-finished 情境沿用了
installed/renamed 的歷史 D1 名(-kbdb),但那顆殘骸是安裝器留下的,
安裝器真正會取的名字是 `${baseName}-db`。名字不對,這個情境就測不到
真的那條路(D1 會被判成「不同名 ⇒ 照舊新建」而不是接回來)。

把 d1Title 加進情境定義、installerRequirements 收一個 d1CreateName 參數,
half-finished 用 -db、其餘照舊 -kbdb。

實跑:node shared/resource-rule/tests/half-finished-install.mjs → 全部通過

Refs: inkstone/Arcrun#123
This commit is contained in:
claude-code
2026-08-14 08:18:46 +00:00
parent 3f2e45f5dc
commit 2b807da324
2 changed files with 9 additions and 6 deletions
@@ -59,7 +59,7 @@ export const BASE_NAME = 'arcrun-rag-yuga3bse';
* @typedef {'fresh' | 'installed' | 'renamed' | 'half-finished'} Scenario
*/
/** @type {Record<Scenario, {label: string, deployed: boolean, resourcesExist?: boolean, titleFor: (binding: string) => string}>} */
/** @type {Record<Scenario, {label: string, deployed: boolean, resourcesExist?: boolean, d1Title?: string, titleFor: (binding: string) => string}>} */
export const SCENARIOS = {
fresh: {
label: '沒裝過(全新帳號,一顆 worker 都沒有)',
@@ -82,6 +82,9 @@ export const SCENARIOS = {
deployed: false,
resourcesExist: true,
titleFor: (b) => `${BASE_NAME}-kv-${b.toLowerCase()}`,
// 這顆殘骸是**安裝器**留下的 ⇒ 名字要照安裝器真正會取的那個(`-db`),
// 不是上面兩個情境沿用的歷史名(`-kbdb`)。名字不對,這個測試就測不到真的那條路。
d1Title: `${BASE_NAME}-db`,
},
};
@@ -93,7 +96,7 @@ export const SCENARIOS = {
*
* @param {boolean} [claimOwnership] 預設 true;傳 false 就是「安裝器忘了聲明」的對照組。
*/
export function installerRequirements(claimOwnership = true) {
export function installerRequirements(claimOwnership = true, d1CreateName = `${BASE_NAME}-kbdb`) {
const out = [];
for (const [worker, need] of Object.entries(WORKER_NEEDS)) {
for (const b of need.kv) {
@@ -106,7 +109,7 @@ export function installerRequirements(claimOwnership = true) {
for (const d of need.d1) {
out.push({
kind: 'd1', binding: d.binding, worker,
createName: `${BASE_NAME}-kbdb`,
createName: d1CreateName,
...(claimOwnership ? { createNameIsOurs: true } : {}),
});
}
@@ -159,7 +162,7 @@ export function makeAccount(scenario) {
kv.set(spec.titleFor(b), id);
kvIdByBinding.set(b, id);
}
d1.set(`${BASE_NAME}-kbdb`, D1_ID);
d1.set(spec.d1Title ?? `${BASE_NAME}-kbdb`, D1_ID);
}
if (spec.deployed) {
@@ -70,7 +70,7 @@ section('① 驗收線本身:半殘帳號 + 安裝器再按一次 → 裝得
const api = apiFor(account, cfRejectsDuplicateNames(account));
// mode='init':安裝器看的是自己的紀錄(`deployed:<account>:`),上次沒裝完就沒有那筆
// ⇒ 這一輪照定義是「新裝」。半殘狀態必須在 init 這條路上就被處理掉。
const plan = await planResources(api, installerRequirements(), 'init');
const plan = await planResources(api, installerRequirements(true, `${BASE_NAME}-db`), 'init');
check(plan.blockers.length === 0, `不該有任何 blocker(實得 ${plan.blockers.length} 條)`);
if (plan.blockers.length) console.log(plan.blockers.map((b) => ` · ${b}`).join('\n'));
@@ -104,7 +104,7 @@ section('② 對照組:修好之前是什麼下場(沒有聲明 createNameIs
// 舊版會直接送 POST,然後被 CF 用「title already exists」打回來。
const account = makeAccount('half-finished');
const api = apiFor(account, cfRejectsDuplicateNames(account));
const plan = await planResources(api, installerRequirements(false), 'init');
const plan = await planResources(api, installerRequirements(false, `${BASE_NAME}-db`), 'init');
check(plan.blockers.length > 0, '證明不了是自己的 → 一定要停手(fail-closed');
check(plan.blockers.join('\n').includes('RES-NAME-TAKEN'), '錯誤碼要在訊息裡(讓用戶回報,而不是自己去後台動手)');