更新最新代码

This commit is contained in:
CodeBuddy
2026-10-26 17:32:47 +08:00
commit 0f5dc01674
315 changed files with 42640 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
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://past.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:17}}});await Promise.resolve();await Promise.resolve();assert.equal(e.calls[0].kind,'undo');assert.equal(e.calls[0].original_id,17);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,'—');
});