Skip to content

test: Engine.snapshot() has no unit tests, and it is the contract every front-end depends on #57

Description

@LeyckerS

Context

Engine.snapshot() is the single interface between the engine and every front-end. The GUI polls it several times a second and renders whatever it returns: the stage name, the counters, the speed, the ETA, the elapsed clock. moon_bridge.py serves it over the loopback API, and render_gui.py and integration_http.py both depend on its shape.

It has no unit tests. Everything it produces is currently verified only by running a real download and looking at the window.

That gap is not theoretical. #58 is a bug that lives in snapshot() — the elapsed clock never stops after a run — and nothing caught it, because catching it requires nothing more than calling the method twice and comparing two numbers.

tests/test_no_chrome.py already proves the engine can be driven headlessly with Chrome and the network stubbed at the moon_extract boundary, so the hard part of the setup is already solved and working.

What to do

Add tests/test_snapshot.py covering the state machine that snapshot() exposes. The interesting assertions are about transitions, not single values:

  1. Idle before anything runs. A fresh Engine() reports stage == "idle", elapsed_s == 0, and zeroed counters. It must not raise.
  2. Stage progression. With the internal counters set to represent extraction in progress, then downloading, then everything done, stage reports extracting, downloading, done respectively. The branch is at moon_engine.py:586-593.
  3. The log cursor. snapshot(cursor=N) returns only lines after N, and the returned cursor advances. Log a few lines, read with the returned cursor, assert you get nothing the second time. This is the contract the GUI relies on to avoid gaps it cannot detect.
  4. ETA and speed are safe when empty. With no byte samples, speed_mbs and eta_s are 0 and nothing divides by zero.
  5. Counters reset between runs. start() must clear the previous run's totals.

Do not restructure snapshot() to make it testable. It is already testable — it is a method on an object with no I/O. If you find you cannot test something without changing production code, say so in the PR rather than changing it: that is useful information about the design.

Acceptance criteria

  • pytest tests/ -q passes with the new file, no browser and no network
  • At least the five behaviours above are covered
  • Each test asserts one behaviour and its name says which
  • No production code changed
  • Runtime stays well under a second — no sleep longer than needed

Notes for the contributor

No Windows needed. Read tests/conftest.py and tests/test_no_chrome.py first: they show how the engine is driven in isolation. tests/test_proxy_pool.py shows the preferred style for small focused tests.

If #58 is still open when you start, do not fix it here — write the test that would have caught it and let it fail, or coordinate on that issue. Two PRs touching the same lines is the one thing worth avoiding.

Comment here with which behaviours you are taking if you would rather split this.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions