import assert from 'node:assert/strict'; import {writeFile} from 'node:fs/promises'; const base='http://8.211.154.134:18131'; async function api(path,body,method=body?'POST':'GET',status=200){const r=await fetch(base+path,{method,...(body?{headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}:{})});assert.equal(r.status,status,path);return r.json()} const catalog=await api('/studio/api/layouts');assert.equal(catalog.layouts.length,28); const note=(await api('/api/notes/8eb8c76500580f7a4b56ba3c')).entry; const asset=note.coverAssetId||note.assets.find(a=>a.kind==='image').id; const preview=await fetch(base+'/api/preview?'+new URLSearchParams({source:'/api/media/'+asset,width:'960'}));assert.equal(preview.status,200);assert.equal(preview.headers.get('content-type'),'image/webp');const bytes=(await preview.arrayBuffer()).byteLength;assert.ok(bytes<=220000); const results=[]; for(const layoutId of ['standard-01','ref-17']){ const schema=await api('/studio/api/layouts/'+layoutId);assert.ok(schema.elements.some(s=>s.id==='T01')); const p={texts:{T01:{value:'API 验收 · 原生页面',fontFamily:'serif',fontWeight:600}},images:{I01:{assetId:asset,fit:'cover',cropX:35,cropY:65,zoom:1.2}},added:[{id:'user-api-check',kind:'text',role:'扩展正文',box:[10,120,60,9],direction:'horizontal',text:'通过 HTTP 保存并恢复',reading:'custom'}],layers:{'user-api-check':{z:20,rotation:2}}}; let d=(await api('/api/studio/designs',{schemaVersion:1,layoutId,name:'API 验收 '+layoutId,parameters:p},'POST',201)).design; const id=d.id,postId=d.postId; assert.deepEqual((await api('/api/studio/designs/'+id)).design.parameters,d.parameters); const html=await fetch(base+d.url).then(r=>r.text());assert.ok(html.includes('API 验收'));assert.ok(html.includes('user-api-check'));assert.ok(html.includes('/api/preview?'));assert.ok(!html.includes('src="/api/media/')); d=(await api('/api/studio/designs/'+id,{name:'API 验收修订 '+layoutId,parameters:p,revision:d.revision},'PATCH')).design; assert.equal(d.postId,postId);assert.equal(d.revision,2); await api('/api/studio/designs/'+id,{name:'过期写入',parameters:{},revision:1},'PATCH',409); d=(await api('/api/studio/designs/'+id,{revision:2},'DELETE')).design;assert.equal(d.archived,true); assert.equal((await fetch(base+d.url)).status,404);assert.equal((await api('/api/notes/'+postId)).entry.archived,true); assert.deepEqual((await api('/api/studio/designs/'+id)).design.parameters,d.parameters); assert.ok((await api('/api/studio/designs?archived=true&layoutId='+layoutId)).designs.some(v=>v.id===id)); d=(await api('/api/studio/designs/'+id,{revision:3,archived:false},'POST')).design;assert.equal(d.revision,4);assert.equal(d.archived,false);assert.equal((await fetch(base+d.url)).status,200); results.push({layoutId,id,url:d.url,revision:d.revision,postId,parameters:p}); // Restore tested URL for visual inspection, then archive only these clearly named test records later. } for(const l of catalog.layouts){const r=await fetch(base+l.url);assert.equal(r.status,200,l.id);const html=await r.text();assert.ok(html.includes('data-layout-id="'+l.id+'"'));assert.ok(!/r)}));