- transform-acad: 고르기 물음 ALL · L · P = 「n개를 찾음, 총 m개」 · 다시 「객체 선택:」 · 빈 Enter = 고르기 끝(설계 1-4 · 2-15) - transform-tools: 안 쓰는 옛 OFFSET · LENGTHEN 머신 지움(새 offset-tool · lengthen-tool 이 대신) - prompt-input · prompt · sequence-tool: 옛 꼴 통과(meta.options 글 배열 · TEXT_INPUT 대문자 통과 · chainFromLastPoint) 지움 - prompt-contract: 옛 꼴 안 쓰는지 지키는 시험 더함 - 안내 글(설계 4 장 끝): 오른쪽 단추 · 휠 두 번 · CAD 키 줄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
77 lines
3.2 KiB
TypeScript
77 lines
3.2 KiB
TypeScript
/**
|
|
* 337 계약 시험(설계 2-16) — 모든 도구 머신의 평평한 상태가 물음 약속을 지키는지.
|
|
* ① `prompt.options` key 마다 `OPTION.<key>` 또는 `OPTION.*` 처리 ② 한 물음 안 kw 안 겹침(없는 옵션 포함)
|
|
* ③ `meta.kind` 있는 상태마다 ESC ④ `prompt` 와 옛 `instructions` 를 같이 두지 않음 ⑤ 함수 꼴 물음은 빈 context 로 그려짐.
|
|
* 실행: cd B07_DesignDetail/openwebcad && npx vitest run --dir ../../resources/tester/cad prompt-contract
|
|
*/
|
|
import { describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("react-toastify", () => ({
|
|
toast: Object.assign(vi.fn(), { error: vi.fn(), info: vi.fn(), warn: vi.fn(), success: vi.fn() }),
|
|
}));
|
|
|
|
import {
|
|
type PromptMeta,
|
|
resolvePrompt,
|
|
} from "../../../B07_DesignDetail/openwebcad/src/commands/prompt";
|
|
import { TOOL_STATE_MACHINES } from "../../../B07_DesignDetail/openwebcad/src/commands/registry";
|
|
|
|
type StateNode = { config: { meta?: PromptMeta; on?: Record<string, unknown> } };
|
|
|
|
const rows = Object.entries(TOOL_STATE_MACHINES).flatMap(([tool, machine]) =>
|
|
Object.entries((machine as { states: Record<string, StateNode> }).states).map(([name, node]) => ({
|
|
where: `${tool}.${name}`,
|
|
meta: node.config.meta,
|
|
on: node.config.on ?? {},
|
|
})),
|
|
);
|
|
|
|
describe("물음 약속(2-16)", () => {
|
|
it("도구 머신을 읽음", () => {
|
|
expect(rows.length).toBeGreaterThan(100);
|
|
});
|
|
|
|
it("옵션 처리 · kw 안 겹침 · 함수 꼴은 빈 context 로", () => {
|
|
const problems: string[] = [];
|
|
for (const { where, meta, on } of rows) {
|
|
if (!meta?.prompt) continue;
|
|
let spec: ReturnType<typeof resolvePrompt>;
|
|
try {
|
|
spec = resolvePrompt(meta.prompt, {});
|
|
} catch (error) {
|
|
problems.push(`${where}: 빈 context 로 못 그림 (${(error as Error).message})`);
|
|
continue;
|
|
}
|
|
for (const option of spec?.options ?? []) {
|
|
if (!on[`OPTION.${option.key}`] && !on["OPTION.*"]) {
|
|
problems.push(`${where}: OPTION.${option.key} 처리 없음`);
|
|
}
|
|
}
|
|
const kws = [...(spec?.options ?? []), ...(spec?.absent ?? [])].map((option) => option.kw);
|
|
const twice = kws.filter((kw, index) => kws.indexOf(kw) !== index);
|
|
if (twice.length) problems.push(`${where}: kw 겹침 ${twice.join(",")}`);
|
|
}
|
|
expect(problems).toEqual([]);
|
|
});
|
|
|
|
it("meta.kind 있는 상태 = ESC 있음 · prompt 와 instructions 같이 없음", () => {
|
|
const problems: string[] = [];
|
|
for (const { where, meta, on } of rows) {
|
|
if (meta?.kind && !on.ESC) problems.push(`${where}: ESC 없음`);
|
|
if (meta?.prompt && meta.instructions) problems.push(`${where}: prompt + instructions`);
|
|
}
|
|
expect(problems).toEqual([]);
|
|
});
|
|
|
|
it("옛 꼴 통과 안 씀 — 옵션 글 배열 없음 · 숫자 없이 TEXT_INPUT 만 받는 상태 없음(글 물음 빼고)", () => {
|
|
const problems: string[] = [];
|
|
for (const { where, meta, on } of rows) {
|
|
if ((meta as { options?: unknown } | undefined)?.options)
|
|
problems.push(`${where}: meta.options`);
|
|
if (on.TEXT_INPUT && !on.NUMBER_INPUT && meta?.kind !== "text")
|
|
problems.push(`${where}: TEXT_INPUT 만`);
|
|
}
|
|
expect(problems).toEqual([]);
|
|
});
|
|
});
|