// 段位多语言集成回归:请求使用本地桩,缓存通过真实 Pinia 持久化插件恢复。 const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const { createPinia, defineStore } = require('pinia'); const { createSSRApp } = require('vue'); const persist = require('pinia-plugin-persistedstate').default; const root = path.resolve(__dirname, '..'); const read = file => fs.readFileSync(path.join(root, file), 'utf8'); const requests = []; let hasToken = true; let nextFailure; const rank = {rankLvl: 1, rankName: 'Bronze I', rankIcon: 'https://example.test/青铜1.png'}; const apiUni = { getAccountInfoSync: () => ({miniProgram: {envVersion: 'trial'}}), getStorageSync: key => hasToken && key === 'trial_token' ? 'existing-token' : '', request(options) { requests.push(options); if (options.url.endsWith('/user/device/aimRenew')) return {}; if (nextFailure) { const error = nextFailure; nextFailure = undefined; options.success({statusCode: 200, data: error}); return; } const data = options.url.includes('/myHome') ? {...rank, user: {id: 42, scores: 150, rankLvl: 1, nickName: '原昵称'}} : {list: [{...rank}], randInfos: [{rank_id: 1, upgrade_scores: 100, name: 'Bronze I', icoin: rank.rankIcon}]}; options.success({statusCode: 200, data: {code: 0, data}}); }, }; const apiCode = read('src/apis.js').replace(/^import .*;\r?\n/gm, '').replace(/^export /gm, ''); const api = new Function('uni', 't', 'normalizeBattleApiResult', apiCode + ` return {request, getAppConfig, getHomeData, getScoreRankList, getTenRingRankList, getMvpRankList, getMyScoreRank, getMyTenRingRank, getMyMvpRank, getSeasonStats, getProvinceData, getDailyCountAPI, aimRenewAPI}; `)(apiUni, key => key, data => data); const storeCode = read('src/store.js').replace(/^import .*;\r?\n/gm, '').replace('export default defineStore', 'return defineStore'); function restore(cached) { const storage = new Map([ ['store', JSON.stringify(cached)], ['trial_token', 'existing-token'], ['point-book', JSON.stringify({draft: true})], ]); const uni = {getStorageSync: key => storage.get(key), setStorageSync: (key, value) => storage.set(key, value)}; const useStore = new Function('defineStore', 'uni', storeCode)(defineStore, uni); const pinia = createPinia(); pinia.use(persist); createSSRApp({}).use(pinia); return {store: useStore(pinia), storage}; } async function main() { const config = await api.getAppConfig(); const home = await api.getHomeData(42); await api.getScoreRankList(42, 2, 20); await api.getTenRingRankList(42, 2, 20); await api.getMvpRankList(42, 2, 20); await api.getMyScoreRank(42); await api.getMyTenRingRank(42); await api.getMyMvpRank(42); await api.getSeasonStats(0); await api.request('GET', '/index/myHome?seasonId=42'); for (const request of requests) { assert.equal(request.header['Accept-Language'], 'en-US'); assert.equal(request.header.Authorization, 'Bearer existing-token'); assert.equal(request.method, 'GET'); assert(request.url.startsWith('https://apitest.shelingxingqiu.com/api/shoot/')); } assert(requests[1].url.endsWith('/user/myHome?seasonId=42'), 'Existing home route must remain unchanged'); assert.deepEqual(requests[2].data, {seasonId: 42, page: 2, perPage: 20}); assert.deepEqual(requests[8].data, {seasonId: 0}); assert.equal(home.user.rankName, rank.rankName); assert.equal(home.user.rankIcon, rank.rankIcon); assert.equal(home.user.nickName, '原昵称'); await api.getProvinceData(); await api.getDailyCountAPI(); await api.request('GET', '/index/appConfig/other'); await api.request('POST', '/index/appConfig'); await api.request('PUT', '/test/update', {value: 0}); await api.request('DELETE', '/test/delete', {id: 42}); for (const request of requests.slice(10)) { assert.equal(request.header['Accept-Language'], 'en-US', 'All HTTP requests must declare English'); assert.equal(request.header.Authorization, 'Bearer existing-token'); } assert.deepEqual(requests.at(-2).data, {value: 0}); assert.deepEqual(requests.at(-1).data, {id: 42}); // 续期请求即使未收到响应,也必须保持同步返回且不弹失败提示。 assert.equal(api.aimRenewAPI(), undefined); assert.equal(requests.at(-1).header['Accept-Language'], 'en-US'); assert.equal(requests.at(-1).header.Authorization, 'Bearer existing-token'); assert.equal(requests.at(-1).method, 'POST'); assert.deepEqual(requests.at(-1).data, {}); assert.equal(requests.at(-1).timeout, 10000); assert.equal(requests.at(-1).fail({errMsg: 'timeout'}), undefined); hasToken = false; await api.getAppConfig(); assert.deepEqual(requests.at(-1).header, {'Accept-Language': 'en-US'}); assert.equal(api.aimRenewAPI(), undefined); assert.deepEqual(requests.at(-1).header, {'Accept-Language': 'en-US'}); hasToken = true; nextFailure = {code: 400, message: '设备未连接', data: {reason: 'offline'}}; await assert.rejects(api.request('POST', '/test/error', {}, undefined, undefined, {showErrorToast: false}), error => error.code === 400 && error.message === '设备未连接' && error.data.reason === 'offline'); assert.equal(requests.at(-1).header['Accept-Language'], 'en-US'); const legacy = { user: {id: 42, nickName: '原昵称', avatar: 'avatar.png', trio: 1, scores: 150, rankLvl: 1, lvlName: '倔强青铜I', rankName: '倔强青铜I', nextRankName: '倔强青铜II', rankIcon: rank.rankIcon, nextRankIcon: 'next.png', lvlImage: 'level.png', vip: true}, device: {deviceId: 'unchanged-device', deviceName: '我的弓'}, }; const {store, storage} = restore(legacy); for (const [field, value] of Object.entries(legacy.user)) { assert.deepEqual(store.user[field], ['lvlName','rankName','nextRankName'].includes(field) ? '' : value); } assert.deepEqual({...store.device}, legacy.device); assert.equal(storage.get('trial_token'), 'existing-token'); assert.equal(storage.get('point-book'), JSON.stringify({draft: true})); assert.deepEqual([...storage.keys()].sort(), ['store', 'trial_token', 'point-book'].sort()); await store.updateConfig(config); await store.updateUser(home.user); assert.equal(store.user.rankName, 'Bronze I'); assert.equal(store.user.lvlName, 'Bronze I'); assert.equal(store.user.scores, 150); assert.equal(store.user.rankLvl, 1); store.$persist(); const next = restore(JSON.parse(storage.get('store'))).store; assert.equal(next.user.rankName, 'Bronze I', 'English cached labels must survive cold start'); assert.equal(next.user.lvlName, 'Bronze I'); assert.equal(next.user.nickName, '原昵称'); assert.deepEqual({...next.device}, legacy.device); assert.equal(restore({user: {id: 7, rankName: '', lvlName: null}, device: {}}).store.user.lvlName, null); console.log('Rank i18n checks passed: global HTTP English headers (GET/POST/PUT/DELETE + aim renewal), auth/query/pagination and original errors preserved; non-blocking renewal and real Pinia cache hydration verified.'); } main().catch(error => {console.error(error); process.exitCode = 1;});