library.test.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. import test from 'node:test';
  2. import assert from 'node:assert/strict';
  3. import { libraryRequest,libraryPath,libraryLabel,libraryRecordInfo,libraryProductRequest,libraryCatalogQuery,LIBRARY_GROUPS } from '../src/core/library.js';
  4. import { readLibrary } from '../src/services/library-service.js';
  5. import { createLibraryApi } from '../server/library-api.mjs';
  6. import { Readable } from 'node:stream';
  7. test('资料条件同时保留,未知字段和日期不被关键词替代',()=>{
  8. const r=libraryRequest({type:'reviews',platform:'jd',brand:'ksf',category:'banmian',q:'份量',from:'2026-10-01',to:'2026-11-05',shopId:'__unknown'});
  9. assert.deepEqual(r.filters,{platform:'jd',brand:'ksf',category:'banmian',shopId:{exists:false},reviewDate:{min:'2026-10-01',max:'2026-11-05'}});
  10. assert.equal(r.keyword,'份量');assert.equal(libraryRequest({type:'social',from:'2026-10-01'}).filters.publishedDate.min,'2026-10-01');
  11. });
  12. test('详情深链接保留查询,标签使用真实字典',()=>{
  13. assert.equal(libraryPath({type:'products',brand:'ksf'},{entity:'products',id:'jd:123'}),'/library/products/jd%3A123?type=products&brand=ksf');
  14. assert.equal(libraryLabel(null),'未知/待核对');assert.equal(libraryLabel('ksf',{taxonomy:{brands:[{code:'ksf',name:'康师傅'}],categories:[]}}),'康师傅');
  15. });
  16. test('阅读信息按资料类型选择业务事实,内部规则、编号和指纹不进入展示',()=>{
  17. const record={marketplace:'jd',shop_name:'康师傅旗舰店',category_basis:'rule_title_explicit',brand_basis:'rule_title_prefix',
  18. classification_state:'supported',source_record_id:'internal-source',item_id:'123',shop_id:'456',
  19. expected_sha256:'a'.repeat(64),issues:['INTERNAL_RULE'],source_time_raw:'2026-11-04'};
  20. assert.deepEqual(libraryRecordInfo(record,'products'),[['销售平台','京东'],['店铺','康师傅旗舰店'],['观察时间','2026-11-04']]);
  21. assert.deepEqual(libraryRecordInfo({...record,likes:0,comment_count:30},'social'),[['平台点赞数','0'],['平台显示评论量','30']]);
  22. });
  23. test('阅读价格使用元且不暴露分单位和技术字段,单位价格只在核对后展示',()=>{
  24. const record={amount_minor:2650,raw_amount:'26.5',price_basis:'search_listing_snapshot',price_per_100g:3.5,unit_price_verification:'unavailable'};
  25. assert.deepEqual(libraryRecordInfo(record,'prices'),[['观察价格','¥26.50'],['价格口径','搜索展示价']]);
  26. assert.ok(libraryRecordInfo({...record,unit_price_verification:'verified'},'prices').some(([k,v])=>k==='每100克价格(元)'&&v==='3.5'));
  27. });
  28. test('商品是浏览入口,详情内查询固定商品编号并隔离列表条件和分页',()=>{
  29. assert.deepEqual(LIBRARY_GROUPS.flatMap(group=>group.entities),['products','social','documents','reports']);
  30. const query={type:'products',platform:'tmall',brand:'other-brand',category:'other-category',q:'旧列表词',cursor:'list-cursor',panel:'reviews',relatedQ:'包装',relatedQuality:'audit',relatedFrom:'2026-11-01',relatedTo:'2026-11-05',relatedCursor:'review-cursor'};
  31. assert.deepEqual(libraryProductRequest('jd:123',query),{op:'browse',entity:'reviews',filters:{productId:'jd:123',reviewDate:{min:'2026-11-01',max:'2026-11-05'}},quality:'audit',limit:20,keyword:'包装',cursor:'review-cursor'});
  32. assert.deepEqual(libraryCatalogQuery(query),{type:'products',platform:'tmall',brand:'other-brand',category:'other-category',q:'旧列表词',cursor:'list-cursor'});
  33. assert.equal(libraryProductRequest('jd:123',{panel:'overview'}),null);
  34. assert.equal(libraryProductRequest('jd:123',{panel:'unknown'}),null);
  35. });
  36. test('接口失败不作为空数据,浏览器不发送服务凭据',async()=>{
  37. await assert.rejects(()=>readLibrary({op:'summary'},{fetchImpl:async(_url,opt)=>{assert.deepEqual(opt.headers,{'content-type':'application/json'});return {ok:false,json:async()=>({ok:false,error:{code:'DATABASE_NOT_INSTALLED'}})};}}),/尚未连接/);
  38. await assert.rejects(()=>readLibrary({op:'summary'},{fetchImpl:async()=>({ok:true,json:async()=>{throw Error('HTML')}})}),/尚未接通/);
  39. });
  40. test('生产接口默认拒绝匿名及写操作,凭据不进入返回',async()=>{
  41. const api=createLibraryApi({token:'test-token'});let status,output;
  42. const req=Readable.from(['{"op":"summary"}']);req.method='POST';req.headers={};
  43. await api(req,{writeHead(s){status=s;},end(v){output=JSON.parse(v);}});
  44. assert.equal(status,401);assert.equal(output.error.code,'AUTH_REQUIRED');assert.ok(!JSON.stringify(output).includes('test-token'));
  45. });