Files
tea-sdlc/test/coding-standards-assets.test.js
jiantw83andClaude Opus 5 8ea6a2a8b3 test(逐項實作): 覆蓋勾選的五種危險、兩份規則正本與正本第二段
勾選的測試全部繞著「會不會改錯行」打轉,五種都是 code review 抓出來的實際缺陷:
圍欄裡長得像 checkbox 的那一行不會被改到、不同段落的同一句話靠 --section 分得開、
大寫 [X] 重跑是 no-op、方框後沒有空白照樣勾得到、沒有方框的項目給的是指路的錯誤
而不是謊報已勾過。

另外釘住抽取端與勾選端的一致性:wp-extract 交得出來的每一種 raw,--tick 都要收得下。

patchOf 收進 helpers——它先前在三個測試檔裡各有一份一模一樣的定義。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 07:39:54 +00:00

137 lines
5.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 實作規範與註解格式對照表這兩份規則正本。
*
* 它們是 /sdlc-feat 第二段實際交付的東西:規範寫漏一條,產出的程式碼就少一種註解,
* 而那要等 reviewer 看到才會發現。對照表少一種語言,agent 就會開始猜格式。
*/
import test from 'node:test';
import assert from 'node:assert/strict';
import { readReference } from './helpers/prompt-doc.js';
const standards = readReference('coding-standards');
const styles = readReference('comment-styles');
/**
* 切出一個 `## 標題` 段落。
* 以整行比對而不是 indexOf:`## Java` 是 `## JavaScript` 的前綴,
* 用 indexOf 會切到錯的那一節,而且切出來還是有內容的,錯得很安靜。
*/
function sectionOf(doc, heading) {
const lines = doc.split('\n');
const start = lines.findIndex((line) => line.trim() === `## ${heading}`);
if (start === -1) return null;
const rest = lines.slice(start + 1);
const end = rest.findIndex((line) => line.startsWith('## '));
return (end === -1 ? rest : rest.slice(0, end)).join('\n');
}
// ── 實作規範 ───────────────────────────────────────────────────────
test('六種專案檔都對得到語言', () => {
for (const file of [
'\\*\\.csproj',
'composer\\.json',
'package\\.json',
'go\\.mod',
'pom\\.xml',
'pyproject\\.toml',
]) {
assert.match(standards, new RegExp(file), `專案檔對照缺少 ${file}`);
}
});
test('認不出語言時要停下來問,而且說明了為什麼不猜', () => {
assert.match(standards, /認不出來就停下來問/);
assert.match(standards, /不要猜/);
assert.match(standards, /比沒有註解更難清理/, '要說出猜錯的代價,否則這條規則會被當成客套話');
});
test('分層判定明講看職責不看目錄', () => {
assert.match(standards, /看職責,不看目錄/);
assert.match(standards, /目錄名稱會騙人/);
});
test('三層各自要寫哪一種註解都寫明了', () => {
for (const [layer, comment] of [
['控制層', '功能註解'],
['服務層', '邏輯註解'],
['存取層', '資料源註解'],
]) {
const row = standards.split('\n').find((line) => line.includes(layer) && line.includes('|'));
assert.ok(row, `${layer}沒有出現在分層表裡`);
assert.match(row, new RegExp(comment), `${layer}要寫的是${comment}`);
}
});
test('服務層要標註呼叫的方法,並說明理由是追呼叫鏈', () => {
assert.match(standards, /標註它呼叫的所有方法/);
assert.match(standards, /追得到呼叫鏈/);
});
test('屬性註解要遞迴,而且明講不能只註解最外層', () => {
assert.match(standards, /屬性本身是類別時遞迴處理/);
assert.match(standards, /不能只註解最外層/);
});
test('資料範例的來源有優先序,且未經驗證時要註明', () => {
assert.match(standards, /優先從 MCP 取得/);
assert.match(standards, /由邏輯推理、未經驗證/);
assert.match(standards, /有人會照著那個格式寫解析/, '要說出不註明的代價');
});
test('明講不寫入目標專案的任何檔案', () => {
assert.match(standards, /不寫入目標專案的任何檔案/);
assert.match(standards, /CLAUDE\.md/);
});
// ── 註解格式對照表 ─────────────────────────────────────────────────
test('六種語言各有一節,且都附可照抄的程式碼範例', () => {
for (const [language, marker] of [
['C#', '///'],
['PHP', '@var'],
['JavaScript/TypeScript', 'JSDoc'],
['Go', 'go doc'],
['Java', 'Javadoc'],
['Python', 'docstring'],
]) {
const body = sectionOf(styles, language);
assert.ok(body, `對照表缺少 ${language}`);
assert.match(body, new RegExp(marker.replace(/[/#]/g, '\\$&')), `${language} 缺少 ${marker}`);
assert.match(body, /```/, `${language} 要有可照抄的範例,不要只用文字描述`);
}
});
test('每個語言的範例都同時示範了方法註解與屬性註解', () => {
const sections = styles.split(/^## /m).filter((s) => s.includes('```'));
for (const section of sections) {
const name = section.split('\n')[0].trim();
if (name === '未經驗證的範例怎麼標') continue;
assert.match(section, /例:|例如/, `${name} 的範例要示範「附真實資料範例」這件事`);
}
});
test('Go 的慣例(以識別字開頭)有被指出來,不是照抄別的語言', () => {
assert.match(sectionOf(styles, 'Go'), /以被註解的識別字開頭/);
});
test('Python 的 docstring 位置有講清楚在定義的下一行', () => {
const python = sectionOf(styles, 'Python');
assert.match(python, /下一行/);
assert.match(python, /不是上一行/, '這是最容易寫錯的一點,要明講');
});
test('未經驗證的註明怎麼寫,兩種語言各有一個可照抄的寫法', () => {
const section = sectionOf(styles, '未經驗證的範例怎麼標');
assert.match(section, /不要另起一行 TODO/);
assert.ok((section.match(/由邏輯推理、未經驗證/g) ?? []).length >= 2, '至少要有兩種語言的寫法');
});
// ── 兩份的分工 ─────────────────────────────────────────────────────
test('規範與格式分開:對照表不重複寫一遍規範', () => {
assert.match(styles, /這份只管\*\*格式\*\*/);
assert.match(standards, /comment-styles\.md/, '規範要指名去哪裡查格式');
});