feat: add multi-source breed encyclopedia and two-hour refresh sessions
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
const test=require('node:test'), assert=require('node:assert/strict'), fs=require('node:fs'),vm=require('node:vm');
|
||||
const {createRequire}=require('node:module');
|
||||
const adminRequire=createRequire(require('node:path').resolve('pets-be/web/admin/package.json'));
|
||||
const ts=adminRequire('typescript'),axios=adminRequire('axios').default;
|
||||
const tick=()=>new Promise(resolve=>setImmediate(resolve));
|
||||
function runtime(adapter) {
|
||||
const storage=new Map(),events=[];
|
||||
const localStorage={getItem:k=>storage.get(k)||null,setItem:(k,v)=>storage.set(k,v),removeItem:k=>storage.delete(k)};
|
||||
axios.defaults.adapter=adapter;
|
||||
const module={exports:{}};
|
||||
const code=ts.transpileModule(fs.readFileSync('pets-be/web/admin/src/lib/api.ts','utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS,esModuleInterop:true,target:ts.ScriptTarget.ES2022}}).outputText;
|
||||
vm.runInNewContext(code,{module,exports:module.exports,require:adminRequire,localStorage,navigator:{},window:{dispatchEvent:e=>events.push(e.type)},Event:class{constructor(type){this.type=type;}}});
|
||||
return {...module.exports,storage,events};
|
||||
}
|
||||
const response=(config,data)=>({status:200,statusText:'OK',config,headers:{},data:{code:0,data}});
|
||||
const unauthorized=config=>Promise.reject(new axios.AxiosError('expired','ERR_BAD_REQUEST',config,null,{status:401,data:{message:'expired'},config}));
|
||||
const pair=token=>({token,refresh_token:'refresh-'+token,expires_at:Date.now()/1000+7200,refresh_expires_at:Date.now()/1000+7200});
|
||||
test('管理端并发请求只刷新一次,再携带新 token 请求',async()=>{
|
||||
let releases,refreshes=0,seen=[];
|
||||
const r=runtime(config=>{if(config.url==='/admin/refresh'){refreshes++;return new Promise(resolve=>releases=()=>resolve(response(config,pair('new'))));}seen.push(config.headers.Authorization);return Promise.resolve(response(config,{users:1}));});
|
||||
r.setToken({...pair('old'),expires_at:Date.now()/1000+30});const calls=[r.api.stats(),r.api.stats(),r.api.stats()];await tick();assert.equal(refreshes,1);releases();await Promise.all(calls);assert.deepEqual(seen,['Bearer new','Bearer new','Bearer new']);assert.equal(r.getToken(),'new');
|
||||
});
|
||||
test('刷新网络失败保留登录,过期拒绝则清空会话',async()=>{
|
||||
let rejectAuth=false;
|
||||
const r=runtime(config=>config.url==='/admin/refresh' ? rejectAuth?unauthorized(config):Promise.reject(new Error('offline')):unauthorized(config));
|
||||
r.setToken({...pair('old'),expires_at:Date.now()/1000+30});await assert.rejects(r.api.stats());assert.equal(r.getToken(),'old');assert.equal(r.events.length,0);
|
||||
rejectAuth=true;await assert.rejects(r.api.stats());assert.equal(r.getToken(),'');assert.ok(r.events.includes('admin-auth-expired'));
|
||||
});
|
||||
test('退出期间的刷新响应不会恢复会话,登录密码错误也不会递归刷新',async()=>{
|
||||
let release,refreshes=0;
|
||||
const r=runtime(config=>{if(config.url==='/admin/login')return unauthorized(config);if(config.url==='/admin/refresh'){refreshes++;return new Promise(resolve=>release=()=>resolve(response(config,pair('new'))));}return Promise.resolve(response(config,{}));});
|
||||
r.setToken({...pair('old'),expires_at:Date.now()/1000+30});const request=r.api.stats();await tick();r.clearToken();release();await assert.rejects(request);assert.equal(r.getToken(),'');
|
||||
await assert.rejects(r.api.login('sundynix','bad'));assert.equal(refreshes,1);
|
||||
});
|
||||
Reference in New Issue
Block a user