| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- // Validate the real S2 template through the save/read path in isolated memory.
- import fs from 'node:fs';
- import crypto from 'node:crypto';
- import assert from 'node:assert/strict';
- import {createApplication} from '../server/src/core.mjs';
- import {attachWorkshopHandlers} from '../server/src/workshop.mjs';
- import {runWorkshop} from '../skills/ksf-workshop-runtime/scripts/ksf-workshop.mjs';
- const data=JSON.parse(fs.readFileSync('server/references/runtime-data.json','utf8'));
- const templateBytes=fs.readFileSync('work/first-live-S2-template.json'),template=JSON.parse(templateBytes);
- const sha=x=>crypto.createHash('sha256').update(x).digest('hex');
- assert.equal(template.sessionId,null);assert.equal(template.stageCode,'S2');assert.equal(template.version,1);
- assert.equal(template.artifactType,'pain_list');assert.equal(template.taskKey,'KSF-真实资料-20261102-01:S2');
- assert.equal(template.sourceIds.length,4);assert.equal(template.pendingQuestions.length,6);
- assert.equal(template.projectId,undefined);assert.equal(template.opportunity,undefined);
- const rows={},copy=structuredClone;let seq=0;
- const store={
- async transaction(fn){const before=copy(rows);try{return await fn(this);}catch(e){for(const k of Object.keys(rows))delete rows[k];Object.assign(rows,before);throw e;}},
- async lock(){},
- async get(name,id){return copy(rows[name]?.find(r=>r.objectId===id)||null);},
- async list(name,{where={},afterId,limit=200}={}){return copy((rows[name]||[]).filter(r=>Object.entries(where).every(([k,v])=>JSON.stringify(r[k]??null)===JSON.stringify(v))).filter(r=>!afterId||r.objectId>afterId).sort((a,b)=>a.objectId.localeCompare(b.objectId)).slice(0,limit));},
- async create(name,fields){for(const k of Object.keys(fields))assert.ok(data.tables[name][k],name+'.'+k);const row={...copy(fields),objectId:'fixture'+String(++seq).padStart(7,'0'),createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};(rows[name]??=[]).push(row);return copy(row);},
- async update(name,id,fields){for(const k of Object.keys(fields))assert.ok(data.tables[name][k],name+'.'+k);const row=rows[name]?.find(r=>r.objectId===id);assert.ok(row);Object.assign(row,copy(fields));return copy(row);}
- };
- const actor={id:'local-s2-validation',kind:'service',scopes:['ingest:write','data:read'],projectIds:['*']};
- // Local fixtures only: no production identities or endpoint credentials enter this script.
- const profile={key:'agent-account:'+'a'.repeat(64),name:'本地S2校验夹具',identityBasis:'runtime_config',role:'workshop',runtimePlatform:'hermes',channel:'wecom',account:{platform:'wecom',accountId:'fixture-only'}};
- rows.KfSource=template.sourceIds.map((id,index)=>{
- const key='cq-0'+(index+1),input=JSON.parse(fs.readFileSync('work/consumer-quote-inputs-20261102/'+key+'.input.json','utf8'));
- return {...copy(input.source),objectId:id,contentHash:sha(input.source.rawText),schemaVersion:'ksf.v0.7',externalKey:'fixture-source-'+key,reviewState:'pending',recordState:'active',ingestedAt:'2026-11-02T00:00:00Z',meta:{ownerId:actor.id}};
- });
- const sourcesBefore=copy(rows.KfSource);
- const app=createApplication({store,crypto,Parse:{},data,settings:{signingSecret:'local-verification-only'},files:{}});
- attachWorkshopHandlers(app,{store,data});
- const call=async(name,p)=>(await app.dispatch(name,copy(p),actor)).data;
- const session=await call('ksfStartWorkshopSession',{requestId:'fixture-s2-start',workshopKey:'fixture-s2-validation',dataMode:'live',groupId:'',
- participant:{key:'fixture-participant',name:'本地校验',identityBasis:'participant_registration'},
- agent:{key:profile.key,name:profile.name,identityBasis:profile.identityBasis,role:profile.role,runtimePlatform:profile.runtimePlatform,channel:profile.channel,account:profile.account},
- conversation:{key:'fixture-conversation',channel:'wecom'}});
- const task={...copy(template),sessionId:session.sessionId};
- assert.deepEqual(Object.keys(task).filter(k=>JSON.stringify(task[k])!==JSON.stringify(template[k])),['sessionId']);
- const result=await runWorkshop('workshop-save',task,{requestId:'fixture-s2-save',agentProfile:profile,call});
- assert.equal(result.dataMode,'live');assert.equal(result.stageCode,'S2');assert.equal(result.version,1);
- assert.equal(result.boardVisible,true);assert.equal(result.formalReportPublished,false);assert.equal(result.employeeConfirmed,false);
- assert.equal(result.opportunityId,null);assert.equal(result.projectId,null);
- const read=(await runWorkshop('workshop-read',{className:'KfArtifact',objectId:result.artifactId},{requestId:'fixture-s2-read',call})).record;
- for(const field of ['rawText','title','version','stageCode','artifactType'])assert.deepEqual(read[field],task[field]);
- assert.equal(read.dashboardState,'workshop');assert.equal(read.ownerSession,session.sessionId);
- assert.deepEqual(read.meta.workshop.sourceIds,template.sourceIds);assert.deepEqual(read.meta.workshop.pendingQuestions,template.pendingQuestions);
- assert.deepEqual(read.report.executive.slice(1),template.report.executive);
- assert.deepEqual(read.report.chapters,template.report.chapters);assert.deepEqual(read.report.evidence,template.report.evidence);
- assert.equal(read.evidenceCount,4);assert.equal(read.sources.length,4);
- for(const entry of Object.values(read.report.evidence)){
- const origin=rows.KfSource.find(x=>x.objectId===entry.sourceId);assert.ok(origin);
- for(const quote of entry.quotes)assert.ok(origin.rawText.includes(quote.text));
- }
- assert.deepEqual(rows.KfSource.slice(0,4),sourcesBefore);
- assert.equal(rows.KfArtifact.length,1);assert.equal(rows.KfSource.length,5);
- assert.equal((await store.get('KfSession',session.sessionId)).stageCode,'S2');
- for(const name of ['KfOpportunity','KfProject','KfProduct','KfCategory','KfConcept','KfEvidence'])assert.equal((rows[name]??[]).length,0,name);
- const publicBoard=await app.dispatch('ksfGetPublicBoard',{},null);
- assert.ok(publicBoard.data.artifact.some(x=>x.objectId===result.artifactId&&x.stageCode==='S2'&&x.dataMode==='live'));
- const verified={ok:true,operation:'live-S2-template-validated-locally',at:new Date().toISOString(),templateSha256:sha(templateBytes),
- scope:'real backend/workshop wrapper, isolated memory with explicit local fixtures',quoteCount:4,sourceCount:4,pendingQuestionCount:6,
- saveAndReadbackMatched:true,publicBoardDataVisible:true,sourceContentPreserved:true,onlySessionIdChanged:true,
- newLocalArtifacts:1,newLocalReportSources:1,newLocalOpportunities:0,newLocalProjects:0,
- networkRequests:0,productionBusinessWrites:0,targetSaved:false,browserVerified:false};
- const output='work/first-live-S2-local-check.json';assert.equal(fs.existsSync(output),false,'Retain previous check');
- fs.writeFileSync(output,JSON.stringify(verified,null,2)+'\n',{flag:'wx'});console.log(JSON.stringify(verified,null,2));
|