|
- // Read-only API contract checks; these are synthetic observations, not learner
- // operations. No progress, score or teaching run is created.
- import fs from 'node:fs';
- import assert from 'node:assert/strict';
- import {login,call} from './role-permissions-live.mjs';
- const session=await login('root'),r=await call('tran/v1/teaching/assignments/86',session.accessToken);assert.equal(r.status,200);
- const task=r.data,step=task.definitionSnapshot.steps[0],ops=step.action.mechanism.steps,code=step.stepCode||step.id;
- const observation={schema:'teaching-virtual-observation/1',stepCode:code,kind:'target-click',targetId:ops.at(-1).targetId,completed:true,
- selectedToolId:'',selectedToolIds:step.toolRequirements?.map(r=>r.anyOf[0])||(step.toolId?[step.toolId]:[]),
- safetyKeys:(step.safetyRules||[]).map((s,i)=>s.key||s.code||s.id||'SAFETY-'+(i+1)),measurementNote:'',
- operations:ops.map(op=>({targetId:op.targetId,kind:op.kind,completed:true}))};
- const body={version:task.version,steps:[{completedStepCode:code,actionCode:step.actionCode||'COMPLETE_'+code.toUpperCase(),observation}]};
- const valid=await call('tran/v1/teaching/assignments/86/preview/validate',session.accessToken,body,'POST');
- assert.equal(valid.status,200,'historical task accepts complete observations');
- body.steps[0].observation.operations=[];
- const invalid=await call('tran/v1/teaching/assignments/86/preview/validate',session.accessToken,body,'POST');
- const dir='reports/bridge-tools-authoring-20260924';fs.mkdirSync(dir,{recursive:true});
- fs.writeFileSync(dir+'/api-contract.json',JSON.stringify({pass:valid.status===200&&invalid.status===400,validStatus:valid.status,missingOperationsStatus:invalid.status,syntheticReadOnlyContractTest:true},null,2));
- assert.equal(invalid.status,400,'incomplete observations rejected by running service');console.log('Live operation contract: complete accepted, missing operations rejected');
|