fix: resume campaign progress safely
This commit is contained in:
161
scripts/verify-battle-result-persistence-retry.mjs
Normal file
161
scripts/verify-battle-result-persistence-retry.mjs
Normal file
@@ -0,0 +1,161 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
const [battleSource, campaignSource] = await Promise.all([
|
||||
readFile(
|
||||
new URL(
|
||||
'../src/game/scenes/BattleScene.ts',
|
||||
import.meta.url
|
||||
),
|
||||
'utf8'
|
||||
),
|
||||
readFile(
|
||||
new URL(
|
||||
'../src/game/state/campaignState.ts',
|
||||
import.meta.url
|
||||
),
|
||||
'utf8'
|
||||
)
|
||||
]);
|
||||
|
||||
const publishStart = battleSource.indexOf(
|
||||
'private publishFirstBattleReport('
|
||||
);
|
||||
const publishEnd = battleSource.indexOf(
|
||||
'\n private resultSubtitle(',
|
||||
publishStart
|
||||
);
|
||||
assert(
|
||||
publishStart >= 0 && publishEnd > publishStart,
|
||||
'BattleScene must retain a bounded result-persistence method.'
|
||||
);
|
||||
const publishSource = battleSource.slice(
|
||||
publishStart,
|
||||
publishEnd
|
||||
);
|
||||
assert(
|
||||
publishSource.includes(
|
||||
'persistedReport = setFirstBattleReport('
|
||||
),
|
||||
'BattleScene must persist its complete battle report through the atomic campaign writer.'
|
||||
);
|
||||
assert(
|
||||
publishSource.includes(
|
||||
"this.resultReportPersistenceStatus =\n 'failed';"
|
||||
) &&
|
||||
publishSource.includes('return false;'),
|
||||
'A failed campaign write must leave the result screen in an explicit retry state.'
|
||||
);
|
||||
assert(
|
||||
publishSource.includes(
|
||||
"this.resultReportPersistenceStatus =\n 'complete';"
|
||||
) &&
|
||||
publishSource.includes('return true;'),
|
||||
'A successful campaign write must mark the result report complete.'
|
||||
);
|
||||
|
||||
const continueStart = battleSource.indexOf(
|
||||
'private handleResultContinue()'
|
||||
);
|
||||
const continueEnd = battleSource.indexOf(
|
||||
'\n private hideBattleResult(',
|
||||
continueStart
|
||||
);
|
||||
assert(
|
||||
continueStart >= 0 && continueEnd > continueStart,
|
||||
'BattleScene must retain a bounded result continuation handler.'
|
||||
);
|
||||
const continueSource = battleSource.slice(
|
||||
continueStart,
|
||||
continueEnd
|
||||
);
|
||||
assert(
|
||||
continueSource.includes(
|
||||
"this.resultReportPersistenceStatus ===\n 'failed'"
|
||||
) &&
|
||||
continueSource.includes(
|
||||
'!this.publishFirstBattleReport(outcome)'
|
||||
) &&
|
||||
continueSource.includes(
|
||||
'this.showBattleResult(outcome);'
|
||||
),
|
||||
'The result CTA must retry persistence in place and redraw the result only after recovery.'
|
||||
);
|
||||
assert(
|
||||
battleSource.includes("return '저장 다시 시도';"),
|
||||
'The failed result CTA must clearly say “저장 다시 시도”.'
|
||||
);
|
||||
assert(
|
||||
battleSource.includes(
|
||||
"outcome === 'victory' ||\n this.resultReportPersistenceStatus === 'failed'"
|
||||
),
|
||||
'Defeat results with an unsaved report must also expose the persistence retry CTA.'
|
||||
);
|
||||
assert(
|
||||
battleSource.includes(
|
||||
'const retryButton = this.addResultButton('
|
||||
) &&
|
||||
battleSource.includes(
|
||||
'const titleButton = this.addResultButton('
|
||||
) &&
|
||||
battleSource.includes(
|
||||
'this.setResultButtonEnabled(\n retryButton,\n false'
|
||||
) &&
|
||||
battleSource.includes(
|
||||
'this.setResultButtonEnabled(\n titleButton,\n false'
|
||||
),
|
||||
'Normal retry and title navigation must be visibly disabled while result persistence has failed.'
|
||||
);
|
||||
assert(
|
||||
battleSource.includes(
|
||||
'this.resultNavigationPending ||\n this.blockResultNavigationWhilePersistenceFailed()'
|
||||
),
|
||||
'All result navigation must retain a persistence-failure guard even if a disabled button is invoked indirectly.'
|
||||
);
|
||||
assert(
|
||||
battleSource.includes(
|
||||
"const improvementEnabled =\n this.resultReportPersistenceStatus !==\n 'failed';"
|
||||
) &&
|
||||
battleSource.includes(
|
||||
"improvementEnabled,\n false,\n () => this.openResultSortieImprovement(outcome)"
|
||||
),
|
||||
'Result-screen formation improvement must remain disabled until the battle report is durable.'
|
||||
);
|
||||
assert(
|
||||
battleSource.includes(
|
||||
'reportPersistence:\n this.resultReportPersistenceStatus'
|
||||
),
|
||||
'The result persistence state must remain observable to the browser regression.'
|
||||
);
|
||||
|
||||
const persistenceStart = campaignSource.indexOf(
|
||||
'function persistCampaignState('
|
||||
);
|
||||
const persistenceEnd = campaignSource.indexOf(
|
||||
'\nfunction tryStorage()',
|
||||
persistenceStart
|
||||
);
|
||||
assert(
|
||||
persistenceStart >= 0 &&
|
||||
persistenceEnd > persistenceStart,
|
||||
'Campaign persistence must retain a bounded writer.'
|
||||
);
|
||||
const persistenceSource = campaignSource.slice(
|
||||
persistenceStart,
|
||||
persistenceEnd
|
||||
);
|
||||
const canonicalWrite = persistenceSource.indexOf(
|
||||
'campaignSaveSlotKey(state.activeSaveSlot)'
|
||||
);
|
||||
const compatibilityWrite = persistenceSource.indexOf(
|
||||
'storage.setItem(campaignStorageKey, serialized)'
|
||||
);
|
||||
assert(
|
||||
canonicalWrite >= 0 &&
|
||||
compatibilityWrite > canonicalWrite,
|
||||
'The canonical campaign slot must be written before the compatibility mirror.'
|
||||
);
|
||||
|
||||
console.log(
|
||||
'Verified battle-result persistence failure/retry UI, debug observability, and canonical-slot-first atomic write contract.'
|
||||
);
|
||||
Reference in New Issue
Block a user