feat(api): world/system/crew snapshots expose the active + pending task queue (#44)
This commit is contained in:
+51
-4
@@ -239,7 +239,7 @@ describe('explorePlanet', () => {
|
||||
describe('getCrewState', () => {
|
||||
it('seeds a starting crew of 2–3 members derived from the save seed', async () => {
|
||||
const { db } = await freshFile()
|
||||
const crew = await getCrewState(db) // ensureSave runs inside, so this also seeds the crew
|
||||
const crew = (await getCrewState(db)).crew // ensureSave runs inside, so this also seeds the crew (the roster moved under .crew — JC-7)
|
||||
|
||||
expect(crew.length).toBeGreaterThanOrEqual(2)
|
||||
expect(crew.length).toBeLessThanOrEqual(3)
|
||||
@@ -285,7 +285,7 @@ describe('explorePlanet crew finds', () => {
|
||||
await db.db.insert(schema.planets).values({ systemId: sys.id, parentId: null, name: `P${i}`, type: 'ROCKY', hazard: false })
|
||||
}
|
||||
|
||||
const before = await getCrewState(db) // empty — this save was hand-built, no starting crew seeded
|
||||
const before = (await getCrewState(db)).crew // empty — this save was hand-built, no starting crew seeded
|
||||
const result = await explorePlanet(db, crewPlanetId)
|
||||
|
||||
expect(result.crewFound).not.toBeNull()
|
||||
@@ -297,13 +297,13 @@ describe('explorePlanet crew finds', () => {
|
||||
expect([result.crewFound!.traitGood, result.crewFound!.traitBad]).toEqual([expectedMember.traitGood, expectedMember.traitBad])
|
||||
expect(result.crewFound!.origin).toBe(`${planetName}, Test System`)
|
||||
|
||||
const after = await getCrewState(db)
|
||||
const after = (await getCrewState(db)).crew
|
||||
expect(after.length).toBe(before.length + 1) // exactly one join
|
||||
expect(after.find((c) => c.id === result.crewFound!.id)).toMatchObject({ name: expectedMember.name, origin: `${planetName}, Test System` })
|
||||
|
||||
// The body is already explored → a second scan is rejected and no duplicate joins.
|
||||
await expect(explorePlanet(db, crewPlanetId)).rejects.toMatchObject({ status: 409 })
|
||||
expect((await getCrewState(db)).length).toBe(before.length + 1)
|
||||
expect((await getCrewState(db)).crew.length).toBe(before.length + 1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -540,6 +540,53 @@ describe('runTurn (tick on the DB)', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// --- The GET snapshots' task queue (Task 6) ---------------------------------------------
|
||||
//
|
||||
// Every GET snapshot (world / system / crew) carries the active + pending queue: ACTIVE rows
|
||||
// first, then PENDING, each block ordered by (queuedAtTurn, id); DONE rows never appear. A
|
||||
// pure read — `remaining` is the stored value, never recomputed; crew names resolve from the
|
||||
// roster by id.
|
||||
describe('snapshot task queue (world / system / crew)', () => {
|
||||
// buildWorldFixture (4a): a save (seed '42') + Origin (s0, current) / Outpost (s1) +
|
||||
// Alder (p0) / Basalt (p1) + the route pair — no crew, no tasks; this block adds its own.
|
||||
|
||||
it('empty queue → every GET snapshot carries tasks: []', async () => {
|
||||
const { db } = await freshFile()
|
||||
await buildWorldFixture(db)
|
||||
expect((await getWorldState(db)).tasks).toEqual([])
|
||||
expect((await getSystemState(db)).tasks).toEqual([])
|
||||
const crew = await getCrewState(db)
|
||||
expect(crew.tasks).toEqual([])
|
||||
expect(crew.crew).toEqual([]) // the new wrapper: the roster moves under .crew (JC-7)
|
||||
})
|
||||
|
||||
it('one ACTIVE + one PENDING row → exactly those two, ACTIVE first, in all three snapshots', async () => {
|
||||
const { db } = await freshFile()
|
||||
const f = await buildWorldFixture(db)
|
||||
const [c1, c2, c3] = await addCrew(db, 3)
|
||||
const jumpId = await insertTaskRow(db, { type: 'JUMP', payload: { systemId: f.sys1, hops: 1 }, leadCrewId: c1, helpers: [], duration: 2, remaining: 1, status: 'ACTIVE', queuedAtTurn: 3 })
|
||||
const exploreId = await insertTaskRow(db, { type: 'EXPLORE', payload: { planetId: f.p1 }, leadCrewId: c2, helpers: [c3], duration: 4, remaining: 4, status: 'PENDING', queuedAtTurn: 3 })
|
||||
const expected = [
|
||||
{ id: jumpId, type: 'JUMP', targetLabel: 'Outpost', leadName: 'CREW 1', helperNames: [], remaining: 1 },
|
||||
{ id: exploreId, type: 'EXPLORE', targetLabel: 'Basalt', leadName: 'CREW 2', helperNames: ['CREW 3'], remaining: 4 },
|
||||
]
|
||||
expect((await getWorldState(db)).tasks).toEqual(expected)
|
||||
expect((await getSystemState(db)).tasks).toEqual(expected)
|
||||
expect((await getCrewState(db)).tasks).toEqual(expected)
|
||||
})
|
||||
|
||||
it('DONE rows never appear — even when they sort first by (queuedAtTurn, id)', async () => {
|
||||
const { db } = await freshFile()
|
||||
const f = await buildWorldFixture(db)
|
||||
const [c1] = await addCrew(db, 1)
|
||||
await insertTaskRow(db, { type: 'JUMP', payload: { systemId: f.sys1, hops: 1 }, leadCrewId: c1, helpers: [], duration: 1, remaining: 0, status: 'DONE', queuedAtTurn: 1 }) // oldest, lowest id in the queue
|
||||
await insertTaskRow(db, { type: 'EXPLORE', payload: { planetId: f.p0 }, leadCrewId: c1, helpers: [], duration: 4, remaining: 4, status: 'PENDING', queuedAtTurn: 2 })
|
||||
const snap = await getWorldState(db)
|
||||
expect(snap.tasks).toHaveLength(1)
|
||||
expect(snap.tasks[0].type).toBe('EXPLORE') // the DONE row is filtered out, not reordered to the back
|
||||
})
|
||||
})
|
||||
|
||||
describe('backfill (old saves)', () => {
|
||||
// Planets/moons of one system in insertion order, nested by parent — ids are excluded so a
|
||||
// regenerated set compares equal to the original even though autoincrement moved on.
|
||||
|
||||
Reference in New Issue
Block a user