step 10.1: done
This commit is contained in:
+12
-11
@@ -4,9 +4,9 @@
|
||||
> If this file disagrees with the code, trust the code and fix this file.
|
||||
|
||||
## 1. Where we are
|
||||
- **Last completed step:** 9.6 (Update README with real run instructions) — TICKED in STATUS.md
|
||||
- **Next step:** Phase 9 is complete. Ready to start Stretch Goals (S1–S7) or address Known Issues from STATUS.md.
|
||||
- **Updated:** 2026-10-21
|
||||
- **Last completed step:** 10.1 (Fix expedition START corrupting energy) — TICKED in STATUS.md
|
||||
- **Next step:** Phase 10.2 — Fix expedition CLAIM overwriting XP
|
||||
- **Updated:** 2026-10-22
|
||||
- **Was the last session cut off mid-task?** No
|
||||
|
||||
## 2. Verified state (run these first)
|
||||
@@ -14,7 +14,7 @@
|
||||
|---|---|
|
||||
| `npm install` | succeeds |
|
||||
| `npm run migrate` | applies 001–006 / "nothing to do" |
|
||||
| `npm test` | **176 passing, 0 failing** (85 unit + 91 integration incl. 1 e2e) |
|
||||
| `npm test` | **178 passing, 0 failing** (85 unit + 93 integration incl. 2 new 10.1 tests) |
|
||||
| `npm run seed:dev` | creates demo user "demo"/"demo123", hero "Aldric" (warrior), starter gear |
|
||||
| `npm start` + `curl localhost:3000/api/health` | `{"ok":true}` |
|
||||
|
||||
@@ -31,10 +31,12 @@
|
||||
- `public/js/auth.js` — Shared API wrapper + showError.
|
||||
- `scripts/seed-dev.js` — Creates demo account + hero + starter gear.
|
||||
- `src/app.js` — Express app with helmet, rate limiters, catch-all 404, 500 HTML serving.
|
||||
- `src/services/expeditionService.js` — **Fixed in 10.1:** transaction param order corrected `(cid, ne, eat, defid, sat, endAt)`.
|
||||
- `src/services/*` — All game services.
|
||||
- `src/routes/*` — All route handlers.
|
||||
- `src/game/*` — Pure game logic.
|
||||
- `tests/integration/*.test.js` — 91 integration tests (incl. e2e-playable.test.js).
|
||||
- `tests/integration/expeditions.test.js` — 16 expedition tests (incl. 2 new 10.1 DB-level assertions).
|
||||
- `tests/integration/*.test.js` — 93 integration tests (incl. 1 e2e).
|
||||
- `tests/unit/*.test.js` — 85 unit tests.
|
||||
- `README.md` — Updated with real run instructions, play-through steps, project structure, known issues.
|
||||
|
||||
@@ -47,6 +49,7 @@
|
||||
- Starter gear documented but not auto-created on character creation.
|
||||
- `GET /api/zones` documented in API.md but not implemented (Issue #2).
|
||||
- Mobile nav uses emoji icons (⚔️ 🏕️ 🎒) — verify they render acceptably on all platforms.
|
||||
- **10.2 next:** expedition claim overwrites XP instead of adding to it; needs shared `applyXpGain` function.
|
||||
|
||||
## 5. Decisions & deviations from the docs
|
||||
- D-004..D-007 in `docs/DECISIONS.md` (combat rounds, equipment counted once, session store, clock).
|
||||
@@ -56,7 +59,6 @@
|
||||
- Inventory response uses `itemId` (item.id) and `inventoryId` (inventory row id) to avoid confusion.
|
||||
- Consumable effect parsing: regex `^(heal|energy):(\d+)$` on items.effect field.
|
||||
- Qty decrement avoids CHECK constraint violation: delete directly when qty==1, decrement when qty>1.
|
||||
- D-008..D-013 from previous steps.
|
||||
|
||||
## 6. Gotchas learned
|
||||
- `npm test` must be `node --test` with NO path/glob; files must be named `*.test.js`.
|
||||
@@ -65,7 +67,7 @@
|
||||
- Verify test COUNT, not just "all passing".
|
||||
- Node.js `fetch` doesn't persist cookies across calls — extract `Set-Cookie` header and pass manually in integration tests.
|
||||
- SQL `SELECT id` from inventory returns the auto-increment row id; rename with `AS inventory_id` when you also need `item_id`.
|
||||
- Transaction callback args must match the call order exactly.
|
||||
- Transaction callback args must match the call order exactly. (This is the 10.1 bug — double-check param/call order in every new transaction.)
|
||||
- Error responses from route-level `AppError`s do NOT include `data` field.
|
||||
- `setNow` affects ALL `now()` calls including those in service logic.
|
||||
- Shop API returns `{ ok: true, data: { stock: [...], character: {...} } }` — field is `stock`, not `stocks`.
|
||||
@@ -86,9 +88,8 @@
|
||||
- `src/utils/time` exports `{ now, setNow, resetNow }` directly — `const time = require(...)`, not `const { time }`.
|
||||
- Inventory sell endpoint (`POST /api/shop/sell`) only accepts `material`-type items.
|
||||
- `handleListInventory` in `src/routes/inventory.js` — parameter name must be `res`, not `_res`.
|
||||
- Character profile response has `energy` at `data.energy.current`, NOT `data.character.energy`.
|
||||
|
||||
## 7. Next-step briefing
|
||||
- **Phase 9 is complete.** All hardening and polish done.
|
||||
- **Stretch Goals available:** S1 (PvP duels), S2 (Skills/talents), S3 (Guilds), S4 (Player market), S5 (Daily rewards/quests), S6 (In-game chat), S7 (Achievements).
|
||||
- **Or address Known Issues:** Issue #2 (`GET /api/zones` missing — major), Issue #5 (equipment not in combat — minor), Issue #3 (`npm run seed` dead script — cosmetic).
|
||||
- **Recommended next step:** Fix Known Issue #2 (`GET /api/zones`) since it's a documented API gap and would improve the hunt UI. Then consider S7 (Achievements) as it's a self-contained, low-risk stretch goal that adds meaningful gameplay.
|
||||
- **Next step: 10.2** — Fix expedition CLAIM overwriting XP. The `claimExpedition` transaction uses `updateCharacterXp.run(xp, lvl, pts, cid)` which OVERWRITES xp/level/points instead of ADDING to current values. Needs a shared `applyXpGain` function used by BOTH `huntService` and `expeditionService`.
|
||||
- **Before 10.2:** check `src/services/expeditionService.js` `claimExpedition` transaction — same param-order bug pattern as 10.1 may be present.
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
> - Keep Session Log entries to one or two lines.
|
||||
|
||||
## Current Focus
|
||||
- **Phase:** 10.1z
|
||||
- **Step:** Stretch Goals
|
||||
- **Phase:** 10.1
|
||||
- **Step:** 10.2 — Fix expedition CLAIM overwriting XP
|
||||
- **Blocked?** No
|
||||
- **Last verified working command:** `npm test` — 176/176 passing (85 unit + 91 integration + 1 e2e), 2026-10-21 step 9.6
|
||||
- **Last verified working command:** `npm test` — 178/178 passing (85 unit + 93 integration incl. 2 new 10.1 tests), 2026-10-22 step 10.1
|
||||
|
||||
## Project Health
|
||||
| Item | State |
|
||||
|---|---|
|
||||
| App starts (`npm start`) | ✅ smoke-tested: register → create hero → 12 hunts → level-up (real seeded zones) |
|
||||
| Tests pass (`npm test`) | ✅ 93/93 (runs EVERY *.test.js; auth/combat/health were previously skipped by a bad glob) |
|
||||
| Tests pass (`npm test`) | ✅ 178/178 (85 unit + 93 integration incl. 2 new 10.1 tests) |
|
||||
| Migrations run clean on empty DB | ✅ 001_users, 002_characters, 003_content (also seeds zones/monsters/items), 004_inventory, 006_logs (005 reserved for expeditions) |
|
||||
| Can register + login | ✅ verified by tests |
|
||||
| Data persists across restart | ⚠️ sessions use the real SQLite store (also in tests); a restart test is still missing |
|
||||
@@ -138,8 +138,9 @@
|
||||
---
|
||||
|
||||
### A. Expedition bugs (verified live)
|
||||
- [ ] **10.1** Fix expedition START corrupting energy. In `startExpedition` (`src/services/expeditionService.js`) the transaction function's parameters are in a different order than the call, so the DB gets `energy = def.energy_cost` and `energy_updated_at = endsAt`. Fix so the DB stores `energy - cost` and the timestamp returned by `computeEnergy`.
|
||||
- [x] **10.1** Fix expedition START corrupting energy. In `startExpedition` (`src/services/expeditionService.js`) the transaction function's parameters are in a different order than the call, so the DB gets `energy = def.energy_cost` and `energy_updated_at = endsAt`. Fix so the DB stores `energy - cost` and the timestamp returned by `computeEnergy`.
|
||||
*Acceptance:* test: hero energy 100, start the 10-energy expedition → DB `energy` is 90 and `energy_updated_at` is NOT in the future; `GET /api/character` shows 90; response energy equals DB energy. Test also starting at energy 55 → 45.
|
||||
*Verified:* 2 new integration tests (DB-level assertions) + 14 existing expedition tests pass; transaction params `(cid, ne, eat, defid, sat, endAt)` now match call `(characterId, newEnergy, updatedAtMs, defId, startedAt, endsAt)`.
|
||||
- [ ] **10.2** Fix expedition CLAIM overwriting XP. Claim must ADD the reward to current XP, run `checkLevelUp`, ADD (not overwrite) unspent points, and heal to the new max HP on level-up. Extract ONE shared function (e.g. `applyXpGain` in `src/game/leveling.js` or a small progression service) and use it from BOTH `huntService` and `expeditionService`.
|
||||
*Acceptance:* tests: xp 80 + reward 30 at level 1 → level 2, xp 10, unspent_points +3; xp 0 + 30 → xp 30, level 1; a reward spanning two levels → both levels gained, points +6; double claim still rejected; gold added correctly. Existing hunt tests still pass.
|
||||
- [ ] **10.3** Make expeditions testable: use `time.now()` and an injectable `rng` (no `Date.now()` / `Math.random()` in `expeditionService.js`).
|
||||
@@ -227,6 +228,7 @@ Severity: `blocker` / `major` / `minor` / `cosmetic`. Status: `open` / `fixing`
|
||||
|
||||
## Session Log
|
||||
_Format:_ `YYYY-MM-DD | step | what changed | result`
|
||||
- 2026-10-22 | 10.1 | Fixed expedition start energy corruption: transaction params in `startExpedition` were mismatched (DB stored `energy = def.energy_cost` and `energy_updated_at = endsAt`); corrected param order to `(cid, ne, eat, defid, sat, endAt)` matching call `(characterId, newEnergy, updatedAtMs, defId, startedAt, endsAt)`; added 2 DB-level integration tests | 178/178 tests pass
|
||||
- 2026-10-21 | 9.6 | Rewrote README.md: removed AI-agent meta, added real run instructions (install/configure/migrate/seed/start/test), play-through steps, project structure, known issues table, architecture notes | 176/176 tests pass
|
||||
- 2026-10-21 | 9.5 | Created `tests/integration/e2e-playable.test.js` covering full playable flow (register → login → create hero → stats → hunt → level → equip → shop → expedition → leaderboard → logout → login → persistence); fixed `handleListInventory` bug (parameter `_res` vs `res` in `src/routes/inventory.js`); 176/176 tests pass
|
||||
- 2026-10-21 | 9.4 | Mobile-responsive pass: game.html 3-col grid (stats | action | combat), mobile bottom nav (⚔️ Hunt / 🏕️ Trip / 🎒 Items), desktop tabs hidden < 700px, game-container padding for nav clearance, mobile stat-row stacking, inventory container stacks < 600px; 175/175 tests pass
|
||||
|
||||
@@ -129,12 +129,12 @@ function startExpedition(characterId, defId) {
|
||||
const endsAt = startedAt + (def.duration_sec * 1000);
|
||||
|
||||
// Execute inside a transaction.
|
||||
const doStart = db.transaction((nc, ne, eat, cid, defid, sat) => {
|
||||
const doStart = db.transaction((cid, ne, eat, defid, sat, endAt) => {
|
||||
s.updateEnergy.run(ne, eat, cid);
|
||||
s.insertExpedition.run(cid, defid, sat, eat);
|
||||
s.insertExpedition.run(cid, defid, sat, endAt);
|
||||
});
|
||||
|
||||
doStart(newEnergy, def.energy_cost, endsAt, characterId, defId, startedAt);
|
||||
doStart(characterId, newEnergy, updatedAtMs, defId, startedAt, endsAt);
|
||||
|
||||
// Read back the just-inserted expedition (latest active for this character).
|
||||
const inserted = s.activeExpedition.get(characterId);
|
||||
|
||||
@@ -404,4 +404,50 @@ describe('expeditions', () => {
|
||||
assert.equal(res.status, 401);
|
||||
assert.equal(body.ok, false);
|
||||
});
|
||||
|
||||
// ─── 10.1: expedition start must deduct energy correctly ──────────────
|
||||
|
||||
it('10.1: starts expedition → DB energy = energy - cost, timestamp NOT in future', async () => {
|
||||
const { cookie, characterId } = await setupHero({ energy: 100, gold: 500 });
|
||||
|
||||
const res = await fetch(`${baseUrl}/api/expeditions/start`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...authHeaders(cookie) },
|
||||
body: JSON.stringify({ defId: 1 }), // scout, cost=10
|
||||
});
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json();
|
||||
assert.equal(body.ok, true);
|
||||
|
||||
// Assert DB state directly — this is the core of 10.1.
|
||||
const db = require('../../src/db').getDb();
|
||||
const ch = db.prepare('SELECT energy, energy_updated_at FROM characters WHERE id = ?').get(characterId);
|
||||
assert.equal(ch.energy, 90, 'DB energy must be 100 - 10 = 90');
|
||||
assert.ok(ch.energy_updated_at <= Date.now(), 'energy_updated_at must NOT be in the future');
|
||||
|
||||
// Assert GET /api/character returns the same energy.
|
||||
const charRes = await fetch(`${baseUrl}/api/character`, {
|
||||
headers: authHeaders(cookie),
|
||||
});
|
||||
assert.equal(charRes.status, 200);
|
||||
const charBody = await charRes.json();
|
||||
assert.equal(charBody.data.energy.current, 90);
|
||||
});
|
||||
|
||||
it('10.1: starting at energy 55 → DB energy = 45', async () => {
|
||||
const { cookie, characterId } = await setupHero({ energy: 55, gold: 500 });
|
||||
|
||||
const res = await fetch(`${baseUrl}/api/expeditions/start`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...authHeaders(cookie) },
|
||||
body: JSON.stringify({ defId: 1 }), // scout, cost=10
|
||||
});
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json();
|
||||
assert.equal(body.ok, true);
|
||||
|
||||
const db = require('../../src/db').getDb();
|
||||
const ch = db.prepare('SELECT energy FROM characters WHERE id = ?').get(characterId);
|
||||
assert.equal(ch.energy, 45, 'DB energy must be 55 - 10 = 45');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user