← back to Tk 10965 Zero Price Analysis

vienna-offline-adapter.mjs

35 lines

// Deterministic file-backed test adapter. All supported CLI writers hold the
// same state-file lock across the entire journey, including compare and write.
// Reads never write logs or cached record state back to disk.
import {readRegular,durableWrite,key} from './vienna-executor.mjs';
export function openOfflineAdapter(file,expectedStore) {
  const pinned=JSON.stringify(expectedStore);
  const load=()=>{
    const state=JSON.parse(readRegular(file));
    if(state.fixture!==true || state.version!==1 || !Array.isArray(state.records) || !Array.isArray(state.calls)) throw new Error('Offline fixture required');
    if(JSON.stringify(state.store)!==pinned) throw new Error('Adapter store mismatch');
    return state;
  };
  load();
  return {
    async shop(){return load().store;},
    async read(r){return load().records.find(x=>key(x)===key(r));},
    async set(r,before,after) {
      const state=load();
      const actual=state.records.find(x=>key(x)===key(r));
      // Recompare full current identity/preconditions, not a previously read object.
      if(JSON.stringify(actual)!==JSON.stringify({...r,onHand:before})) return {kind:'rejected',confirmedNoWrite:true};
      state.calls.push({method:'set',key:key(r)});
      const persist=()=>durableWrite(file,JSON.stringify(state,null,2)+'\n');
      persist();
      const fault=state.faults?.[key(r)];
      if(fault==='reject') return {kind:'rejected',confirmedNoWrite:true};
      if(fault==='throw-before') throw new Error('Simulated transport failure before response');
      actual.onHand=after; persist();
      if(fault==='throw-after') throw new Error('Simulated transport failure after persisted write');
      if(fault==='bad-postread') { actual.variantId='gid://shopify/ProductVariant/999';persist(); }
      return {kind:'success',key:key(r),quantity:after};
    }
  };
}