development-references.test.js 4.4 KB

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