Source-candidate: poe d9a564e6d20f5f0fd75ce45b12cbbf8bb4c9d568\nSource-candidate: magica38ba5762b06716db1dee00d0afec162656488f5c\nValidated321whole-file phase plus whole bus1349/model-proxy100/webui490; final gate tooling checks pending, not landed.
68 lines
8.4 KiB
JavaScript
68 lines
8.4 KiB
JavaScript
// #656/1745 STATIC-only authored tests; no measured outcome until separately cleared.
|
|
// Generic fallback retains its current behavior; absent cache is NOT a universal refusal.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import { revocationFixture, outcome, id } from './model-revocation-fixture.mjs';
|
|
import { Credentials } from '../src/credentials.mjs';
|
|
const timeout=30000;
|
|
async function prepared(f){f.seed({matchingTmp:false});await f.prepare();}
|
|
async function finished(f){await prepared(f);f.complete();}
|
|
function proof(f){assert.equal(f.proofs.length,1,'real controller minted exact opaque model completion proof');return f.proofs[0];}
|
|
|
|
test('656 generic remove after exact completion skips only redundant model unlink and keeps metadata',{timeout},async t=>{
|
|
const f=await revocationFixture(t);await finished(f);const before=f.credentials.status(),count=f.counts.unlinkFinal;
|
|
const result=f.credentials.remove(f.instance);assert.equal(f.counts.unlinkFinal,count,'generic remove does not perform SECOND model unlink after exact completion');assert.deepEqual(result.services,['model']);assert.deepEqual(f.credentials.status(),before,'reference date and revoked status metadata unchanged');assert.equal(f.exists(f.file)||f.exists(f.temporary),false);f.valueFree();
|
|
});
|
|
for(const entry of ['model','model.tmp'])test('656 '+entry+' appearing after completed proof is preserved by unchanged unknown fence',{timeout},async t=>{
|
|
const f=await revocationFixture(t);await finished(f);const file=join(dirname(f.file),entry),body='owned unrecognized\n';fs.writeFileSync(file,body,{mode:0o600,flag:'wx'});const count=f.counts.unlinkFinal,result=await outcome(()=>f.credentials.remove(f.instance));
|
|
assert.equal(f.counts.unlinkFinal,count,'finished proof never authorizes another model pathname unlink');assert.equal(result.ok,false);assert.equal(result.code,'credential-file');assert.equal(id(fs.readFileSync(file)),id(body),'postcompletion unknown entry preserved');f.valueFree();
|
|
});
|
|
|
|
for(const state of ['valid','revoking'])test('656 '+state+' cached unconfirmed model keeps existing generic removal guard',{timeout},async t=>{
|
|
const f=await revocationFixture(t);f.seed({matchingTmp:false,valid:state==='valid'});const before=id(fs.readFileSync(f.file)),count=f.counts.unlinkFinal,result=await outcome(()=>f.credentials.remove(f.instance));assert.equal(result.ok,false);assert.equal(result.code,'credential-revoke-failed');assert.equal(f.counts.unlinkFinal,count);assert.equal(id(fs.readFileSync(f.file)),before);assert.equal(f.record()?.state,state);f.valueFree();
|
|
});
|
|
test('656 unconfirmed prepared model still cannot use finished-proof skip',{timeout},async t=>{
|
|
const f=await revocationFixture(t);await prepared(f);const count=f.counts.unlinkFinal,result=await outcome(()=>f.credentials.remove(f.instance));assert.equal(result.ok,false);assert.equal(result.code,'credential-revoke-failed');assert.equal(f.counts.unlinkFinal,count);assert.equal(f.record()?.state,'revoking');f.valueFree();
|
|
});
|
|
test('656 no-proof absent-cache model retains existing generic file removal behavior',{timeout},async t=>{
|
|
const f=await revocationFixture(t);f.seed({matchingTmp:false});f.credentials.hold(f.instance,'model');const count=f.counts.unlinkFinal,result=f.credentials.remove(f.instance);assert.deepEqual(result.services,['model']);assert.equal(f.counts.unlinkFinal-count,1,'ordinary generic no-cache behavior remains allowed');assert.equal(f.exists(f.file),false);f.valueFree();
|
|
});
|
|
test('656 no-record no-proof generic remove retains existing ENOENT behavior',{timeout},async t=>{
|
|
const f=await revocationFixture(t),count=f.counts.unlinkFinal,result=f.credentials.remove(f.instance);assert.deepEqual(result.services,['model']);assert.equal(f.counts.unlinkFinal-count,1,'no proof does not invent completed-proof skip');f.valueFree();
|
|
});
|
|
|
|
for(const action of ['register','unregister','reload','hold','claim','revoke','rewrite','close'])test('656 '+action+' invalidates latest completed proof without granting fallback authority',{timeout},async t=>{
|
|
const f=await revocationFixture(t);await finished(f);const token=proof(f),c=f.credentials;let signal;
|
|
if(action==='register')c.register(f.instance,'model',{store:true,file:f.file,rotateBy:'2099-01-01'});
|
|
if(action==='unregister')c.unregister(f.instance,'model');
|
|
if(action==='reload'){const result=await outcome(()=>c.reload(f.instance,'model','2099-01-01'));assert.equal(result.ok,false);assert.equal(result.code,'credential-file');}
|
|
if(action==='hold')c.hold(f.instance,'model');
|
|
if(action==='claim')signal=c.claim(f.instance,'model');
|
|
if(action==='revoke')c.revoke(f.instance,'model');
|
|
if(action==='rewrite')f.seed({matchingTmp:false,valid:true});
|
|
if(action==='close')c.close();
|
|
const result=await outcome(()=>c.finishModelRevocation(f.instance,token));assert.equal(result.ok,false);assert.equal(result.code,'credential-revoke-failed');if(signal)c.release(f.instance,'model',signal);if(action==='rewrite')assert.equal(f.exists(f.file),true);f.valueFree();
|
|
});
|
|
test('656 new snapshot invalidates old token even if later capture refuses on absent final',{timeout},async t=>{
|
|
const f=await revocationFixture(t);await finished(f);const token=proof(f),capture=await outcome(()=>f.credentials.captureModelRevocation(f.instance));assert.equal(capture.ok,false);assert.equal(capture.code,'credential-revoke-failed');const result=await outcome(()=>f.credentials.finishModelRevocation(f.instance,token));assert.equal(result.ok,false);assert.equal(result.code,'credential-revoke-failed');f.valueFree();
|
|
});
|
|
test('656 wrong instance cannot reuse a completed token',{timeout},async t=>{
|
|
const f=await revocationFixture(t);await finished(f);const token=proof(f),count=f.counts.unlinkFinal,result=await outcome(()=>f.credentials.finishModelRevocation('demo/other',token));assert.equal(result.ok,false);assert.equal(result.code,'credential-revoke-failed');assert.equal(f.counts.unlinkFinal,count);f.credentials.finishModelRevocation(f.instance,token);assert.equal(f.counts.unlinkFinal,count);f.valueFree();
|
|
});
|
|
|
|
test('656 nonmodel memory-first failure and retry remain unchanged after model completion',{timeout},async t=>{
|
|
const f=await revocationFixture(t);f.seed({matchingTmp:false});const file=join(dirname(f.file),'gitea'),value='fixture-gitea-key';f.remember(value);f.credentials.register(f.instance,'gitea',{store:true,file,rotateBy:'2099-01-01'});f.credentials.issue(f.instance,'gitea',value,'2099-01-01');await f.prepare();f.complete();const count=f.counts.unlinkFinal;f.faults.unlinkService=true;
|
|
const failure=await outcome(()=>f.credentials.remove(f.instance));assert.equal(f.counts.unlinkFinal,count,'nonmodel failure does not repeat model unlink');assert.equal(failure.ok,false);assert.equal(failure.code,'credential-file');assert.equal(f.credentials.status().find(s=>s.instance===f.instance&&s.service==='gitea').state,'revoked','nonmodel keeps memory-first semantics');assert.equal(f.exists(file),true,'failed nonmodel unlink remains honestly present');f.faults.unlinkService=false;
|
|
const result=f.credentials.remove(f.instance);assert.deepEqual([...result.services].sort(),['gitea','model']);assert.equal(f.counts.unlinkFinal,count);assert.equal(f.counts.unlinkService,2,'one failed then one successful service unlink; model remains completed');f.valueFree();
|
|
});
|
|
|
|
test('656 replaced private agent directory cannot reuse exact historical completion proof',{timeout},async t=>{
|
|
const f=await revocationFixture(t);await finished(f);const token=proof(f),directory=dirname(f.file);fs.renameSync(directory,join(f.root,'former-owned-agent-dir'));fs.mkdirSync(directory,{mode:0o700});const count=f.counts.unlinkFinal,result=await outcome(()=>f.credentials.finishModelRevocation(f.instance,token));assert.equal(result.ok,false);assert.equal(result.code,'credential-revoke-failed');assert.equal(f.counts.unlinkFinal,count);f.valueFree();
|
|
});
|
|
|
|
test('656 another Credentials epoch cannot use a former objects completed token',{timeout},async t=>{
|
|
const f=await revocationFixture(t);await finished(f);const token=proof(f);let other;t.after(()=>other?.close());other=new Credentials({storeRoot:f.storeRoot,dataRoot:f.dataRoot,modelAgents:[f.instance]});const count=f.counts.unlinkFinal,result=await outcome(()=>other.finishModelRevocation(f.instance,token));assert.equal(result.ok,false);assert.equal(result.code,'credential-revoke-failed');assert.equal(f.counts.unlinkFinal,count);f.credentials.finishModelRevocation(f.instance,token);assert.equal(f.counts.unlinkFinal,count);f.valueFree();
|
|
});
|