library.test.js 6.4 KB

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