| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 |
- import {test} from 'node:test';
- import assert from 'node:assert/strict';
- import fs from 'node:fs';
- import {validateDevelopmentReferences,researchRecords,directionReferences,materialGroup,
- standardDate,standardUse,developmentBrief,DEVELOPMENT_DIRECTIONS} from '../src/core/development-references.js';
- import {readDevelopmentReferences} from '../src/services/development-service.js';
- const published=JSON.parse(fs.readFileSync(new URL('../src/data/development-references.json',import.meta.url)));
- const copy=()=>structuredClone(published);
- test('研发资料来源、品类关系和五维记录可回溯,统计保留候选分母',()=>{
- assert.equal(validateDevelopmentReferences(published),published);
- const counts=Object.fromEntries(['KfCategory','KfCategoryRelation','KfMaterial','KfProcess','KfStandard','KfSupplier'].map(c=>[c,researchRecords(published,c).length]));
- assert.deepEqual(counts,{KfCategory:16,KfCategoryRelation:15,KfMaterial:14,KfProcess:3,KfStandard:9,KfSupplier:1});
- assert.equal(published.sources.length,14);
- assert.equal(published.scope.products+published.scope.pending+published.scope.excluded,4674);
- assert.equal(published.scope.coverage.productsWithReviews,155);
- assert.equal(published.scope.coverage.verifiedUnitPriceObservations,0);
- assert.ok(published.records.every(r=>r.reviewState==='pending'));
- });
- test('来源丢失、重复ID或关系断开拒绝展示;错误计数不能变成零',()=>{
- for(const corrupt of [
- d=>d.sources.pop(),d=>d.records.push(d.records[0]),
- d=>d.records.find(r=>r.className==='KfCategoryRelation').toCategoryRef.objectId='missing',
- d=>d.scope.coverage.products=100,d=>d.scope.coverage.productsWithReviews=10000,
- d=>delete d.scope.coverage.productsWithReviews,
- ]){const d=copy();corrupt(d);assert.throws(()=>validateDevelopmentReferences(d));}
- });
- test('方向假设保留原评与完整定位,不把待核配方称为已验证结果',()=>{
- for(const d of DEVELOPMENT_DIRECTIONS){
- const records=directionReferences(published,d);
- assert.equal(records.length,d.keys.length,d.id);
- const text=developmentBrief(published,d);
- assert.ok(text.includes('待验证设想')&&text.includes(published.libraryGeneration));
- assert.ok(text.includes('快照')&&text.includes('不代表需求比例'));
- for(const r of records)assert.ok(text.includes(r.className+'/'+r.objectId)&&text.includes('KfSource/'+r.sourceId));
- for(const rid of d.reviews){const r=published.reviewExamples.find(r=>r.reviewId===rid);assert.ok(r);assert.ok(text.includes(r.text)&&text.includes(r.reviewId)&&text.includes(r.sourceRecordId));}
- }
- assert.equal(DEVELOPMENT_DIRECTIONS.find(d=>d.id==='nutrition').reviews.length,0);
- });
- test('驳回资料不再作为可用研发参考,原料角色和标准版次分开',()=>{
- const d=copy(),row=d.records.find(r=>r.key==='material-ye');row.reviewState='rejected';
- assert.ok(!directionReferences(d,DEVELOPMENT_DIRECTIONS[0]).some(r=>r.objectId===row.objectId));
- assert.ok(!researchRecords(d,'KfMaterial').some(r=>r.objectId===row.objectId));
- const groups=researchRecords(published,'KfMaterial').reduce((o,r)=>(o[materialGroup(r)]=(o[materialGroup(r)]||0)+1,o),{});
- assert.deepEqual(groups,{'营养强化':2,'面体原料':3,'添加剂候选':6,'风味配料':3});
- const future=published.records.find(r=>r.key==='standard-GB7718-2025');
- assert.equal(standardDate(future),'2027-03-16');assert.match(standardUse(future),/过渡/);
- });
- test('研发读取请求不带凭据,网络和格式错误直接报告',async()=>{
- const originalDocument=globalThis.document;
- globalThis.document={querySelector:()=>({getAttribute:()=>'/'}),baseURI:'http://localhost/'};
- const originalLocation=globalThis.location;
- globalThis.location={origin:'http://localhost',pathname:'/'};
- try{
- let request;
- await readDevelopmentReferences({fetchImpl:async(url,options)=>{request={url,options};return {ok:true,json:async()=>published};}});
- assert.equal(request.url,'http://localhost/src/data/development-references.json');assert.equal(request.options.cache,'no-store');
- assert.equal(request.options.headers,undefined);
- await assert.rejects(()=>readDevelopmentReferences({fetchImpl:async()=>({ok:false})}),/尚未发布/);
- await assert.rejects(()=>readDevelopmentReferences({fetchImpl:async()=>({ok:true,json:async()=>({})})}),/不完整/);
- }finally{globalThis.document=originalDocument;globalThis.location=originalLocation;}
- });
|