bend_puzzle/test/v4784-pan-solve-production-smoke-test.js

53 lines
3.7 KiB
JavaScript
Raw Normal View History

2026-07-31 12:54:46 +09:00
'use strict';
const {vm,assert,app,functionSource}=require('./helpers/app-source');
assert(!app.includes('SPECIAL_CELL_DEBUG_ALL_LEVELS'),'Special-cell debug scheduling remains in production code');
assert(functionSource('addSpecialCellPattern').includes('AppLogic.specialSchedule(level,'),'Special cells do not use the production level directly');
const solvedState={paths:[],specialProgress:{crossings:[]},solved:true,expanded:true,solvedBy:'Player',scoreAwarded:500,store:null,rev:10};
const sanitizeContext={
puzzleOf:()=>({}),metaState:()=>solvedState,cellSet:()=>new Set(),crossingKeys:()=>[],warpMap:()=>new Map(),
normalizePath:value=>value,ckey:()=>'',sameCell:()=>false,pathCellsAdjacent:()=>false,nextRevision:()=>11,
markStateDirty:()=>{throw new Error('A durable clear was marked dirty by route revalidation')},
isSolved:()=>false,LEGACY_LOCAL_SOLVER:'Legacy'
};
vm.createContext(sanitizeContext);
vm.runInContext(`${functionSource('sanitizeStateForPuzzle')}\nthis.sanitizeStateForPuzzle=sanitizeStateForPuzzle;`,sanitizeContext);
sanitizeContext.sanitizeStateForPuzzle({id:'B0'},{quiet:true});
assert(solvedState.solved===true&&solvedState.scoreAwarded===500&&solvedState.solvedBy==='Player','Route revalidation downgraded a durable clear');
let center=[0,0];
const coverageContext={
overviewCache:{revision:4,width:800,height:600,scale:.2,anchorX:0,anchorY:0,unit:20,overscan:100},
minimapWorldRevision:4,cam:{scale:.2},MIN_CAMERA_SCALE:.1,getViewportRect:()=>({width:800,height:600}),
cameraCenterInChunks:()=>center
};
vm.createContext(coverageContext);
vm.runInContext(`${functionSource('overviewCacheNeedsInteractionRebuild')}\nthis.needsRefresh=overviewCacheNeedsInteractionRebuild;`,coverageContext);
assert(!coverageContext.needsRefresh(),'A centered overview cache was treated as exhausted');
center=[5,0];
assert(coverageContext.needsRefresh(),'A held pan beyond overview overscan did not request a cache refresh');
let frameCallback=null,idleCallback=null,drawOptions=null;
const scheduleContext={
overviewFrame:0,overviewDirty:true,overviewLastDraw:0,overviewDelayTimer:0,overviewAllowInteractionBuild:false,overviewInteractionLastBuild:0,
OVERVIEW_INTERACTION_REBUILD_INTERVAL:180,AUXILIARY_FRAME_INTERVAL:1000/30,overviewCanvas:{},
interactionActive:()=>true,inWorldOverview:()=>true,
requestAnimationFrame:callback=>{frameCallback=callback;return 1},
requestIdleCallback:callback=>{idleCallback=callback;return 2},
markVisualFrame:()=>{},drawWorldOverview:options=>{drawOptions=options},perfNow:()=>201
};
vm.createContext(scheduleContext);
vm.runInContext(`${functionSource('scheduleWorldOverview')}\nthis.scheduleWorldOverview=scheduleWorldOverview;`,scheduleContext);
scheduleContext.scheduleWorldOverview(false,{allowDuringInteraction:true});
assert(typeof frameCallback==='function','An in-gesture overview refresh was not frame-scheduled');
frameCallback(200);
assert(typeof idleCallback==='function','An in-gesture overview refresh was not deferred to idle time');
idleCallback();
assert(drawOptions?.allowDuringInteraction===true&&scheduleContext.overviewInteractionLastBuild===201,'The bounded overview refresh was blocked while panning');
const applyCamera=functionSource('applyCamera');
assert(applyCamera.includes('overviewCacheNeedsInteractionRebuild()')&&applyCamera.includes('allowDuringInteraction:cameraGestureActive'),'Camera painting does not replenish an exhausted overview cache during a held pan');
assert(app.includes('CAMERA_DISPLAY_WATCHDOG_MS=34')&&functionSource('scheduleCustomCursorFrame').includes('requestAnimationFrame(paint)')&&!app.includes('CURSOR_DISPLAY_WATCHDOG_MS'),'A cursor fallback timer can still preempt the next 60 Hz display frame');
console.log('v47.84 pan continuity, durable clear, and production gate checks passed');