feat(aihr): complete phase two pilot readiness

This commit is contained in:
2026-07-10 19:13:31 +08:00
parent 0219de6fa8
commit 13230e6b9a
48 changed files with 7040 additions and 1647 deletions
@@ -0,0 +1,145 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { parseCsv, verifyPilotExportContract } from '../lib/pilot-export-contract.mjs';
const startDate = '2026-07-07';
const endDate = '2026-07-10';
function validCsv() {
const metrics = {
pilot_start_date: startDate,
pilot_end_date: endDate,
data_scope: 'active_org_unique_identity',
scenario_count: '12',
scenario_count_target: '12',
formal_org_login_count: '20',
formal_pilot_people_count: '20',
formal_pilot_people_target: '20',
formal_pilot_project_count: '1',
formal_pilot_project_target: '1-2',
formal_pilot_scope_ready: 'true',
training_count: '1',
completion_definition: 'at_least_10_sessions_per_person',
completion_people_count: '1',
completion_people_total: '20',
completion_rate: '5.0%',
completion_rate_target: '80.0%',
completion_rate_ready: 'false',
average_score: '86.0',
alert_count: '0',
review_rate: '100.0%',
calibration_sample_count: '1',
ai_human_band_matched_count: '1',
calibration_sample_target: '20',
calibration_sample_ready: 'false',
ai_human_band_consistency_rate: '100.0%',
ai_human_band_consistency_target: '70.0%',
ai_human_band_consistency_ready: 'false',
sop_review_sample_count: '2',
sop_answer_usable_count: '2',
sop_answer_usable_rate: '100.0%',
sop_answer_usable_target: '80.0%',
sop_answer_usable_ready: 'true',
pilot_metric_ready: 'false'
};
const lines = ['metric,value', ...Object.entries(metrics).map(([key, value]) => `${key},${value}`)];
lines.push('', 'formal_ext_party_id,project_codes,session_id,trainee,scene,score,status,finished_time,summary,review_advice,incentive_point');
lines.push('FORMAL-1,P1,session-1,员工甲,投诉处理,86,已复盘,2026-07-08 10:00:00,正式记录,保持回访,3');
return lines.join('\n');
}
test('accepts a complete formal export whose training summary matches its details', () => {
const result = verifyPilotExportContract({
csv: validCsv(),
startDate,
endDate,
contentDisposition: 'attachment; filename=aihr-practice-pilot-2026-07-07-2026-07-10.csv'
});
assert.equal(result.metrics.get('training_count'), '1');
assert.equal(result.detailRows.length, 1);
});
test('rejects a missing metric and a detail row outside the requested date window', () => {
assert.throws(() => verifyPilotExportContract({
csv: validCsv().replace('calibration_sample_count,1\n', ''),
startDate,
endDate,
contentDisposition: 'attachment; filename=aihr-practice-pilot-2026-07-07-2026-07-10.csv'
}), /missing metric calibration_sample_count/);
assert.throws(() => verifyPilotExportContract({
csv: validCsv().replace('2026-07-08 10:00:00', '2026-07-11 10:00:00'),
startDate,
endDate,
contentDisposition: 'attachment; filename=aihr-practice-pilot-2026-07-07-2026-07-10.csv'
}), /outside requested window/);
});
test('rejects contradictory pilot metrics and incomplete formal details', () => {
assert.throws(() => verifyPilotExportContract({
csv: validCsv().replace('completion_people_total,20', 'completion_people_total,19'),
startDate,
endDate,
contentDisposition: 'attachment; filename=aihr-practice-pilot-2026-07-07-2026-07-10.csv'
}), /completion_people_total/);
assert.throws(() => verifyPilotExportContract({
csv: validCsv().replace('training_count,1', 'training_count,2'),
startDate,
endDate,
contentDisposition: 'attachment; filename=aihr-practice-pilot-2026-07-07-2026-07-10.csv'
}), /does not match detail rows/);
assert.throws(() => verifyPilotExportContract({
csv: validCsv().replace('FORMAL-1,P1,session-1', ',P1,session-1'),
startDate,
endDate,
contentDisposition: 'attachment; filename=wrong.csv'
}), /missing formal identity/);
assert.throws(() => verifyPilotExportContract({
csv: validCsv().replace('ai_human_band_consistency_rate,100.0%', 'ai_human_band_consistency_rate,0.0%'),
startDate,
endDate,
contentDisposition: 'attachment; filename=aihr-practice-pilot-2026-07-07-2026-07-10.csv'
}), /ai_human_band_consistency_rate/);
});
test('parses valid quoted CSV fields and rejects malformed quoting', () => {
assert.deepEqual(parseCsv('metric,value\nsummary,"逗号,转义""引号""\n第二行"\n'), [
['metric', 'value'],
['summary', '逗号,转义"引号"\n第二行']
]);
assert.deepEqual(parseCsv('""'), [['']]);
assert.throws(() => parseCsv('metric,value\nsummary,"未闭合'), /unclosed quoted field/);
assert.throws(() => parseCsv('metric,value\nsummary,值"错误'), /quote must start a field/);
assert.throws(() => parseCsv('metric,value\nsummary,"值"错误'), /unexpected character after closing quote/);
assert.throws(() => verifyPilotExportContract({
csv: validCsv().replace('metric,value', '"metric,value"'),
startDate,
endDate,
contentDisposition: 'attachment; filename=aihr-practice-pilot-2026-07-07-2026-07-10.csv'
}), /missing metric,value header/);
});
test('uses raw numerators for threshold readiness instead of rounded percentages', () => {
const csv = validCsv()
.replace('calibration_sample_count,1', 'calibration_sample_count,203')
.replace('ai_human_band_matched_count,1', 'ai_human_band_matched_count,142')
.replace('ai_human_band_consistency_rate,100.0%', 'ai_human_band_consistency_rate,70.0%')
.replace('calibration_sample_ready,false', 'calibration_sample_ready,true')
.replace('sop_review_sample_count,2', 'sop_review_sample_count,2049')
.replace('sop_answer_usable_count,2', 'sop_answer_usable_count,1639')
.replace('sop_answer_usable_rate,100.0%', 'sop_answer_usable_rate,80.0%')
.replace('sop_answer_usable_ready,true', 'sop_answer_usable_ready,false');
assert.doesNotThrow(() => verifyPilotExportContract({
csv,
startDate,
endDate,
contentDisposition: 'attachment; filename=aihr-practice-pilot-2026-07-07-2026-07-10.csv'
}));
});