Skip to content

Tests: every server module in one layer; one serve helper and one config - #48

Merged
Bobowski merged 1 commit into
mainfrom
tests-layers-fixtures
Nov 2, 2026
Merged

Bobowski merged 1 commit into
mainfrom
tests-layers-fixtures

Conversation

@Bobowski

@Bobowski Bobowski commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Intent

Fix two test defects and share the test fixtures. Source: the user asked for an audit of Yard and then for every finding to be done. This is the first pull request of that series.

Found

  • tests/test_layers.py listed binds as a service, but app/server/binds.py is gone. sites was in no layer, and sources and detect/ were in no layer. Thus an adapter could import sites and no test failed.
  • Four TestClient fixtures in tests/server/test_journal.py copied the env setup of tests/conftest.py but did not set YARD_JOURNAL_SOCKET. On a Linux runner, journal.send in those tests wrote to /run/systemd/journal/socket. test_doctor_flags_a_missing_journal built a Config with the same gap.
  • Ten tests built a Config(...) by hand. Four Podman fakes in the doctor and status tests did the same thing in four ways.
  • Some tests repeated other tests: two "a yard self-deploy is pending" tests, a parse_port test in test_apps.py behind a wrapper, three "the removed command or route is gone" tests, and an ssh deduction test that repeats test_remote_repo_spellings.

Changed

  • tests/test_layers.py: the sets name every server module. The new test test_every_server_module_has_one_layer fails when a module is in no layer or in two layers. A module in detect/ counts as detect.
  • tests/conftest.py: one serve(tmp_path, monkeypatch, journalctl=..., journal_dir=...) context manager starts the production bootstrap. It always sets the journal socket into tmp_path. The yard fixture and the four journal fixtures use it.
  • tests/server/conftest.py: make_config(tmp_path, **overrides) and make_yard(tmp_path, **overrides) take Config field names (skip_deploy=False, not skip=False). FakePodman(answers, default) answers by command prefix, and dead_podman() refuses every command.
  • I removed the duplicate tests and moved test_parse_port to test_podman.py.

I kept the specific Podman fakes in test_apps.py. They model a deploy container that changes state, and a table of prefixes does not fit that.

Not done

  • A shared FakeApi for tests/cli/test_main.py. It comes with the CLI HTTP change in a later pull request, because those tests patch cli._api_try.

Proof

$ just check
...
500 passed in 60.87s

test_every_server_module_has_one_layer passes. With the old sets it fails on sites and sources.

Questions

None

🤖 Generated with Claude Code

…fig for every fixture

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Bobowski
Bobowski merged commit 3cb8f22 into main Nov 2, 2026
1 check passed
@Bobowski
Bobowski deleted the tests-layers-fixtures branch November 2, 2026 22:53
Bobowski added a commit that referenced this pull request Nov 2, 2026
…is a dataclass (#54)

## Intent

Write each rule of a deploy once: the image of a plan, the Dockerfile
path, the compose files, and the status of an exit code. Source: the
user, in the Yard audit. It replaces #49, which GitHub closed when the
base branch of #49 was deleted. #48 is merged.

## Found

- `apps._ready_repo`, `apps._ready_source`, and `compose.saved_project`
each built the image and the Dockerfile path with their own copy of the
rules: `split('@')[0]@<digest>` two times, `compose:<rev[7:19]>` two
times, and the `yard:` Dockerfile rule two times. `compose.project` took
eight arguments, and the callers worked out the checkout themselves.
- `apps._apply` and `status.deploy_record` each turned an exit code into
a status and an error text: 0 ok, 3 reverted, any other code failed.
- `status.deploy_record` returned a plain dict. The API and `apps._join`
read it by string keys.
- `test_check_repo_debounces` failed at random on `main`. Since #47,
each check runs `podman ps`. That call can take longer than the 50 ms
window of the test.

## Changed

- `compose.project(config, app, plan)` takes the plan (`Deployed`). It
picks the checkout (the clone for a repo app, the app folder otherwise),
the image (`compose.plan_image`), the Dockerfile, and the files.
`Project` carries `image`. `saved_project` only checks that the files
are still there, then calls `project`.
- `status.deploy_status(code)` and `status.deploy_error(code, previous)`
hold the exit-code rule. `_apply` and `deploy_record` use them.
- `status.Record` is the deploy record. `Row.deploy` and `Page.deploy`
hold a `Record`. The JSON shapes do not change: `asdict` writes the same
keys, and the deploy route still adds `port` on `ok`.
- The debounce test uses a 0.5 s window.

I did not add a separate `Source` type. When `compose.project` owns the
rules, the string checks that remain are each in one place.

## Not done

Nothing

## Proof

```
$ just check
...
500 passed in 60.45s
```

`tests/server/test_compose.py` checks the image and the Dockerfile of
each kind through the new `project`. `test_check_repo_debounces` passed
5 runs in a row.

## Questions

None

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant