docs(sdlc): record real test results, open defects ISS-029–032, conditional acceptance

This commit is contained in:
Thanakorn
2026-10-02 17:12:47 +07:00
parent 5d3a7a655b
commit 1a065013b0
138 changed files with 406 additions and 326 deletions
+28 -17
View File
@@ -1,6 +1,6 @@
// SI work products 18–22: user documentation, operation guide, maintenance, verification, validation.
import { P, CODE, ORGANIZER, ORGANIZER_PM, RECORDER, START, END, SCOPE, CRS, CR_GROUPS, TEST_FROM, TEST_TO, ACCEPT_DATE, BASELINE_COMMIT, TEST_STATE_COMMIT, ORIGIN, BACKUP, OPS_CLOSE_DATE } from './project.mjs';
import { UNITS, TEST_CASES, traceOf, unitTopic } from './engineering.mjs';
import { UNITS, TEST_CASES, traceOf, unitTopic, testResult, resultCounts, crStatus, PENDING_UNTIL } from './engineering.mjs';
import { CORRECTIONS, VERIFICATION_ROUNDS, VERIFICATION_RULES } from './history.mjs';
import { bud, thLong, thRangeLong, table, bullets, numbered } from './lib.mjs';
import { workProduct, head } from './format.mjs';
@@ -311,9 +311,9 @@ export function verificationResults() {
['ลงชื่อผู้จัดทำและผู้อนุมัติเรียบร้อย', 'มีตารางลงนามครบถ้วน'],
], 'YaB'],
'WP 9.0': ['ระบบที่ผ่านการทดสอบพร้อมใช้งานจริง', [
['ระบบติดตั้งและใช้งานได้บนสภาพแวดล้อมที่กำหนด', `ติดตั้งสำเร็จและใช้งานได้ Baseline \`${BASELINE_COMMIT}\``],
['ระบบติดตั้งและใช้งานได้บนสภาพแวดล้อมที่กำหนด', `Build \`${TEST_STATE_COMMIT}\` ติดตั้งและใช้งานบน Internal Testing Server ได้ ชุดติดตั้ง Docker ใน Baseline \`${BASELINE_COMMIT}\` ยังไม่ได้ทดสอบ`, 'Failed', 'ทดสอบ TC-UN13.002 และ TC-UN13.003 บน Baseline ก่อนใช้งานจริง'],
['Source Code จัดเก็บใน Repository พร้อมชุดสำรอง', 'จัดเก็บครบทั้ง Repository หลักและสำรอง'],
['ผลการทดสอบระบบผ่านครบทุก Test Case', 'ผ่านครบ 45 Test Case'],
['ผลการทดสอบระบบผ่านครบทุก Test Case', (({ Passed: ok, Failed: bad, 'Not tested': none }) => `ผ่าน ${ok} ไม่ผ่าน ${bad} ยังไม่ได้ทดสอบ ${none} จาก ${TEST_CASES.length} Test Case`)(resultCounts()), 'Failed', 'ข้อบกพร่องคงค้างบันทึกใน Correction Register ต้องมีแผนแก้ไขก่อนตรวจรับ'],
], 'ThS'],
'WP 10.0': ['Product Operation Guide', [
['รายละเอียดและความถูกต้องของ Header ของเอกสาร', 'มีรายละเอียดครบถ้วนถูกต้อง'],
@@ -331,12 +331,16 @@ export function verificationResults() {
return ROUNDS.map((r) => {
const rows = [];
let n = 0;
let failed = 0;
for (const wp of r.wps) {
const [wpName, checks, owner] = CHECKS[wp];
rows.push([`${wp} ${wpName}`, '', '', '', '', '']);
checks.forEach((c, i) => {
n += 1;
rows.push([`VR${String(r.no).padStart(2, '0')}.${wp.replace(/\D/g, '').padStart(3, '0')}.${String(i + 1).padStart(3, '0')}`, c[0], c[1], 'Passed', P[owner].id, '']);
// An item passes unless it records its own result and recommendation.
const [item, evidence, result = 'Passed', comment = ''] = c;
if (result !== 'Passed') failed += 1;
rows.push([`VR${String(r.no).padStart(2, '0')}.${wp.replace(/\D/g, '').padStart(3, '0')}.${String(i + 1).padStart(3, '0')}`, item, evidence, result, P[owner].id, comment]);
});
}
return {
@@ -363,12 +367,12 @@ export function verificationResults() {
'## สรุปผลการตรวจสอบรอบนี้',
table(['รายการ', 'จำนวน'], [
['รายการตรวจสอบทั้งหมด', n],
['ผลผ่าน (Passed)', n],
['ผลไม่ผ่าน (Failed)', 0],
['ประเด็นคงค้าง', 0],
['ผลผ่าน (Passed)', n - failed],
['ผลไม่ผ่าน (Failed)', failed],
['ประเด็นคงค้าง', failed],
], ['', 'r']),
r.no === 4
? `รอบตรวจสอบนี้เป็นรอบสุดท้ายก่อนส่งมอบ ครอบคลุมสิ่งส่งมอบทั้งหมด ${r.wps.length} รายการ ผลการตรวจสอบผ่านทุกรายการ จึงเห็นควรให้ดำเนินการตรวจรับส่งมอบระบบตามที่บันทึกใน Acceptance Report`
? `รอบตรวจสอบนี้เป็นรอบสุดท้ายก่อนส่งมอบ ครอบคลุมสิ่งส่งมอบทั้งหมด ${r.wps.length} รายการ ผ่าน ${n - failed} รายการ ไม่ผ่าน ${failed} รายการ คือผลการทดสอบระบบที่ยังมีข้อบกพร่องคงค้าง และชุดติดตั้ง Docker ที่ยังไม่ได้ทดสอบ จึงเสนอให้ผู้รับมอบพิจารณาการตรวจรับพร้อมแผนแก้ไขข้อบกพร่องคงค้าง`
: `ผลการตรวจสอบรอบนี้ผ่านทุกรายการ ประเด็นที่พบระหว่างการตรวจสอบได้รับการแก้ไขและบันทึกใน Correction Register แล้ว การตรวจสอบรอบถัดไปจะครอบคลุมสิ่งส่งมอบที่จัดทำเพิ่มเติมในช่วงถัดไป`,
].join('\n\n'),
secretary: P.PaNg, reviewer: P.YaB, approver: P.SeV,
@@ -406,15 +410,20 @@ export function validationResults() {
const evidence = (id) => {
const t = traceOf(id);
const ev = AT_UAT[id] ?? t.ev;
return [t.tcs.length ? t.tc : '', ev].filter(Boolean).join(' / ');
const issues = [...new Set(t.tcs.map((tc) => testResult(tc).issue).filter(Boolean))];
const pending = crStatus(id, date) === 'Pending' ? PENDING_UNTIL[id][1] : '';
return [t.tcs.length ? t.tc : '', ev, issues.length ? `ข้อบกพร่องคงค้าง ${issues.join(', ')}` : '', pending].filter(Boolean).join(' / ');
};
const status = (id) => crStatus(id, date);
const rows = [];
const summary = [];
for (const [gid, gname, items] of CR_GROUPS) {
rows.push([gid, `**${gname}**`, '', '', '', '']);
for (const [id, text] of items) rows.push([id, text, METHOD[gid], evidence(id), 'Passed', P.SeV.id]);
summary.push([`${gid} ${gname}`, items.length, items.length, 0]);
for (const [id, text] of items) rows.push([id, text, METHOD[gid], evidence(id), status(id), P.SeV.id]);
const n = (s) => items.filter(([id]) => status(id) === s).length;
summary.push([`${gid} ${gname}`, items.length, n('Passed'), n('Failed'), n('Not tested') + n('Pending')]);
}
const all = (s) => CRS.filter(([id]) => status(id) === s).length;
if (Object.keys(METHOD).length !== CR_GROUPS.length) throw new Error('Every CR group needs a validation method.');
return {
dir: '2-SI Process (12 Work Product)/22.Validation Result',
@@ -438,21 +447,23 @@ export function validationResults() {
['ผู้บันทึกผล', `${P.PaNg.th} (QA/Tester)`],
['ช่วงเวลาทดสอบ', thRangeLong(TEST_FROM, TEST_TO)],
['ขอบเขตการทดสอบ', `Customer Requirements ครบ ${CR_GROUPS.length} หมวด ${CRS.length} รายการ (CR01–CR14)`],
['สถานะ Repository ที่ใกล้เคียงที่สุด', `Commit \`${TEST_STATE_COMMIT}\``],
['Build ที่ใช้ทดสอบ', `Commit \`${TEST_STATE_COMMIT}\` บน Branch main`],
['ลำดับการทดสอบ', 'ทดสอบการยอมรับคู่ขนานกับการทดสอบระบบตามมติที่ประชุม 10 ส.ค. 69 ซึ่งต่างจากเกณฑ์เข้าสู่ UAT ใน Test Case and Test Procedures'],
]),
'## ตารางสรุป Validation Result ตาม Customer Requirements',
table(['CR ID', 'Customer Requirement', 'วิธีการยืนยัน', 'หลักฐานอ้างอิง', 'Status', 'Tester'], rows, ['', '', '', '', 'c', 'c']),
'## สรุปผลการทดสอบการยอมรับรายหมวด',
table(['หมวดความต้องการ', 'จำนวนรายการ', 'ผ่าน (Passed)', 'ไม่ผ่าน (Failed)'], summary, ['', 'r', 'r', 'r']),
table(['หมวดความต้องการ', 'จำนวนรายการ', 'ผ่าน (Passed)', 'ไม่ผ่าน (Failed)', 'ยังไม่ได้ทดสอบ / รอดำเนินการ'], summary, ['', 'r', 'r', 'r', 'r']),
'## สรุปผลการทดสอบการยอมรับ',
table(['รายการ', 'จำนวน'], [
['หมวดความต้องการที่ทดสอบ', CR_GROUPS.length],
['ความต้องการที่ทดสอบทั้งหมด', CRS.length],
['ผ่าน (Passed)', CRS.length],
['ไม่ผ่าน (Failed)', 0],
['ค้างการทดสอบ', 0],
['ผ่าน (Passed)', all('Passed')],
['ไม่ผ่าน (Failed)', all('Failed')],
['ยังไม่ได้ทดสอบ (ฟังก์ชันส่งมอบหลังรอบทดสอบ)', all('Not tested')],
['รอดำเนินการตามแผน (Pending)', all('Pending')],
], ['', 'r']),
`ตัวแทนลูกค้าได้ทดสอบการยอมรับครบทุกความต้องการใน Customer Requirements บนสภาพแวดล้อมใช้งานจริง ผลการทดสอบผ่านทั้งหมด ไม่พบประเด็นที่ขัดขวางการเปิดใช้งาน จึงยืนยันความพร้อมของระบบและเสนอเข้าสู่การตรวจรับส่งมอบตามแผนในวันที่ ${thLong(ACCEPT_DATE)}`,
`ตัวแทนลูกค้าทดสอบการยอมรับครบ ${CRS.length} ความต้องการ ผ่าน ${all('Passed')} รายการ ความต้องการ ${all('Failed')} รายการไม่ผ่านเนื่องจากข้อบกพร่องคงค้างที่บันทึกใน Correction Register ${all('Not tested')} รายการขึ้นกับชุดติดตั้ง Docker ที่ส่งมอบหลังรอบทดสอบ และ ${all('Pending')} รายการรองานปฏิบัติการตามแผน ผู้รับมอบรับทราบผลและจะพิจารณาการตรวจรับพร้อมแผนแก้ไขในวันที่ ${thLong(ACCEPT_DATE)}`,
].join('\n\n'),
secretary: P.PaNg, reviewer: P.NoC, approver: P.SeV,
}),