65 lines
3.5 KiB
TypeScript
65 lines
3.5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import {test} from 'node:test';
|
|
import type {NextApiRequest,NextApiResponse} from 'next';
|
|
import {apiDocuments,serveApiDocumentation} from '../src/lib/api-documentation';
|
|
import index from '../src/pages/api/index';
|
|
|
|
function response(){
|
|
const state={status:0,headers:{} as Record<string,string>,body:undefined as unknown};
|
|
const res={setHeader(name:string,value:string){state.headers[name]=value;return res;},status(code:number){state.status=code;return res;},send(body:unknown){state.body=body;return res;},json(body:unknown){state.body=body;return res;},end(){return res;}};
|
|
return {state,res:res as unknown as NextApiResponse};
|
|
}
|
|
const request=(method:string,agent_mode:unknown)=>({method,query:{agent_mode}} as unknown as NextApiRequest);
|
|
|
|
test('every business handler serves documentation before validating parameters or accessing dependencies',async()=>{
|
|
const routes:Record<string,string>={
|
|
'/api/canvases/{...path}':'canvases/[[...path]]','/api/explore-feed':'explore-feed','/api/feed':'feed','/api/notes/{id}':'notes/[id]',
|
|
'/api/posts/{id}/archive':'posts/[id]/archive','/api/posts':'posts/index','/api/tags':'tags','/api/upload':'upload',
|
|
'/api/media/{id}':'media/[id]','/api/preview':'preview',
|
|
'/api/skills/street-people':'skills/street-people','/api/health':'health','/api/ready':'ready',
|
|
};
|
|
for(const [path,route] of Object.entries(routes)){
|
|
const {default:handler}=await import('../src/pages/api/'+route+'.ts');
|
|
for(const method of ['GET','HEAD']){
|
|
const {state,res}=response();
|
|
await handler(request(method,'tRuE'),res);
|
|
assert.equal(state.status,200,path);
|
|
assert.equal(state.headers['Content-Type'],'text/plain; charset=utf-8',path);
|
|
if(method==='HEAD')assert.equal(state.body,undefined);else {
|
|
assert.ok(String(state.body).includes(path),path);
|
|
assert.ok(String(state.body).includes('参数'),path);
|
|
}
|
|
}
|
|
}
|
|
assert.deepEqual(Object.keys(routes).sort(),Object.keys(apiDocuments).sort());
|
|
});
|
|
|
|
test('normal calls and mutations do not enter documentation mode',()=>{
|
|
for(const [method,value] of [['GET',undefined],['GET','false'],['GET',['True','false']],['POST','True'],['PUT','True'],['DELETE','True']]){
|
|
const {state,res}=response();
|
|
assert.equal(serveApiDocumentation(request(method as string,value),res,'/api/posts'),false);
|
|
assert.equal(state.status,0);
|
|
}
|
|
});
|
|
|
|
test('index exposes every business endpoint and its text URL',()=>{
|
|
const {state,res}=response();index(request('GET','True'),res);
|
|
for(const path of [...Object.keys(apiDocuments),'/studio/api/export/{slug}','/studio/api/health','/api/studio/designs','/api/studio/designs/{id}','/api/studio/designs/{id}/post'])assert.ok(String(state.body).includes(path+'?agent_mode=True'));
|
|
});
|
|
|
|
test('health keeps its regular JSON response',async()=>{
|
|
const {default:handler}=await import('../src/pages/api/health');
|
|
const {state,res}=response();handler(request('GET',undefined),res);
|
|
assert.equal(state.status,200);assert.equal((state.body as {status:string}).status,'ok');
|
|
});
|
|
|
|
|
|
|
|
test('Studio docs describe native editing and atomic Wiki publication without accessing storage',async()=>{
|
|
for(const route of ['studio/designs/index','studio/designs/[id]','studio/designs/[id]/post']){
|
|
const {default:handler}=await import('../src/pages/api/'+route+'.ts');
|
|
const {state,res}=response();await handler(request('GET','True'),res);
|
|
assert.equal(state.status,200);assert.match(String(state.body),/postUrl/);assert.match(String(state.body),/整体替换/);assert.match(String(state.body),/同一数据库事务/);assert.match(String(state.body),/原生页面/);
|
|
}
|
|
});
|