const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const vm = require('node:vm'); const ts = require(process.env.TYPESCRIPT_PATH || 'typescript'); function fixture() { const info = {uid:'u1',miniProgramWelfare:{favorite:false,desktop:false},freezeAmount:5,hammerAmount:6,magicAmount:7}; const state = {favorite_entry:{eligibleAt:1},desktop_entry:{eligibleAt:1}}; const storage = {prop:{hourglassAmount:4}}, posts=[]; let handler=(url,data,cb)=>cb(url==='userProp'?{code:1,data:{freeze:6,hammer:7,magic_wand:8}}:{code:1}); const cc={fx:{GameConfig:{GM_INFO:info},StorageMessage:{getStorage:k=>storage[k],setStorage:(k,v)=>storage[k]=v}}}; const Utils={POST:(url,data,cb)=>{posts.push({url,data});handler(url,data,cb);}}; const module={exports:{}}; vm.runInNewContext(ts.transpileModule(fs.readFileSync('assets/mini_program_benefits/script/MiniProgramBenefitsReward.ts','utf8'),{compilerOptions:{target:ts.ScriptTarget.ES2017,module:ts.ModuleKind.CommonJS}}).outputText,{module,exports:module.exports,require:()=>({default:Utils}),cc,setTimeout,clearTimeout}); const Reward=module.exports.default; return {info,state,storage,posts,api:new Reward('u1','u1',()=>state),Reward,setHandler:h=>handler=h}; } test('deduplicates claims, maps task names, uploads fixed client rewards once',async()=>{ const f=fixture(); await Promise.all([f.api.request('claim',{task:'favorite_entry'},1),new f.Reward('u1','u1',()=>f.state).request('claim',{task:'favorite_entry'},1)]); assert.equal(f.posts.length,2); assert.equal(f.posts[0].url,'miniProgramWelfare');assert.equal(f.posts[0].data.type,'favorite');assert.equal(f.posts[1].data.action,'save');assert.deepEqual(JSON.parse(f.posts[1].data.propData),{freeze:6,hammer:7,magic_wand:8}); assert.equal(f.info.freezeAmount,6);assert.equal(f.storage.prop.hourglassAmount,4);assert.equal(f.info.miniProgramWelfare.favorite,true); await f.api.request('ack',{task:'favorite_entry',receiptId:f.state.favorite_entry.receiptId});assert.equal(f.state.favorite_entry.acknowledged,true); }); test('server login claims remain claimed without local storage',async()=>{ const f=fixture();f.state.favorite_entry={claimedAt:1,acknowledged:true};await f.api.request('claim',{task:'favorite_entry'},1);assert.equal(f.posts.length,0); }); test('failed inventory upload retries without requesting or adding another award',async()=>{ const f=fixture();let reads=0;f.setHandler((url,data,cb)=>cb(url==='userProp'?(++reads===1?{code:0,msg:'offline'}:{code:1,data:{freeze:6,hammer:7,magic_wand:8}}):{code:1})); await assert.rejects(f.api.request('claim',{task:'desktop_entry'},1),/offline/);assert.ok(f.state.desktop_entry.claimedAt);assert.equal(f.info.freezeAmount,6); await new f.Reward('u1','u1',()=>f.state).request('read');assert.equal(f.posts.filter(p=>p.url==='miniProgramWelfare').length,1);assert.equal(f.info.freezeAmount,6); }); test('failed claim does not mutate inventory or mark claimed',async()=>{ const f=fixture();f.setHandler((u,d,cb)=>cb({code:0,msg:'已领取'}));await assert.rejects(f.api.request('claim',{task:'favorite_entry'},1),/已领取/);assert.equal(f.info.freezeAmount,5);assert.ok(!f.state.favorite_entry.claimedAt); }); test('account switch ignores delayed response',async()=>{ const f=fixture();f.setHandler((u,d,cb)=>{f.info.uid='u2';cb({code:1})});await assert.rejects(f.api.request('claim',{task:'favorite_entry'},1),/账号/);assert.ok(!f.state.favorite_entry.claimedAt);assert.equal(f.info.freezeAmount,5); }); test('missing contract and invalid eligibility cannot claim',async()=>{ const f=fixture();await assert.rejects(f.api.request('claim',{task:'favorite_entry'},0));delete f.info.miniProgramWelfare;await assert.rejects(f.api.request('claim',{task:'favorite_entry'},1),/重新登录/);await assert.rejects(f.api.request('reset'));assert.equal(f.posts.length,0); });