返回 CodeWhale
ocr-adapters.test.mjs
根目录 / crates / tui / extension-host / test / ocr-adapters.test.mjs
1 import test from 'node:test'
2 import assert from 'node:assert/strict'
3 import { transformStockSnapshot } from '../dist/stock-adapters.mjs'
4 import { createHarnessModule } from '../dist/builtin/harness.mjs'
5 const native = extra => ({ kind:'ocr_process', state:'native', status:'success', can_fallback:false, ...extra })
6 const tess = extra => ({ kind:'ocr_process', state:'tesseract', status:'complete', success:true, exit_code:0, ...extra })
7 const metadata = value => transformStockSnapshot(value).result.metadata
8 const owner = { plugin_id:'host:harness', generation:3, owner_token:'fixture-owner' }
9 const params = { owner, execution_id:'opaque-ocr-job', ticket:'native-grant', deadline_ms:1000 }
10
11 test('Native success, error and missing backend are finite private-text directives', () => {
12 assert.deepEqual(metadata(native({})),{kind:'ocr_decision',code:'native_success',trim_end:false})
13 assert.deepEqual(metadata(native({status:'error'})),{kind:'ocr_decision',code:'native_error',trim_end:false})
14 assert.equal(metadata(native({status:'unavailable'})).code,'no_backend')
15 assert.match(metadata(native({status:'unavailable'})).message,/no local OCR backend/)
16 })
17 test('OCR fallback is admitted only with a separate exact continuation grant', () => {
18 for (const status of ['error','unavailable']) assert.deepEqual(metadata(native({status,can_fallback:true,next_ticket:'tesseract-grant'})),{kind:'ocr_decision',code:'fallback',trim_end:false})
19 for (const value of [native({status:'error',can_fallback:true}),native({status:'error',next_ticket:'not-admitted'}),native({can_fallback:true,next_ticket:'extra'}),native({status:'error',can_fallback:true,next_ticket:'x'.repeat(257)})]) assert.throws(()=>metadata(value))
20 })
21 test('Tesseract success requests private Rust trimming and failure prefix without private diagnostics', () => {
22 assert.deepEqual(metadata(tess({})),{kind:'ocr_decision',code:'tesseract_success',trim_end:true})
23 assert.equal(metadata(tess({success:false,exit_code:2})).message,'tesseract failed (exit Some(2)): ')
24 assert.equal(metadata(tess({success:false,exit_code:null})).message,'tesseract failed (exit None): ')
25 assert.deepEqual(metadata({kind:'ocr_process',state:'tesseract',status:'fault'}),{kind:'ocr_decision',code:'fault',trim_end:false})
26 })
27 test('unknown, contradictory, private-byte or oversized OCR projections refuse', () => {
28 for (const value of [native({status:'retry'}),native({can_fallback:'yes'}),native({path:'/private/image.png'}),native({stdout:'private text'}),tess({status:'pending'}),tess({exit_code:1.5}),tess({exit_code:2**31}),tess({success:null}),tess({stderr:'x'.repeat(1024*1024)}),tess({next_ticket:'third-launch'}),{kind:'ocr_process',state:'tesseract',status:'fault',stdout:'private'}]) assert.throws(()=>metadata(value))
29 })
30 test('actual compiled runner redeems ordered Native and Tesseract grants once', async () => {
31 const calls=[]
32 const runner=createHarnessModule({async request(method,input) {calls.push([method,input]);return calls.length===1?native({status:'error',can_fallback:true,next_ticket:'tesseract-grant'}):tess({})}},owner)
33 const result=await runner.run(params,new AbortController().signal)
34 assert.deepEqual(result.result.metadata,{kind:'ocr_decision',code:'tesseract_success',trim_end:true})
35 assert.deepEqual(calls,[['exec/redeem',{owner,execution_id:'opaque-ocr-job',ticket:'native-grant'}],['exec/redeem',{owner,execution_id:'opaque-ocr-job',ticket:'tesseract-grant'}]])
36 assert.equal(JSON.stringify(calls).includes('path'),false)
37 await runner.dispose()
38 })
39 test('Native completion or no fallback performs no Tesseract redemption', async () => {
40 for (const value of [native({}),native({status:'error'}),native({status:'unavailable'})]) {
41 let calls=0
42 const runner=createHarnessModule({async request(){calls++;return value}},owner)
43 await runner.run(params,new AbortController().signal)
44 assert.equal(calls,1);await runner.dispose()
45 }
46 })
47 test('wrong initial/continuation stage and wrong invocation owner refuse', async () => {
48 const first=createHarnessModule({async request(){return tess({})}},owner)
49 await assert.rejects(first.run(params,new AbortController().signal),/must begin/);await first.dispose()
50 let calls=0
51 const second=createHarnessModule({async request(){calls++;return native({status:'error',can_fallback:true,next_ticket:'next-grant'})}},owner)
52 await assert.rejects(second.run(params,new AbortController().signal),/wrong stage/)
53 assert.equal(calls,2);await second.dispose()
54 const forbidden=createHarnessModule({async request(){throw new Error('must never reach RPC')}},owner)
55 await assert.rejects(forbidden.run({...params,owner:{...owner,owner_token:'wrong-owner'}},new AbortController().signal),/stale/);await forbidden.dispose()
56 })
57 test('OCR cancellation before continuation or late reply never retries', async () => {
58 const stop=new AbortController();let calls=0
59 const first=createHarnessModule({async request(){calls++;stop.abort();return native({status:'error',can_fallback:true,next_ticket:'next-grant'})}},owner)
60 await assert.rejects(first.run(params,stop.signal),/cancelled/);assert.equal(calls,1);await first.dispose()
61 const abort=new AbortController();let finish;calls=0
62 const second=createHarnessModule({request(){calls++;return calls===1?Promise.resolve(native({status:'unavailable',can_fallback:true,next_ticket:'next-grant'})):new Promise(resolve=>{finish=resolve})}},owner)
63 const pending=second.run(params,abort.signal)
64 while(!finish) await Promise.resolve()
65 abort.abort();await assert.rejects(pending,/cancelled/)
66 finish(tess({}));assert.equal(calls,2);await second.dispose()
67 })
68 test('Core or Host rejection never attempts another backend or grant', async () => {
69 let calls=0
70 const runner=createHarnessModule({async request(){calls++;throw new Error('Core authority withdrawn')}},owner)
71 await assert.rejects(runner.run(params,new AbortController().signal),/withdrawn/)
72 assert.equal(calls,1);await runner.dispose()
73 })
74
74 lines Plain Text