Replace virtual payments with WeChat Pay API v3 and verify callbacks and order polling. Cache WeChat access tokens in Redis and write structured Zap logs. Support configurable capacity plans across the API, admin and mini program, with purchase snapshots and quota enforcement. Allow personal albums before binding and share them with the current partner. Update deployment configuration and domain routing.
59 lines
6.0 KiB
JavaScript
59 lines
6.0 KiB
JavaScript
const test=require('node:test');
|
|
const assert=require('node:assert/strict');
|
|
const path=require('node:path');
|
|
const createLoader=require('./load-module.cjs');
|
|
function setup(action,info) {
|
|
let definition;const calls=[],toasts=[],events=[];
|
|
const api={coupleInfo:async()=>({partner1:{id:1,nickname:'本人',avatar:'uploads/me.jpg'},partner2:{id:2,nickname:'伴侣',avatar:'uploads/partner.jpg'}}),smokingAction:async payload=>{calls.push(payload);return action(payload);},smokingInfo:info||(async()=>({stock:5,today_count:0,logs:[],daily:[],available:true,read_only:false}))};
|
|
const load=createLoader({Component:c=>definition=c,Date,Math,Promise,wx:{getStorageSync:()=>1,showToast:v=>toasts.push(v),showModal:o=>o.success({confirm:true}),navigateTo(){}}},{'../../utils/request':{api,getImageUrl:value=>value?'https://we.sundynix.cn/backend/'+value:''}});
|
|
load(path.join(__dirname,'../components/smoking-card/smoking-card.js'));
|
|
const component={...definition.methods,properties:{detailed:false},data:JSON.parse(JSON.stringify(definition.data)),setData(values){Object.assign(this.data,values);},triggerEvent:name=>events.push(name)};
|
|
component.applyInfo({stock:5,today_count:0,logs:[],daily:[],available:true,read_only:false});
|
|
return {component,calls,toasts,events};
|
|
}
|
|
const result=()=>({success:true,info:{stock:4,today_count:1,logs:[],daily:[],available:true,read_only:false}});
|
|
test('double tap records once and uncertain network retries reuse the idempotency key',async()=>{
|
|
let resolve;const e=setup(()=>new Promise(done=>resolve=done));const first=e.component.smoke();await e.component.smoke();assert.equal(e.calls.length,1);resolve(result());await first;assert.equal(e.component.data.info.stock,4);assert.equal(e.events.length,1);
|
|
let fail=true;const retry=setup(async()=>{if(fail)throw Error('offline');return result();});await retry.component.smoke();fail=false;await retry.component.smoke();assert.equal(retry.calls[0].request_key,retry.calls[1].request_key);
|
|
});
|
|
test('partner view is read only and received-then-smoked is a single atomic operation',async()=>{
|
|
const e=setup(async()=>result());e.component.data.info.read_only=true;await e.component.smoke();assert.equal(e.calls.length,0);
|
|
e.component.data.info.read_only=false;await e.component.receiveSmoke();assert.equal(e.calls[0].kind,'receive_smoke');assert.equal(e.calls[0].quantity,1);
|
|
});
|
|
test('purchase converts packs to pieces and cents; invalid quantities never submit',async()=>{
|
|
const e=setup(async()=>result());e.component.replenish();e.component.setData({quantity:'2',packSize:'20',amount:'30.50'});await e.component.submit();assert.equal(e.calls[0].quantity,40);assert.equal(e.calls[0].amount_cents,3050);
|
|
e.component.setData({quantity:'1.5'});await e.component.submit();assert.equal(e.calls.length,1);
|
|
});
|
|
test('undo includes the original record and fills a seven-day chart with zero days',async()=>{
|
|
const e=setup(async()=>result());e.component.undo({currentTarget:{dataset:{id:'6083e119-6b26-441b-8b70-44d224366238'}}});await Promise.resolve();await Promise.resolve();assert.equal(e.calls[0].kind,'undo');assert.equal(e.calls[0].original_id,'6083e119-6b26-441b-8b70-44d224366238');assert.equal(e.component.data.daily.length,7);
|
|
});
|
|
|
|
test('string zero is active while one is undone, and ledger identity follows the selected account',async()=>{
|
|
const e=setup(async()=>result(),async uid=>({user_id:uid||1,owner_name:uid?'伴侣':'本人',stock:0,available:true,read_only:!!uid,logs:[{id:1,kind:'receive_smoke',undone:'0',delta:'0'},{id:2,kind:'consume',undone:'1',delta:'-1'},{id:3,kind:'consume',undone:0,delta:-1}],daily:[]}));
|
|
await e.component.load();
|
|
assert.equal(e.component.data.logs[0].undone,false);assert.equal(e.component.data.logs[1].undone,true);assert.equal(e.component.data.logs[2].undone,false);
|
|
assert.equal(e.component.data.ownerName,'本人');assert.ok(e.component.data.ownerAvatar.endsWith('/uploads/me.jpg'));
|
|
await e.component.switchOwner({currentTarget:{dataset:{user:2}}});
|
|
assert.equal(e.component.data.ownerName,'伴侣');assert.ok(e.component.data.ownerAvatar.endsWith('/uploads/partner.jpg'));
|
|
e.component.avatarFailed();assert.equal(e.component.data.ownerAvatar,'');assert.equal(e.component.data.ownerInitial,'伴');
|
|
});
|
|
|
|
test('giving a cigarette submits one stock operation, labels its ledger and rejects empty stock or partner view',async()=>{
|
|
const e=setup(async()=>({success:true,info:{user_id:1,stock:4,today_count:0,total_count:0,logs:[{id:9,kind:'give',delta:'-1',balance:'4',undone:'0'}],daily:[],available:true,read_only:false}}));
|
|
await e.component.giveSmoke();assert.equal(e.calls[0].kind,'give');assert.equal(e.calls[0].quantity,1);assert.equal(e.component.data.info.today_count,0);
|
|
assert.equal(e.component.data.logs[0].title,'递给别人一支');assert.equal(e.component.data.logs[0].change,'-1');assert.equal(e.component.data.logs[0].undone,false);
|
|
e.component.data.info.stock=0;await e.component.giveSmoke();assert.equal(e.calls.length,1);assert.equal(e.toasts.at(-1).title,'库存不足,请先补充库存');
|
|
e.component.data.info.stock=5;e.component.data.info.read_only=true;await e.component.giveSmoke();assert.equal(e.calls.length,1);
|
|
});
|
|
|
|
test('weekly and monthly purchase totals use cents and refresh with selected owner',()=>{
|
|
const {component}=setup(async()=>result());
|
|
component.applyInfo({user_id:1,week_count:7,month_count:29,week_purchase_cents:3050,month_purchase_cents:12500,logs:[],daily:[]});
|
|
assert.equal(component.data.periodStats[0].count,7);assert.equal(component.data.periodStats[0].money,'30.50');
|
|
assert.equal(component.data.periodStats[1].count,29);assert.equal(component.data.periodStats[1].money,'125.00');
|
|
component.applyInfo({user_id:2,read_only:true,week_count:0,month_count:2,week_purchase_cents:0,month_purchase_cents:999,logs:[],daily:[]});
|
|
assert.equal(component.data.periodStats[0].money,'0.00');assert.equal(component.data.periodStats[1].money,'9.99');
|
|
assert.equal(component.data.periodStats[1].count,2);
|
|
component.applyInfo({logs:[],daily:[]});assert.equal(component.data.periodStats[0].count,'—');
|
|
});
|