// Independently inspect existing records. Only GETs and ksfGetRecord are allowed here. import fs from 'node:fs'; import crypto from 'node:crypto'; import assert from 'node:assert/strict'; import {invoke,classRows} from '../server/scripts/platform-client.mjs'; const taskKey='KSF-商品页面观察-20261102-01'; const agentKey='agent-account:7c9f85363ca4c5a529a5b363308b065998924e2394e846145a5c62c904506d2e'; const inputsDirectory='work/current-product-observations-20261102/'; const archiveFile='work/current-product-archive-20261102.zip'; const expectedArchive='0ec14dcccbce26bc0fc1a368bfaef4bbc32ea6f0b651ae31df5dd28a8bc0e7a5'; const imported=[ {key:'suning',sourceId:'pBQEl6tlfY',evidenceId:'cTo8j9FdIJ',jobId:'QF1TFQG7bY'}, {key:'huajie',sourceId:'yo2mA_FByU',evidenceId:'dU0wGRdC0D',jobId:'RQoaUYKU8l'}, ]; const ptr=(className,objectId)=>({__type:'Pointer',className,objectId}); const sha=bytes=>crypto.createHash('sha256').update(bytes).digest('hex'); const read=async(className,objectId)=>{ const r=await invoke('ksfGetRecord',{className,objectId}); assert.equal(r.http,200);assert.equal(r.body?.result?.ok,true); return r.body.result.data.record; }; let check='local archive'; try{ const archive=fs.readFileSync(archiveFile); assert.equal(archive.length,614046);assert.equal(sha(archive),expectedArchive); const packageReceipt=JSON.parse(fs.readFileSync('work/current-product-observations-package-receipt.json','utf8')); const reports=[]; for(const ids of imported){ check=ids.key+' records'; const inputBytes=fs.readFileSync(inputsDirectory+ids.key+'.input.json'); assert.equal(sha(inputBytes),packageReceipt.inputHashes[ids.key+'.input.json']); const input=JSON.parse(inputBytes),row=input.records[0]; const [source,evidence,job]=await Promise.all([ read('KfSource',ids.sourceId),read('KfEvidence',ids.evidenceId),read('KfImportJob',ids.jobId), ]); for(const [field,value]of Object.entries(input.source))assert.deepEqual(source[field]??null,value,ids.key+' source '+field); assert.equal(source.contentHash,sha(input.source.rawText)); assert.equal(source.recordState,'active'); for(const [field,value]of Object.entries(row.data))assert.deepEqual(evidence[field]??null,value,ids.key+' evidence '+field); assert.deepEqual(evidence.sourceRef,ptr('KfSource',ids.sourceId)); assert.equal(evidence.productRef??null,null);assert.equal(evidence.artifactRef??null,null); assert.equal(evidence.recordState,'active'); assert.equal(evidence.evidenceKind,'retailer_listing_observation'); assert.equal(evidence.verificationState,'archived_text_and_screenshot_checked'); assert.equal(evidence.collectedAt??null,null);assert.equal(source.collectedAt??null,null); assert.equal(evidence.locator.archiveSha256,sha(archive)); for(const file of [evidence.locator.textOrigin,evidence.locator.screenshot]){ assert.equal(sha(fs.readFileSync('work/current-product-archive-20261102/originals/'+file.file)),file.sha256); } const locator=evidence.locator; assert.equal([...source.rawText].slice(locator.startCharacter,locator.endCharacter).join(''),evidence.quote); assert.equal(locator.exactCaptureTimestampVerified,false); assert.equal(job.status,'succeeded');assert.equal(job.expectedCount,1);assert.equal(job.successCount,1);assert.equal(job.failureCount,0); assert.deepEqual(job.inputSourceRef,ptr('KfSource',ids.sourceId)); assert.equal(job.producer.agent.key,agentKey);assert.equal(job.producer.agent.name,'王刚001'); check=ids.key+' uniqueness and scope'; const [sources,evidences,jobs,items,products,artifacts]=await Promise.all([ classRows('KfSource',{contentHash:source.contentHash,sourceUrl:source.sourceUrl,platform:source.platform,dataMode:'live',recordState:'active'}), classRows('KfEvidence',{sourceRef:ptr('KfSource',ids.sourceId)}), classRows('KfImportJob',{inputSourceRef:ptr('KfSource',ids.sourceId)}), classRows('KfImportItem',{jobRef:ptr('KfImportJob',ids.jobId)}), classRows('KfProduct',{sourceRef:ptr('KfSource',ids.sourceId)}), classRows('KfArtifact',{sourceRef:ptr('KfSource',ids.sourceId)}), ]); assert.deepEqual(sources.map(x=>x.objectId),[ids.sourceId]); assert.deepEqual(evidences.map(x=>x.objectId),[ids.evidenceId]); assert.deepEqual(jobs.map(x=>x.objectId),[ids.jobId]); assert.equal(items.length,1);assert.equal(items[0].targetClass,'KfEvidence'); assert.equal(items[0].targetId,ids.evidenceId);assert.equal(items[0].itemKey,row.itemKey); assert.equal(items[0].businessKey,row.externalKey);assert.equal(items[0].status,'succeeded');assert.equal(items[0].operation,'created'); assert.equal(products.length,0);assert.equal(artifacts.length,0); reports.push({...ids,sourceOperation:'created',created:1,unchanged:0,sourceMatchesFixedInput:true,evidenceMatchesFixedInput:true, quoteAndUnicodeLocatorMatched:true,archiveHashAndOriginalHashesVerified:true,exactCaptureTimePreservedUnknown:true, evidenceKind:evidence.evidenceKind,verificationState:evidence.verificationState,locatorFieldCount:Object.keys(locator).length, duplicateSourceCount:sources.length,sourceProductCount:products.length,sourceArtifactCount:artifacts.length,sameAgent:true}); } check='original S1'; const [artifact,versions]=await Promise.all([ read('KfArtifact','G8IrkkDG7X'),classRows('KfArtifact',{reportSeriesKey:'KSF-真实资料-20261102-01:S1'}), ]); const template=JSON.parse(fs.readFileSync('work/first-live-S1-v2-template.json','utf8')); for(const field of ['rawText','version','stageCode','title'])assert.deepEqual(artifact[field],template[field]); assert.deepEqual(artifact.report.evidence,template.report.evidence); assert.deepEqual(artifact.meta.workshop.sourceIds,template.sourceIds); assert.deepEqual(versions.map(x=>[x.objectId,x.version]).sort((a,b)=>a[1]-b[1]),[['cOm6c8gg7z',1],['G8IrkkDG7X',2]]); const result={ok:true,operation:'page-observations-independently-verified',at:new Date().toISOString(),taskKey,readOnly:true,businessWrites:0, dataMode:'live',sourceCount:2,evidenceCount:2,newProductsFromTheseSources:0,newArtifactsFromTheseSources:0,imports:reports, archive:{sha256:expectedArchive,bytes:archive.length,verifiedLocalPath:'F:/kangshifu-board/'+archiveFile,remoteArchivePackageAvailability:'not independently verified'}, originalS1Preserved:true,originalS1ArtifactId:'G8IrkkDG7X',formalReportPublished:false, currentStockVerified:false,unitPriceVerified:false,consumerReviewsCollected:false,huajieCurrencyVerified:false}; fs.writeFileSync('work/page-observations-readback-20261102.json',JSON.stringify(result,null,2)+'\n'); console.log(JSON.stringify(result,null,2)); }catch(e){ const result={ok:false,operation:'page-observations-independent-readback-stopped',check,errorCode:'PAGE_OBSERVATIONS_READBACK_FAILED', checkLine:e.stack?.match(/verify-page-observations-readback\.mjs:(\d+):/)?.[1]??null,readOnly:true,businessWrites:0}; fs.writeFileSync('work/page-observations-readback-20261102.json',JSON.stringify(result,null,2)+'\n'); console.error(JSON.stringify(result,null,2));process.exitCode=1; }