Skip to content

fix(alerts): cap offline pushes per worker — episode damping lost to … #844

fix(alerts): cap offline pushes per worker — episode damping lost to …

fix(alerts): cap offline pushes per worker — episode damping lost to … #844

Workflow file for this run

name: Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
# Tags are needed by the compose-pin drift test, which compares the
# pinned image tag against the newest RELEASED version. checkout does
# not fetch tags by default, so without this the test finds none and
# skips — decorative in the one place it is meant to run.
fetch-depth: 0
fetch-tags: true
- uses: actions/setup-python@v7
with:
python-version: "3.14"
- uses: actions/setup-node@v6
with:
node-version: "22"
# Nothing here parsed the JavaScript, and pytest cannot: appending a
# syntax error to app.js leaves the whole suite green. What ships in that
# state is a completely dead dashboard — app.js is one IIFE assigned to
# CP, so a parse failure anywhere means CP is never defined and every
# data-action button silently does nothing. No error page, no failed
# request, just a UI where nothing works.
- name: Parse the JavaScript
run: |
for f in app/static/js/*.js; do node --check "$f"; done
# Behaviour pytest cannot reach. This one needs no browser: it runs the
# real currency functions against controlled exchange-rate state, and
# exists because formatCurrency once labelled an UNCONVERTED USD amount
# with the viewer's currency symbol — $24.90 rendered as "£24.90".
- name: Currency formatting behaviour
run: node scripts/currency_check.mjs
# Also browser-free. A string test can prove the Settings catch is no
# longer empty; only running it proves BOTH panels get written and that
# what lands in them says why (CashPilot-cn3).
- name: Settings failure path
run: node scripts/settings_failure_check.mjs
# Also browser-free. The claim modal has to give THREE different answers
# for what looks like one empty result; only running it proves which.
- name: Claim modal honesty
run: node scripts/claim_modal_check.mjs
# Also behaviour pytest cannot reach, and for the same reason: these
# renderers live in a <script> block inside a Jinja template, so a Python
# assertion could only prove the source mentions a variable — never what
# colour a given worker actually produces.
- name: Fleet staleness behaviour
run: node scripts/fleet_staleness_check.mjs
# The Disk and GPU columns. Worth running rather than grepping for the
# same reason as the rest: the first draft iterated `svc.instances`, which
# is a COUNT, and `for...of` over a number throws — a string assertion
# would have been perfectly happy while the services table went blank.
- name: Host resource columns
run: node scripts/host_resources_check.mjs
# The four payout states. The regression this guards is "internal" (there
# is no address, by design) rendering like "not set" (money may be going
# nowhere). Both are a cell with no address in it; only the output tells
# them apart, so only running the renderer can check it.
- name: Payout destination states
run: node scripts/payout_registry_check.mjs
# The only check here that needs a real browser, because it measures a
# property of the RENDERED page. A button's label colour is decided by the
# cascade, so `.modal-body a` (0,1,1) silently outranked `.btn-success`
# (0,1,0) and painted the claim modal's button cyan-on-green at 2.28:1 --
# invisible to every other test in this repo, all of which read values
# rather than looking at the result. Asserting the stylesheet CONTAINS
# ":not(.btn)" would prove a rule was typed, not that a button can be read.
# ubuntu-latest ships Chrome, and the script exits 2 rather than 0 when no
# browser is found, so a skipped run can never read as a pass.
- name: Legibility (contrast + link styling, real browser)
run: ./scripts/legibility_check.sh
# From the LOCKFILE, which is what Docker ships (`uv sync --frozen` in the
# Dockerfile) and what release.yml verifies against. Installing from the
# unpinned requirements.txt meant the suite ran against a resolution
# nobody else had: locally fastapi 0.136.1 / starlette 1.0.1, here
# 0.141.1 / 1.3.1. On the newer one `include_router` stops adding routes
# to `app.routes`, so a route sweep silently covered 65 of 76 — a real
# behaviour difference that only ever appeared on CI (CashPilot-de1).
- name: Install uv
run: pip install uv --quiet
- name: Install dependencies
run: uv sync --frozen --extra dev
- name: Run tests
run: uv run pytest tests/ -v --tb=short
- name: Run tests with coverage
run: uv run pytest tests/ --cov=app --cov-report=term-missing --cov-report=xml --cov-fail-under=90
- name: Upload coverage reports
uses: codecov/codecov-action@v7
with:
file: ./coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
continue-on-error: true