verify-consumer-quote-inputs.mjs 6.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. // Round-trip the actual fixed inputs through the real wrapper/backend in memory only.
  2. import fs from 'node:fs';
  3. import crypto from 'node:crypto';
  4. import assert from 'node:assert/strict';
  5. import {createApplication} from '../server/src/core.mjs';
  6. import {runWorkshop} from '../skills/ksf-workshop-runtime/scripts/ksf-workshop.mjs';
  7. const data=JSON.parse(fs.readFileSync('server/references/runtime-data.json','utf8'));
  8. const rows={},copy=structuredClone;let seq=0;
  9. const store={
  10. async transaction(fn){const before=copy(rows);try{return await fn(this);}catch(e){for(const key of Object.keys(rows))delete rows[key];Object.assign(rows,before);throw e;}},
  11. async lock(){},
  12. async get(name,id){return copy(rows[name]?.find(r=>r.objectId===id)||null);},
  13. async list(name,{where={},afterId,limit=200}={}){return copy((rows[name]||[]).filter(r=>Object.entries(where).every(([key,v])=>JSON.stringify(r[key]??null)===JSON.stringify(v))).filter(r=>!afterId||r.objectId>afterId).sort((a,b)=>a.objectId.localeCompare(b.objectId)).slice(0,limit));},
  14. async create(name,fields){for(const key of Object.keys(fields))assert.ok(data.tables[name][key],name+'.'+key);const row={...copy(fields),objectId:'local'+String(++seq).padStart(7,'0'),createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};(rows[name]??=[]).push(row);return copy(row);},
  15. async update(name,id,fields){for(const key of Object.keys(fields))assert.ok(data.tables[name][key],name+'.'+key);const row=rows[name]?.find(r=>r.objectId===id);assert.ok(row);Object.assign(row,copy(fields));return copy(row);}
  16. };
  17. const app=createApplication({store,crypto,Parse:{},data,settings:{signingSecret:'local-verification-only'},files:{}});
  18. const actor={id:'local-input-check',kind:'service',scopes:['source:write','data:read','ingest:write'],projectIds:['*']};
  19. const call=async(name,p)=>(await app.dispatch(name,JSON.parse(JSON.stringify(p)),actor)).data;
  20. // This fixture never leaves memory and is not an employee or registered production Agent.
  21. const agentProfile={key:'agent-account:'+'a'.repeat(64),name:'本地输入校验夹具',role:'workshop',identityBasis:'runtime_config',runtimePlatform:'hermes',channel:'wecom',registeredAt:'2026-11-02T00:00:00Z',account:{platform:'wecom',accountId:'fixture-local-only'}};
  22. const checks=[],inputHashes={};
  23. for(const key of ['cq-01','cq-02','cq-03','cq-04']){
  24. const input=fs.readFileSync('work/consumer-quote-inputs-20261102/'+key+'.input.json');
  25. inputHashes[key+'.input.json']=crypto.createHash('sha256').update(input).digest('hex');
  26. const task=JSON.parse(input),record=task.records[0],locator=record.data.locator;
  27. assert.equal(task.source.sourceKind,'social');assert.equal(task.source.dataMode,'live');assert.equal(task.source.platform,'smzdm');
  28. assert.equal(task.records.length,1);assert.equal(record.className,'KfEvidence');
  29. assert.equal(record.data.productRef,undefined);assert.equal(record.data.sourceRef,undefined);
  30. assert.equal(record.data.publishedAt,null);assert.equal(locator.publicationTimezone,'unknown');
  31. assert.equal(locator.purchaseVerified,false);assert.equal(locator.realIdentityVerified,false);
  32. assert.equal(record.data.collectedAt,locator.browserReadAt);assert.equal(task.source.collectedAt,locator.httpCollectedAt);
  33. assert.equal(record.data.evidenceKind,'public_user_experience_self_report');
  34. assert.equal([...task.source.rawText].slice(locator.startCharacter,locator.endCharacter).join(''),record.data.quote);
  35. const sourcesBefore=(rows.KfSource??[]).length,evidenceBefore=(rows.KfEvidence??[]).length;
  36. const preflight=await runWorkshop('workshop-dedup',task,{requestId:key+':preflight',call});
  37. assert.equal(preflight.readOnly,true);assert.equal((rows.KfSource??[]).length,sourcesBefore);assert.equal((rows.KfEvidence??[]).length,evidenceBefore);
  38. assert.equal(preflight.source.operation,'no-exact-match');assert.equal(preflight.items[0].reason,'SOURCE_NOT_REGISTERED');
  39. const result=await runWorkshop('workshop-import',task,{requestId:key+':import',agentProfile,call});
  40. assert.equal(result.status,'succeeded');assert.equal(result.items.length,1);assert.equal(result.counts.created,1);
  41. const source=(await runWorkshop('workshop-read',{className:'KfSource',objectId:result.sourceId},{requestId:key+':read-source',call})).record;
  42. const evidence=(await runWorkshop('workshop-read',{className:'KfEvidence',objectId:result.items[0].objectId},{requestId:key+':read-evidence',call})).record;
  43. for(const [field,value]of Object.entries(task.source))assert.deepEqual(source[field],value,key+' source '+field);
  44. for(const [field,value]of Object.entries(record.data))assert.deepEqual(evidence[field],value,key+' evidence '+field);
  45. assert.equal(evidence.sourceRef.objectId,result.sourceId);assert.equal(evidence.productRef??null,null);assert.equal(evidence.publishedAt,null);
  46. const duplicate=await runWorkshop('workshop-dedup',task,{requestId:key+':post-preflight',call});
  47. assert.equal(duplicate.source.operation,'unchanged');assert.equal(duplicate.items[0].decision,'reuse');
  48. const replay=structuredClone(task);replay.records[0].externalKey+=':local-reuse-check';
  49. const reused=await runWorkshop('workshop-import',replay,{requestId:key+':reuse-check',agentProfile,call});
  50. assert.equal(reused.sourceOperation,'unchanged');assert.equal(reused.counts.created,0);assert.equal(reused.counts.unchanged,1);
  51. assert.equal(reused.sourceId,result.sourceId);assert.equal(reused.items[0].objectId,result.items[0].objectId);
  52. checks.push({key,inputMatchesSchema:true,actualWrapperImportAndReadback:true,unicodeQuoteLocatorMatched:true,
  53. actualCollectionTimesPreserved:true,unknownPublicationTimezonePreserved:true,productUnlinked:true,duplicateReused:true});
  54. }
  55. assert.equal(rows.KfSource.length,4);assert.equal(rows.KfEvidence.length,4);
  56. for(const name of ['KfCategory','KfProduct','KfSession','KfArtifact','KfProject','KfOpportunity','KfConcept'])assert.equal((rows[name]??[]).length,0,name);
  57. const result={ok:true,operation:'consumer-quote-inputs-validated-locally',at:new Date().toISOString(),scope:'real backend and wrapper, isolated in-memory store',
  58. checks,inputHashes,networkRequests:0,productionBusinessWrites:0,localSources:4,localEvidence:4,localProducts:0,localReports:0};
  59. const output='work/consumer-quote-inputs-verified-20261102.json';assert.equal(fs.existsSync(output),false,'Retain previous verification');
  60. fs.writeFileSync(output,JSON.stringify(result,null,2)+'\n',{flag:'wx'});
  61. console.log(JSON.stringify(result,null,2));