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.
61 lines
4.4 KiB
JavaScript
61 lines
4.4 KiB
JavaScript
const test=require('node:test');
|
|
const assert=require('node:assert/strict');
|
|
const path=require('node:path');
|
|
function loadPage(file,api,wxExtra={}) {
|
|
let definition;
|
|
const storage=new Map([['userId',1],['coupleId',10]]),intervals=[];
|
|
const wx={getStorageSync:k=>storage.get(k),setStorageSync:(k,v)=>storage.set(k,v),removeStorageSync:k=>storage.delete(k),showToast(){},...wxExtra};
|
|
const loader=require('./load-module.cjs')({Page:p=>definition=p,wx,console,Date,Promise,setTimeout,clearTimeout,setInterval:fn=>{intervals.push(fn);return intervals.length;},clearInterval(){}},{'../../utils/request':{api},'../../utils/layout':{getLayout:()=>({})}});
|
|
loader(path.join(__dirname,'../pages',file));
|
|
const page={...definition,data:JSON.parse(JSON.stringify(definition.data)),setData(v){Object.assign(this.data,v);}};
|
|
return {page,storage,intervals,wx};
|
|
}
|
|
const tick=async()=>{for(let i=0;i<20;i++)await Promise.resolve();};
|
|
const journal=overrides=>({cycles:[],daily:[],sex_records:[],can_access:true,has_couple:true,partner_user_id:2,period_visible:0,...overrides});
|
|
test('legacy shared journal displays returned cycles and clears them when access closes',async()=>{
|
|
let denied=false;
|
|
const {page}=loadPage('period/period.js',{periodList:async()=>journal({can_access:!denied,cycles:denied?[]:[{id:7,start_date:'2026-10-01',end_date:'2026-10-05'}]})});
|
|
page.calculateStats=()=>{};page.buildCalendar=()=>{};
|
|
await page.loadData();assert.equal(page.data.accessDenied,false);assert.equal(page.data.savedCycles.length,1);
|
|
denied=true;await page.loadData();assert.equal(page.data.accessDenied,true);assert.equal(page.data.savedCycles.length,0);
|
|
});
|
|
test('cooling keeps the relationship and server completion clears it',async()=>{
|
|
let status='cooling';const {page,storage}=loadPage('unbind/unbind.js',{unbindStatus:async()=>({status,remaining_seconds:259200,is_requester:false})});page._visible=true;
|
|
await page.loadStatus();await tick();assert.equal(page.data.status,'cooling');assert.equal(storage.get('coupleId'),10);assert.equal(page.data.timerText,'72时 0分 0秒');
|
|
status='completed';await page.loadStatus();await tick();assert.equal(storage.has('coupleId'),false);assert.equal(page.data.status,'completed');
|
|
});
|
|
test('server relation changes discard cached data before another read',async()=>{
|
|
const storage=new Map([['userId',1],['coupleId',10],['token','private']]);let redirects=0,reads=0;
|
|
const wx={getStorageSync:k=>storage.get(k),setStorageSync:(k,v)=>storage.set(k,v),hideLoading(){},request(o){
|
|
if(o.url.includes('/diaryList')){reads++;o.success({statusCode:200,data:{items:[{title:reads===1?'old space':'new space'}]}});}
|
|
else o.success({statusCode:200,header:{'X-Couple-ID':'22','X-User-ID':'1'},data:{loggedIn:true}});
|
|
}};
|
|
const loader=require('./load-module.cjs')({wx,console,Date,Promise,getApp:()=>({onRelationChanged(){redirects++;}})});
|
|
const {api,getImageUrl}=loader(path.join(__dirname,'../utils/request.js'));
|
|
await api.diaryList();await api.diaryList();assert.equal(reads,1);
|
|
await api.checkAuth();assert.equal(storage.get('coupleId'),'22');assert.equal(redirects,1);
|
|
const fresh=await api.diaryList();assert.equal(reads,2);assert.equal(fresh.items[0].title,'new space');
|
|
assert.equal(getImageUrl('/api/files/6083e119-6b26-441b-8b70-44d224366238'),'https://we.sundynix.cn/api/files/6083e119-6b26-441b-8b70-44d224366238');
|
|
});
|
|
|
|
test('settings and profile consent retain couple cache and lead into cooling',async()=>{
|
|
for (const file of ['settings/settings.js','profile/profile.js']) {
|
|
let agreed=0;
|
|
const e=loadPage(file,{unbindRespond:async()=>{agreed++;}}, {
|
|
showActionSheet:o=>o.success({tapIndex:0}),showModal:o=>o.success({confirm:true}),showLoading(){},hideLoading(){},navigateTo(){}
|
|
});
|
|
e.page.loadUnbindStatus=()=>{};
|
|
e.page.doRespondUnbind();await tick();
|
|
assert.equal(agreed,1);assert.equal(e.storage.get('coupleId'),10);
|
|
}
|
|
});
|
|
test('either partner can use cooling withdrawal and state refresh preserves binding',async()=>{
|
|
for (const isRequester of [true,false]) {
|
|
let cancelled=0;
|
|
const e=loadPage('unbind/unbind.js',{unbindCancel:async()=>{cancelled++;},unbindStatus:async()=>({status:'cancelled',is_requester:isRequester})},{showModal:o=>o.success({confirm:true}),showLoading(){},hideLoading(){}});
|
|
e.page._visible=true;e.page.data.status='cooling';e.page.data.isRequester=isRequester;
|
|
e.page.cancelUnbind();await tick();
|
|
assert.equal(cancelled,1);assert.equal(e.page.data.status,'cancelled');assert.equal(e.storage.get('coupleId'),10);
|
|
}
|
|
});
|