| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- /** Independent PostgreSQL, authenticated query and public-board readback. */
- import assert from 'node:assert/strict';
- import {isDeepStrictEqual} from 'node:util';
- import {load,save,read,publicApi,unwrap} from './lib/research-enrichment.mjs';
- const work=process.env.KSF_CLEANUP_WORK||'work/library-cleanup-20261111';
- const input=load(work+'/sealed-plan.json'),plan=input.plan;
- const applied=unwrap(load(work+'/apply-result.json'));
- const planned=unwrap(load(work+'/plan-result.json'));
- const status=unwrap(load(work+'/status-result.json'));
- const before=unwrap(load(work+'/inventory-result.json'));
- const after=unwrap(load(work+'/inventory-after-result.json'));
- assert(applied.ok&&applied.applied&&applied.allRowsVerified);
- assert.equal(applied.planSha256,input.planSha256);
- assert.equal(applied.generation,planned.generation);
- assert.equal(applied.fingerprint,planned.fingerprint);
- assert(status.publicationFound&&status.allRowsVerified);
- assert.equal(status.currentGeneration,applied.generation);
- assert.equal(status.publishedGeneration,applied.generation);
- assert.equal(after.generation,applied.generation);
- const generation=applied.generation;
- function typed(row,entity,manifest){
- const columns=manifest.tables.find(t=>t.name===(entity==='social'?'social_posts':entity)).columns;
- const result={...row};for(const key of ['payload_sha256','source_payload','_ksf_source_rowid'])delete result[key];
- for(const f of columns)if(result[f.name]!=null&&f.type.toUpperCase().includes('INT')){
- result[f.name]=Number(result[f.name]);assert(Number.isSafeInteger(result[f.name]));
- }
- return result;
- }
- const retainedChecks={};
- for(const entity of ['products','social']){
- const deletions=new Set(plan.removals.filter(r=>r.entity===entity).map(r=>r.id));
- const repairs=new Map(plan.repairs.filter(r=>r.entity===entity).map(r=>[r.id,r.changes]));
- const existing=new Map(after[entity].map(r=>[r.id,typed(r,entity,after.manifest)]));
- let count=0;
- for(const row of before[entity]){
- if(deletions.has(row.id)){assert(!existing.has(row.id));continue;}
- assert(isDeepStrictEqual(existing.get(row.id),{...typed(row,entity,before.manifest),...repairs.get(row.id)}),'RETAINED_RECORD_MISMATCH:'+row.id);
- count++;
- }
- assert.equal(existing.size,count);retainedChecks[entity]=count;
- }
- for(const name of ['assets','source_records','source_links','source_origins','collection_requests','policy_documents','policy_clauses','prices','ranking_signals']){
- const b=before.manifest.tables.find(t=>t.name===name),a=after.manifest.tables.find(t=>t.name===name);
- assert.equal(a.sha256,b.sha256,'UNCHANGED_TABLE_HASH:'+name);assert.equal(a.count,b.count);
- }
- const contract=await read('contract');save(work+'/contract-after.json',contract);
- assert.equal(contract.generation,generation);
- const summary=await publicApi({op:'summary',generation});save(work+'/public-summary-after.json',summary);
- assert.equal(summary.generation,generation);
- for(const [table,count] of Object.entries(contract.counts)){
- assert.equal(count,applied.plannedCounts[table]);assert.equal(summary.counts[table],count);
- }
- const checks=[];
- for(const entity of ['products','social']){
- const ids=plan.removals.filter(r=>r.entity===entity).map(r=>r.id);
- if(!ids.length)continue;
- const page=await publicApi({op:'browse',entity,quality:'audit',limit:100,filters:{recordId:ids},generation});
- assert.equal(page.totalMatched,0);assert.equal(page.records.length,0);
- save(work+'/public-removed-'+entity+'.json',page);checks.push({entity,removedIdsVerified:ids.length});
- }
- const productIds=plan.removals.filter(r=>r.entity==='products').map(r=>r.id);
- for(const entity of ['reviews','specs','coverage']){
- if(!productIds.length)continue;
- const page=await publicApi({op:'browse',entity,quality:'audit',limit:100,filters:{productId:productIds},generation});
- assert.equal(page.totalMatched,0);checks.push({entity,orphanDependents:0});
- }
- let repairsChecked=0;
- for(let start=0;start<plan.repairs.length;start+=100){
- const group=plan.repairs.slice(start,start+100);
- const page=await publicApi({op:'browse',entity:'social',quality:'audit',limit:100,filters:{recordId:group.map(r=>r.id)},generation});
- assert.equal(page.records.length,group.length);assert.equal(page.totalMatched,group.length);
- for(const r of page.records){assert.equal(r.fulltext_available,0);assert.equal(r.preview,'');}
- repairsChecked+=group.length;
- save(work+'/public-repairs-'+start+'.json',page);
- }
- const protectedPage=await publicApi({op:'browse',entity:'social',quality:'audit',limit:100,filters:{recordId:plan.protectedSocial.map(r=>r.id)},generation});
- assert.equal(protectedPage.totalMatched,plan.protectedSocial.length);
- assert(protectedPage.records.every(r=>r.fulltext_available===1));
- save(work+'/public-protected-articles.json',protectedPage);
- const sourceId='91cf603c247bcd1ac5a56c96fa88cc38a5a25b70f501bef50606c5e9d102499d';
- const source=await read('source',{id:sourceId,generation});save(work+'/removed-example-source-preserved.json',source);
- assert(source.ok&&source.sourcePayload);assert.equal(source.citation.id,sourceId);
- const result={ok:true,checkedAt:new Date().toISOString(),generation,removedProducts:applied.removedProducts,removedSocial:applied.removedSocial,
- repairedFulltextFlags:repairsChecked,retainedRecordsIndependentlyVerified:retainedChecks,protectedArticlesVerified:plan.protectedSocial.length,
- allRowsVerified:true,publicBoardReadbackVerified:true,sourcePreserved:true,historyPreserved:true,checks,counts:contract.counts,
- criteria:['清理明确无关的饮料、邮费链接和娱乐内容','未将资料缺字段或访问受限视为删除依据','保留有研发参考价值的调味、原料及真实测评线索'],
- limits:['仍有仅标题或待核线索保留在全部资料中,未冒充可读正文;未判定为全库资料逐条有效。']};
- save(work+'/verification-result.json',result);console.log(JSON.stringify(result));
|