Skip to content

refactor: extract queue state machine into testable JS module #28

@samueljklee

Description

@samueljklee

Context

The message queue feature (merged in #22) has all its logic inlined in index.html (~6500 lines). The queue state machine — tryDrainQueue, startCountdown, cancelCountdown, pushToQueue, removeFromQueue, prependToQueue, drain state transitions, and the activeDelegatesRef counting — is tightly coupled to Preact hooks and refs, making it impossible to unit test the logic independently.

The current tests (94 of them) are static string-matching against the HTML file. They verify code patterns exist but can't test behavior — they can't confirm the countdown actually fires, that FIFO ordering is preserved, that the race condition retry works, or that session switching correctly preserves/restores queue state.

Proposal

Extract the queue state machine into a separate JS module (e.g. queue.js) that can be imported by index.html and independently unit tested.

What to extract

  • Queue data operations: pushToQueue, removeFromQueue, prependToQueue, clearSessionQueue, getQueue — pure functions operating on a Map
  • Drain state machine: tryDrainQueue, startCountdown, cancelCountdown, resumeQueue — the FSM transitions between idle → countdown → idle and paused → idle
  • Drain guards: executingRef check, activeDelegatesRef check, activeKeyRef session guard, double-start prevention

What stays in index.html

  • Preact component rendering (QueuePanel, InputArea modifications)
  • SSE event handler wiring (calling the extracted functions)
  • React state (queueVersion, countdownRemaining, executing)

Testing approach

Once extracted, use a lightweight test runner (vitest or plain Node.js assert) to test:

  • FIFO ordering: push A, B, C → drain pops A first
  • Drain guard: tryDrainQueue bails when executing=true
  • Countdown lifecycle: start → tick → tick → callback fires
  • Pause/resume: stop pauses, resume restarts
  • Session isolation: queue for session A is independent of session B
  • Re-queue on failure: prependToQueue puts item back at front
  • Double-start prevention: tryDrainQueue with drainState=countdown is a no-op

Non-goals

  • No build step / bundler — the module should work via <script type="module"> or a simple import pattern compatible with the current CDN-served architecture
  • No framework change — Preact + htm stays as-is

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesttestingTests and test infrastructure

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions