Skip to content

Automate CI coverage, chaos and survey workflow tests - #59

Closed
denniswu28 wants to merge 16 commits into
ram/clean-upfrom
ci/test-automation
Closed

denniswu28 wants to merge 16 commits into
ram/clean-upfrom
ci/test-automation

Conversation

@denniswu28

@denniswu28 denniswu28 commented Oct 5, 2026 •

Copy link
Copy Markdown

Summary

Sets up CI that tests the whole repository with coverage, and adds fault-injection ("chaos monkey") and survey-workflow test suites. Stacked on ram/clean-up; retarget to dev/main once that merges.

CI (.github/workflows/ci.yml)

  • Runs on pull requests into any branch (so stacked branches like this one get CI), pushes to main/dev, and manual dispatch.
  • One test matrix: Python 3.9–3.14 on Ubuntu 24.04, plus 3.9 and 3.14 on macOS. Every job installs sshd and runs the whole suite with the loopback SSH tests, which previously ran only 2 files in a separate job.
  • A coverage job combines coverage from all 8 jobs, writes the report to the job summary, uploads XML/HTML, uploads to Codecov (OIDC, no secret), and enforces a 90% floor in CI itself. Codecov statuses are informational.
  • distribution-and-docs additionally runs twine check --strict and smoke-tests both hm and hallmark.
  • Actions updated to current majors; ruff exclusions moved so ruff check . works.

Nightly (.github/workflows/nightly.yml): chaos tests with fresh seeds and 2,000 random operations per seed (Linux 3.9/3.14, macOS 3.14), and live downloads of ~2 MB from DESI DR1 and EHT/CyVerse. It also runs on PRs that touch these tests, since schedules only start from main.

Tests

  • test/http_server.py: a real loopback HTTP server (python/Apache/CyVerse listings) with scripted faults: resets, short and unannounced-length bodies, 404/429/5xx, stalls, slow drips, bit flips, redirects.
  • test/test_survey.py (-m survey): one scenario per doc/usecase.rst use case, plus DESI-like and EHT-like releases, release updates, silent corruption, Git and snapshot clones. No request leaves the machine.
  • test/test_chaos_*.py (-m chaos): network faults, full disk and I/O errors, SIGKILL at chosen calls, and seeded random operation sequences checked against repository invariants.
  • test/test_survey_live.py (-m survey_live, opt-in): real DESI DR1 and EHT downloads, size-capped, and skipped when an archive is down.

Bugs found (strict expected failures; each fix goes on its own branch)

Also fixes a Python 3.9-only test failure caused by Click 8.1's CliRunner (it never ran in CI on ram/clean-up).

Needs an org admin

Install the Codecov GitHub App on lanisrc, then rerun the coverage job. Until then the upload step logs a warning and CI is gated by the in-workflow floor.

Testing

  • Local mirror of CI: full suite with SSH tests on Python 3.9, 3.13 and 3.14 → 701–702 passed, 7 strict expected failures, only opt-in skips; combined branch coverage 91.9%.
  • actionlint on both workflows; codecov.yml validated; twine check/Sphinx -W pass.
  • Network chaos module run 40× on Python 3.9 with no failures; both monkeys run with 20 seeds × 300 steps.
  • Also confirmed hm add . from a subfolder drops staged files outside it #53 (hm add . from a subfolder drops staged rows); not fixed here.
  • Live tests: DESI DR1 passes; CyVerse was degraded at the time of testing (slow listings, 503s), which the tests now skip instead of failing.
  • GitHub Actions on this PR: all 15 checks green. Each of the 8 test jobs ran the whole suite with SSH enabled (701–702 passed, 7 strict expected failures, no SSH skips); combined coverage on CI is 91.93% (floor 90, unchanged since floor(total) − 1 = 90). Nightly also ran through its path filter: chaos (3 seeds × 300 steps per lane) passed on Linux 3.9/3.14 and macOS 3.14; live DESI and one EHT test passed, one EHT test skipped because CyVerse timed out.
  • The Codecov step obtained an OIDC token, but downloading Codecov's CLI failed with a TLS handshake error on cli.codecov.io, so nothing was uploaded; the step is non-blocking. Uploads also need the Codecov app installed (above).

🤖 Generated with Claude Code

denniswu28 and others added 16 commits October 4, 2026 20:19
Configure coverage.py to measure the hallmark package with branch
coverage and repository-relative paths, so data from separate CI jobs on
Linux and macOS can be combined. Add pytest-cov and pytest-timeout to the
dev dependency group and document `pytest --cov` for local runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the separate build and ssh-transport jobs with one test matrix
that installs the OpenSSH server and runs the complete suite with
HALLMARK_RUN_SSH_TESTS=1 on Python 3.9-3.14 (Linux) and 3.9/3.14 (macOS).
Run CI for pull requests into any branch so stacked feature branches are
tested, lint once in its own job, update the actions to their current
majors, and check the built distributions with twine.

Move the ruff exclusions to the top-level table so `ruff check .` no
longer parses the shell cells in the demo notebooks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Click 8.1, the newest release for Python 3.9, makes CliRunner read
exhausted input as an empty line instead of raising EOFError, so an empty
answer to the clone download prompt declines rather than aborts there.
Real terminals abort on end of input with both versions; only the test
harness differs. Expect the empty-line behaviour when Click is older than
8.2 so the Python 3.9 CI job tests the same outcomes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each test job writes coverage data under its OS and Python version and
uploads it. A coverage job combines them, writes the report to the job
summary, uploads XML and HTML reports, and fails below 90%. Some branches
only run on one Python version or operating system, so no single job
sees the total. The floor lives in the workflow rather than in
pyproject.toml because pytest-cov would apply it to each partial run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upload the combined coverage.xml from the coverage job, verified with
OIDC so no repository secret is needed; pull requests from forks upload
tokenless. The upload cannot fail CI, and codecov.yml marks Codecov's
statuses informational, so the floor in the workflow remains the only
gate. Add a coverage badge and describe the coverage workflow in the
contribution guide.

The Codecov GitHub App must be installed on the lanisrc organization for
uploads to be accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the disposable OpenSSH server fixture from test_ssh_integration.py
into conftest.py, unchanged, so survey workflow and fault-injection tests
can serve data over the same loopback server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a real threaded HTTP server for tests that serves a directory on
127.0.0.1 with python, Apache or CyVerse style listings, records each
request, and applies scripted per-path faults: status codes, resets,
short or unannounced-length bodies, flipped bytes, stalls, slow drips,
redirects and pages without listings. Unlike MockServer, it exercises
the real Requests and urllib3 code paths.

Add deterministic generators for scaled-down DESI healpix, EHT UVFITS,
simulation export and telescope layouts, a CliRunner helper, and
fixtures that start servers and refuse non-loopback HTTP requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add end-to-end tests for each workflow in doc/usecase.rst: telescope
ingest and update, a bare simulation catalog with explicit downloads
over SSH, isolated worktrees, nightly programmatic ingest, in-memory
ParaFrame selection and private lab data from a named SSH remote.

Also test DESI-like and EHT-like releases over loopback HTTP: published
sha256 checksums and listing sizes are recorded, URL-pattern adds read
no payloads, downloads honour --filter, --fmt and --output, a data
release update is rescanned and refreshed, a silently corrupted file is
rejected while the rest succeed, and Git catalogs and published
snapshots clone with a download prompt. A loopback guard keeps every
request on this machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Serve releases from the loopback HTTP server while it resets
connections, truncates bodies, returns 404/429/5xx, stalls, flips bytes
and redirects. Every faulted file must fail alone, leaving no partial
destination or temporary file and no URL in the error, while the other
files arrive intact; downloads must complete on a rerun; and a failed
rescan, including an unreadable SSH folder, must leave the committed
catalog and index untouched.

chaos.py adds a snapshot of a repository's tracked state and an
invariant check for corruption (unopenable repositories, objects that
do not match their checksums, committed files without objects, git
fsck errors) and leftover temporary files.

A body truncated without Content-Length is still accepted when the
catalog records the file's size but no checksum; that test is a strict
expected failure until the download size is verified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fail the Nth file replacement or write with ENOSPC or EIO while staging,
committing and downloading, and make an output folder read-only. A
failed commit must keep the branch pointer, leave no corrupt object and
succeed when retried; a download that runs out of space or permission
must fail only the affected file.

Dothm.save_state writes config.yml, meta.yml and data.tsv one at a time,
so a failure after the first write leaves state files from different
generations; those cases are strict expected failures until the writes
are made atomic together.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run hm in a child process that sends itself SIGKILL at a chosen call
while staging, committing, checking out, adding a worktree or
downloading. The parent requires the kill to have happened, then checks
that the repository is not corrupt and that rerunning the command
finishes the job. An interrupted download must never leave a partial
destination and must keep its unfinished data in a separate file.

Two known recovery bugs are strict expected failures: a checkout killed
after moving files to its backup folder cannot be rerun, and
add_worktree skips restoring files when its destination already has .hm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A seeded "monkey" edits, stages, commits and switches branches while
injecting full-disk and I/O errors, and a second one republishes,
rescans and downloads a release while the server misbehaves. Instead of
modelling every rule, they check what must always hold: operations
succeed or raise a documented error; a failed operation changes neither
the repository nor the worktree; checked-out files match their
catalogued checksums; nothing is ever corrupt; and downloaded files are
always versions the server published. A failure prints its seed, recent
operations and the command that reproduces it. HALLMARK_CHAOS_SEED and
HALLMARK_CHAOS_STEPS widen the search.

The monkey found that checkout before the first commit raises a raw
GitCommandError, recorded as a strict expected failure. Known bugs are
listed where the monkey steers around them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With HALLMARK_RUN_LIVE_TESTS=1, follow the public-collection examples
in doc/private_data.rst: catalog DESI DR1 healpix files and verify the
downloads against the release's sha256 manifest, and catalog and
download EHT M87 UVFITS files from CyVerse, including through a URL
pattern. Every plan is checked before approval so a run downloads a few
megabytes at most. Tests skip, rather than fail, when an archive is
down or times out.

CyVerse lists sizes such as "554K", which Hallmark does not record, so
CyVerse downloads are limited to single, exactly named files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every night, run the chaos tests on Linux (Python 3.9 and 3.14) and
macOS with fresh seeds and 2000 random operations per seed, keeping the
failed repositories as artifacts, and run the live survey tests.
Scheduled and manual runs start only from the default branch, so pull
requests that change these tests or the workflow run it too, with fewer
operations. Document how to run and extend these tests and how to
record the bugs they find.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Name the issue tracking each bug in its strict expected-failure reason:
#54 truncated downloads, #55 state-file atomicity, #56 interrupted
checkouts, #57 interrupted worktree creation and #58 checkout before the
first commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CyVerse listings can take longer than Hallmark's read timeout even after
the availability probe succeeds. Treat timeouts and server errors during
the catalog scan, as during downloads, as the archive being unavailable,
so the nightly job reports Hallmark failures rather than archive load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

denniswu28 added a commit that referenced this pull request Oct 10, 2026
The first CI run on this branch combined 90.45% branch coverage across
all eight test jobs, with 27-29 tests failing in each. A floor of 90
would leave under half a point of margin, so use #59's rule of one
point below the whole-number total: 89.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
denniswu28 added a commit that referenced this pull request Oct 10, 2026
Both CI runs on #59 failed to download the Codecov CLI from
cli.codecov.io, and fail_ci_if_error: false hid that, so Codecov has
never received a report. Install the CLI from PyPI instead, and let an
upload error fail the step but not the job, so a rejected or failed
upload is visible while the in-workflow floor stays the only gate.
denniswu28 added a commit that referenced this pull request Oct 10, 2026
The first CI run on this branch combined 90.45% branch coverage across
all eight test jobs, with 27-29 tests failing in each. A floor of 90
would leave under half a point of margin, so use #59's rule of one
point below the whole-number total: 89.
@denniswu28

Copy link
Copy Markdown
Author

Retiring this draft. Work has moved from ram/clean-up to codex/function-atomic-replay-20260928, and #61 ports the CI plumbing from here onto that branch. The chaos and survey suites and nightly.yml weren't ported; they stay on ci/test-automation if we want them later.

@denniswu28 denniswu28 closed this Oct 10, 2026
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.

2 participants