Step 3.18: define sim public API and add coverage
CI / check (push) Successful in 38s
Details
CI / check (push) Successful in 38s
Details
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
50b038047f
commit
f33a9155cc
|
|
@ -16,4 +16,4 @@ jobs:
|
|||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm typecheck
|
||||
- run: pnpm lint
|
||||
- run: pnpm test
|
||||
- run: pnpm coverage
|
||||
|
|
|
|||
|
|
@ -176,3 +176,9 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
|
|||
- **Tutorial runs grant XP to the avatar** like any delivery; the plan does not exclude it. They credit the stored tutorial payout with no pilot pay.
|
||||
- **Repositions resolve to 0 credits and 0 XP** and never discover anything, since they only fly to bays.
|
||||
- **The caller passes `pilotIsAvatar` and the current discovered set**, so the function stays pure. "Exactly once" is the server's job: it must store the resolution and the acknowledged-at state transactionally.
|
||||
|
||||
## Step 3.18
|
||||
|
||||
- **Explicit public API.** `packages/sim/src/index.ts` lists every export. Internal helpers (`findLocation`, `findShipModel`, `standardCargo`, `buildContract`, `skillLevel`) are not exported; tests that need one import it from its module.
|
||||
- **Coverage** uses `@vitest/coverage-v8`, configured in the root `vitest.config.ts` to measure only `packages/sim/src` with a 90% line threshold. `pnpm coverage` runs the whole suite with coverage, and CI runs it in place of `pnpm test`, so a coverage drop fails CI.
|
||||
- **The Step 1.3 smoke test is removed** now that the sim package has real tests.
|
||||
|
|
|
|||
|
|
@ -36,4 +36,4 @@
|
|||
- [x] **Step 3.15 — In-flight position and progress.** `src/flight.ts`: `dispatchProgress` (clamped 0–1) and `dispatchPosition` (straight line from origin at departure to destination at arrival).
|
||||
- [x] **Step 3.16 — XP and leveling.** `src/xp.ts`: `deliveryXp`, `pilotLevel`, `skillPointsAvailable`.
|
||||
- [x] **Step 3.17 — Dispatch resolution.** `src/resolution.ts`: `resolveDispatch` returns `in-flight` or a `Resolution` (credits after pilot pay, XP, new ship location, discovery).
|
||||
- [ ] Step 3.18 — Sim package public API and coverage
|
||||
- [x] **Step 3.18 — Public API and coverage.** `src/index.ts` exports an explicit list (content lookups and contract building stay internal). `pnpm coverage` (V8, `packages/sim/src`, 90% line threshold) reports 100% lines; CI now runs `pnpm coverage` instead of `pnpm test`.
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@
|
|||
"typecheck": "pnpm -r --parallel typecheck",
|
||||
"lint": "eslint . && prettier --check .",
|
||||
"format": "prettier --write .",
|
||||
"test": "vitest run"
|
||||
"test": "vitest run",
|
||||
"coverage": "vitest run --coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@types/node": "^24",
|
||||
"@vitest/coverage-v8": "^5.0.3",
|
||||
"eslint": "^10.12.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"globals": "^17.13.0",
|
||||
|
|
|
|||
|
|
@ -1,15 +1,63 @@
|
|||
export * from "./clock";
|
||||
export * from "./rng";
|
||||
export * from "./positions";
|
||||
export * from "./distance";
|
||||
export * from "./travel";
|
||||
export * from "./fuel";
|
||||
export * from "./eligibility";
|
||||
export * from "./contract";
|
||||
export * from "./board";
|
||||
export * from "./tutorial";
|
||||
export * from "./pilot";
|
||||
export * from "./dispatch";
|
||||
export * from "./flight";
|
||||
export * from "./xp";
|
||||
export * from "./resolution";
|
||||
// Public API of the deterministic simulation. Internal helpers (content
|
||||
// lookups, contract building) stay unexported.
|
||||
|
||||
export { MS_PER_HOUR, MS_PER_MINUTE, gameToReal, realToGame } from "./clock";
|
||||
export type { GameTime, RealTime } from "./clock";
|
||||
|
||||
export { createRng, deriveSeed } from "./rng";
|
||||
export type { Rng, Seed } from "./rng";
|
||||
|
||||
export { bodyPosition, locationPosition } from "./positions";
|
||||
export type { Position } from "./positions";
|
||||
|
||||
export { routeDistance } from "./distance";
|
||||
export { travelTimeGameHours } from "./travel";
|
||||
export { fuelCost } from "./fuel";
|
||||
|
||||
export { canLandAt, checkRoute } from "./eligibility";
|
||||
export type { RouteCheck } from "./eligibility";
|
||||
|
||||
export { contractPayout } from "./contract";
|
||||
export type { Contract, PayoutInput } from "./contract";
|
||||
|
||||
export {
|
||||
boardPeriodIndex,
|
||||
boardPeriodMs,
|
||||
generateContractBoard,
|
||||
requiredLongHaulLevel,
|
||||
} from "./board";
|
||||
export type { BoardInput, SimContent } from "./board";
|
||||
|
||||
export { generateTutorialBoard, loanerModel } from "./tutorial";
|
||||
export type { TutorialBoardInput, TutorialLocationId } from "./tutorial";
|
||||
|
||||
export { LONG_HAUL_SKILL_ID, checkPilotEligibility } from "./pilot";
|
||||
export type { PilotCheck, SkillLevels } from "./pilot";
|
||||
|
||||
export {
|
||||
createDispatchRecord,
|
||||
createRepositionRecord,
|
||||
tripFuelCost,
|
||||
validateDispatch,
|
||||
validateReposition,
|
||||
} from "./dispatch";
|
||||
export type {
|
||||
DispatchCheck,
|
||||
DispatchFailure,
|
||||
DispatchProposal,
|
||||
DispatchRecord,
|
||||
PilotState,
|
||||
RecordInput,
|
||||
RepositionCheck,
|
||||
RepositionFailure,
|
||||
RepositionProposal,
|
||||
RepositionRecordInput,
|
||||
ShipState,
|
||||
} from "./dispatch";
|
||||
|
||||
export { dispatchPosition, dispatchProgress } from "./flight";
|
||||
|
||||
export { deliveryXp, pilotLevel, skillPointsAvailable } from "./xp";
|
||||
|
||||
export { resolveDispatch } from "./resolution";
|
||||
export type { Resolution, ResolveInput, ResolveResult } from "./resolution";
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("sim", () => {
|
||||
it("runs tests", () => {
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
});
|
||||
|
|
@ -2,11 +2,11 @@ import { content } from "@deadhead/content";
|
|||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
boardPeriodIndex,
|
||||
buildContract,
|
||||
generateTutorialBoard,
|
||||
loanerModel,
|
||||
type TutorialBoardInput,
|
||||
} from "../src/index";
|
||||
import { buildContract } from "../src/board";
|
||||
|
||||
const periodIndex = boardPeriodIndex(Date.parse("2100-01-01T00:00:00Z"), content.config);
|
||||
const atEarth: TutorialBoardInput = { loanerLocationId: "earth", periodIndex, worldSeed: 99 };
|
||||
|
|
|
|||
108
pnpm-lock.yaml
108
pnpm-lock.yaml
|
|
@ -14,6 +14,9 @@ importers:
|
|||
'@types/node':
|
||||
specifier: ^24
|
||||
version: 24.19.1
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3(vitest@5.0.3)
|
||||
eslint:
|
||||
specifier: ^10.12.0
|
||||
version: 10.12.0
|
||||
|
|
@ -34,7 +37,7 @@ importers:
|
|||
version: 8.71.0(eslint@10.12.0)(typescript@6.0.3)
|
||||
vitest:
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3(@types/node@24.19.1)(vite@8.3.2(@types/node@24.19.1))
|
||||
version: 5.0.3(@types/node@24.19.1)(@vitest/coverage-v8@5.0.3)(vite@8.3.2(@types/node@24.19.1))
|
||||
|
||||
apps/server: {}
|
||||
|
||||
|
|
@ -59,6 +62,27 @@ importers:
|
|||
|
||||
packages:
|
||||
|
||||
'@babel/helper-string-parser@7.29.7':
|
||||
resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-identifier@7.29.7':
|
||||
resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/parser@7.29.9':
|
||||
resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/types@7.29.8':
|
||||
resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@bcoe/v8-coverage@1.0.2':
|
||||
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@cacheable/memory@2.2.0':
|
||||
resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==}
|
||||
|
||||
|
|
@ -322,6 +346,23 @@ packages:
|
|||
resolution: {integrity: sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@vitest/coverage-v8@5.0.3':
|
||||
resolution: {integrity: sha512-+klsyz7BvT1vCU28Zkfzms1Ia78XD0V41U3FUtRaa3S+vOr/EXvK1O6BvVD7l1RzEjm6SONR2aybOvDDf9u0mQ==}
|
||||
peerDependencies:
|
||||
'@vitest/browser': 5.0.3
|
||||
vitest: 5.0.3
|
||||
peerDependenciesMeta:
|
||||
'@vitest/browser':
|
||||
optional: true
|
||||
|
||||
'@vitest/istanbul-lib-coverage@1.0.2':
|
||||
resolution: {integrity: sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==}
|
||||
engines: {node: '>=22'}
|
||||
|
||||
'@vitest/istanbul-lib-report@1.0.2':
|
||||
resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==}
|
||||
engines: {node: '>=22'}
|
||||
|
||||
'@vitest/mocker@5.0.3':
|
||||
resolution: {integrity: sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==}
|
||||
peerDependencies:
|
||||
|
|
@ -353,6 +394,9 @@ packages:
|
|||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ast-v8-to-istanbul@1.0.7:
|
||||
resolution: {integrity: sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==}
|
||||
|
||||
astronomy-engine@2.1.19:
|
||||
resolution: {integrity: sha512-8yWKNf7UeNbH458h3sAJ6ZgAjE5jTXp/mNNRFoC20j2SHwZIjAQeEsBB2Q3uCFRaTCCJRv33K2XhkhZQMXoX6w==}
|
||||
|
||||
|
|
@ -530,6 +574,9 @@ packages:
|
|||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
js-tokens@10.0.0:
|
||||
resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==}
|
||||
|
||||
json-schema-traverse@0.4.1:
|
||||
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||
|
||||
|
|
@ -624,6 +671,9 @@ packages:
|
|||
magic-string@1.4.2:
|
||||
resolution: {integrity: sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==}
|
||||
|
||||
magicast@0.5.5:
|
||||
resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==}
|
||||
|
||||
minimatch@10.2.6:
|
||||
resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
|
|
@ -728,6 +778,10 @@ packages:
|
|||
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tinyrainbow@3.2.0:
|
||||
resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
ts-api-utils@2.5.0:
|
||||
resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
|
||||
engines: {node: '>=18.12'}
|
||||
|
|
@ -863,6 +917,21 @@ packages:
|
|||
|
||||
snapshots:
|
||||
|
||||
'@babel/helper-string-parser@7.29.7': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.29.7': {}
|
||||
|
||||
'@babel/parser@7.29.9':
|
||||
dependencies:
|
||||
'@babel/types': 7.29.8
|
||||
|
||||
'@babel/types@7.29.8':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.29.7
|
||||
'@babel/helper-validator-identifier': 7.29.7
|
||||
|
||||
'@bcoe/v8-coverage@1.0.2': {}
|
||||
|
||||
'@cacheable/memory@2.2.0':
|
||||
dependencies:
|
||||
'@cacheable/utils': 2.5.0
|
||||
|
|
@ -1099,6 +1168,24 @@ snapshots:
|
|||
'@typescript-eslint/types': 8.71.0
|
||||
eslint-visitor-keys: 5.0.1
|
||||
|
||||
'@vitest/coverage-v8@5.0.3(vitest@5.0.3)':
|
||||
dependencies:
|
||||
'@bcoe/v8-coverage': 1.0.2
|
||||
'@vitest/istanbul-lib-coverage': 1.0.2
|
||||
'@vitest/istanbul-lib-report': 1.0.2
|
||||
ast-v8-to-istanbul: 1.0.7
|
||||
magicast: 0.5.5
|
||||
obug: 2.2.1
|
||||
std-env: 4.3.0
|
||||
tinyrainbow: 3.2.0
|
||||
vitest: 5.0.3(@types/node@24.19.1)(@vitest/coverage-v8@5.0.3)(vite@8.3.2(@types/node@24.19.1))
|
||||
|
||||
'@vitest/istanbul-lib-coverage@1.0.2': {}
|
||||
|
||||
'@vitest/istanbul-lib-report@1.0.2':
|
||||
dependencies:
|
||||
'@vitest/istanbul-lib-coverage': 1.0.2
|
||||
|
||||
'@vitest/mocker@5.0.3(vite@8.3.2(@types/node@24.19.1))':
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
|
|
@ -1125,6 +1212,12 @@ snapshots:
|
|||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
ast-v8-to-istanbul@1.0.7:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
estree-walker: 3.0.3
|
||||
js-tokens: 10.0.0
|
||||
|
||||
astronomy-engine@2.1.19: {}
|
||||
|
||||
balanced-match@4.0.4: {}
|
||||
|
|
@ -1293,6 +1386,8 @@ snapshots:
|
|||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
js-tokens@10.0.0: {}
|
||||
|
||||
json-schema-traverse@0.4.1: {}
|
||||
|
||||
json-stable-stringify-without-jsonify@1.0.1: {}
|
||||
|
|
@ -1363,6 +1458,12 @@ snapshots:
|
|||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.6.0
|
||||
|
||||
magicast@0.5.5:
|
||||
dependencies:
|
||||
'@babel/parser': 7.29.9
|
||||
'@babel/types': 7.29.8
|
||||
source-map-js: 1.2.2
|
||||
|
||||
minimatch@10.2.6:
|
||||
dependencies:
|
||||
brace-expansion: 5.0.12
|
||||
|
|
@ -1458,6 +1559,8 @@ snapshots:
|
|||
fdir: 6.5.0(picomatch@4.0.7)
|
||||
picomatch: 4.0.7
|
||||
|
||||
tinyrainbow@3.2.0: {}
|
||||
|
||||
ts-api-utils@2.5.0(typescript@6.0.3):
|
||||
dependencies:
|
||||
typescript: 6.0.3
|
||||
|
|
@ -1496,7 +1599,7 @@ snapshots:
|
|||
'@types/node': 24.19.1
|
||||
fsevents: 2.3.3
|
||||
|
||||
vitest@5.0.3(@types/node@24.19.1)(vite@8.3.2(@types/node@24.19.1)):
|
||||
vitest@5.0.3(@types/node@24.19.1)(@vitest/coverage-v8@5.0.3)(vite@8.3.2(@types/node@24.19.1)):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.3
|
||||
'@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@24.19.1))
|
||||
|
|
@ -1514,6 +1617,7 @@ snapshots:
|
|||
why-is-node-running: 3.2.1
|
||||
optionalDependencies:
|
||||
'@types/node': 24.19.1
|
||||
'@vitest/coverage-v8': 5.0.3(vitest@5.0.3)
|
||||
transitivePeerDependencies:
|
||||
- msw
|
||||
|
||||
|
|
|
|||
|
|
@ -5,5 +5,12 @@ import { defineConfig } from "vitest/config";
|
|||
export default defineConfig({
|
||||
test: {
|
||||
projects: ["apps/*", "packages/*"],
|
||||
// `pnpm coverage` measures the simulation core (plan Step 3.18).
|
||||
coverage: {
|
||||
provider: "v8",
|
||||
include: ["packages/sim/src/**/*.ts"],
|
||||
reporter: ["text", "html"],
|
||||
thresholds: { lines: 90 },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue