| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- #!/usr/bin/env node
- const {
- buildAcceptanceGates,
- buildRuntimePreflight,
- computeVideoAcceptance
- } = require('./video-hit-rate-audit');
- function main() {
- const passing = buildSummary();
- passing.acceptance = computeVideoAcceptance(passing);
- passing.gates = buildAcceptanceGates(passing);
- assert(passing.acceptance.passed, 'complete video A/B evidence should pass');
- assert(allGatesPass(passing), 'all gates should pass for complete video A/B evidence');
- assert(hasGate(passing, '真实视频 URL 已加载'), 'should gate real video URL loading');
- assert(hasGate(passing, 'Top10 证据命中提升且分数不下降'), 'should gate top10 evidence and score non-degradation');
- assert(hasGate(passing, '强推荐数量不下降'), 'should gate strong recommendation non-degradation');
- assertRuntimePreflight();
- assertFails('missing video url should fail', summary => {
- summary.enhanced.resourceEvidence.videoUrlCount = 0;
- }, 'realVideoResourceLoaded');
- assertFails('provider failure should fail', summary => {
- summary.enhanced.provider.evidence.providerStatus = 'fallback';
- }, 'videoAnalysisProviderOk');
- assertFails('pending evidence cards should fail', summary => {
- summary.enhanced.evidenceQuality.verifiedCardCount = 0;
- }, 'evidenceCardsNotPending');
- assertFails('top10 score degradation should fail', summary => {
- summary.delta.top10AvgScore = -0.1;
- }, 'top10EvidenceImproved');
- assertFails('strong count degradation should fail', summary => {
- summary.delta.strong = -1;
- }, 'strongNotDegraded');
- console.log('video hit-rate audit smoke ok');
- }
- function assertRuntimePreflight() {
- const missing = buildRuntimePreflight({
- runtimeCredential: '',
- company: '',
- vocSocialToken: '',
- videoToken: '',
- videoBaseUrl: 'https://api.fmode.cn',
- videoModel: 'doubao-seed-2-0-pro',
- brief: 'brief.xlsx',
- referenceUrl: 'https://example.com/video',
- resultFirstMode: false
- }, 'out');
- assert(!missing.readyForVideoAb, 'missing runtime inputs should not be ready for video A/B');
- assert(missing.failureCount === 4, 'missing runtime inputs should expose four failed checks');
- assert(missing.proofLevel === 'not_business_proof', 'runtime preflight should not be business proof');
- assert(missing.canCloseProofGap === false, 'runtime preflight cannot close proof gap');
- assert(missing.missingProofRequirements.includes('proofContext.requiresRuntimeCredential=true'), 'should expose runtime credential requirement');
- assert(missing.missingProofRequirements.includes('proofContext.requiresVideoAnalysisProvider=true'), 'should expose video provider requirement');
- assert(missing.proofContext.generatedBy === 'acceptance:video-ab-preflight', 'preflight should not masquerade as acceptance:video-ab');
- const ready = buildRuntimePreflight({
- runtimeCredential: 'runtime-token',
- company: 'company-id',
- vocSocialToken: 'voc-social-token',
- videoToken: 'video-token',
- videoBaseUrl: 'https://api.fmode.cn',
- videoModel: 'doubao-seed-2-0-pro',
- brief: 'brief.xlsx',
- referenceUrl: 'https://example.com/video',
- resultFirstMode: true
- }, 'out');
- assert(ready.readyForVideoAb, 'complete runtime inputs should be ready to run video A/B');
- assert(ready.failureCount === 0, 'complete runtime inputs should have no failed checks');
- assert(ready.canCloseProofGap === false, 'ready preflight still cannot close proof gap');
- assert(ready.nextActions.some(item => item.includes('acceptance:video-ab')), 'ready preflight should point to live A/B run');
- }
- function buildSummary() {
- return {
- baseline: {
- counts: { candidates: 10, strong: 2, evidenceCards: 0, referenceResources: 0 },
- top10: { avgScore: 72, avgReferenceStyleFitScore: 20, evidenceHitCandidates: 0 }
- },
- enhanced: {
- counts: { candidates: 14, strong: 3, evidenceCards: 4, referenceResources: 2 },
- provider: { evidence: { providerStatus: 'ok' } },
- resourceEvidence: {
- referenceResources: 2,
- videoUrlCount: 1,
- coverUrlCount: 1,
- asrTextCount: 0,
- frameUrlCount: 0
- },
- evidenceQuality: {
- verifiedCardCount: 3,
- cardsWithSignals: 3
- },
- top10: { avgScore: 73, avgReferenceStyleFitScore: 24, evidenceHitCandidates: 3 }
- },
- delta: {
- candidates: 4,
- strong: 1,
- evidenceCards: 4,
- referenceResources: 2,
- top10AvgReferenceStyleFitScore: 4,
- top10AvgScore: 1,
- top10EvidenceHitCandidates: 3
- }
- };
- }
- function assertFails(message, mutate, failedKey) {
- const summary = buildSummary();
- mutate(summary);
- summary.acceptance = computeVideoAcceptance(summary);
- summary.gates = buildAcceptanceGates(summary);
- assert(!summary.acceptance.passed, message);
- assert(summary.acceptance[failedKey] === false, `${message}: ${failedKey} should be false`);
- assert(summary.gates.some(gate => gate.status === 'fail'), `${message}: at least one gate should fail`);
- }
- function allGatesPass(summary) {
- return summary.gates.every(gate => gate.status === 'pass');
- }
- function hasGate(summary, name) {
- return summary.gates.some(gate => gate.name === name);
- }
- function assert(condition, message) {
- if (!condition) throw new Error(message);
- }
- if (require.main === module) main();
|