fix: forFlight refuel leaves an exactly-feasible tank (round up) — e2e 409
CI / build + unit + e2e (pull_request) Failing after 5m26s

This commit is contained in:
2026-09-19 13:00:06 +00:00
parent a801311fc5
commit 687e4f2169
4 changed files with 52 additions and 14 deletions
+9 -6
View File
@@ -248,7 +248,7 @@ export async function refuelPlane(inst: GameDb, planeId: string, opts: RefuelOpt
}
const mode = opts.mode ?? 'full';
let liters: number; // how much fuel to add
let targetFuel: number; // tank level after the refuel
if (mode === 'forFlight') {
if (!opts.packageId) throw new GameActionError(400, 'packageId is required when mode is "forFlight"');
@@ -269,11 +269,17 @@ export async function refuelPlane(inst: GameDb, planeId: string, opts: RefuelOpt
if (requiredL > plane.maxFuel) {
throw new GameActionError(409, `the leg to ${pkg.destIcao} needs ${Math.round(requiredL)} L even starting empty — it does not fit a full ${plane.maxFuel} L tank`);
}
liters = Math.max(0, requiredL - plane.fuel);
// Land EXACTLY on the leg requirement when topping up: flyPlane re-computes this same value and
// compares with strict `>`, so fuel := requiredL guarantees the leg is feasible. The alternative
// (fuel + liters) can round a hair BELOW R in IEEE754 — f + fl(R − f) is not guaranteed ≥ R — and
// would 409 on an otherwise-feasible leg. Already enough → free no-op, tank untouched.
targetFuel = Math.max(plane.fuel, requiredL);
} else {
liters = plane.maxFuel - plane.fuel;
// Exactly maxFuel (as before v0.16.0) — clients/tests compare against it with strict equality.
targetFuel = plane.maxFuel;
}
const liters = Math.max(0, targetFuel - plane.fuel);
const cost = Math.round(liters * FUEL_PRICE_PER_L * 100) / 100;
const account = (await inst.db.select().from(schema.account))[0];
if (!account) throw new Error('account row missing');
@@ -281,9 +287,6 @@ export async function refuelPlane(inst: GameDb, planeId: string, opts: RefuelOpt
throw new GameActionError(409, `insufficient funds: refueling costs ${cost}, you have ${Math.round(account.cash)}`);
}
// Full mode lands EXACTLY on maxFuel (as before v0.16.0), not fuel + liters — the sum can
// drift a ulp for fractional tanks and clients/tests compare against maxFuel exactly.
const targetFuel = mode === 'forFlight' ? plane.fuel + liters : plane.maxFuel;
await inst.db.update(schema.planes).set({ fuel: targetFuel }).where(eq(schema.planes.id, planeId));
if (cost > 0) {
await inst.db.update(schema.account).set({ cash: Math.round((account.cash - cost) * 100) / 100 });