Files
codex-bot 7f7fc28e57
creative workspace review checks / verify (pull_request) Has been cancelled
Add scoped canvas agent API with versioned S3 state and task results
2026-10-30 17:38:05 +08:00

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),/原生页面/);
}
});