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'),require('../config/env').API_BASE+'/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); } });