Local development
Use Python 3.11 for parity with the Function deployment and dependency locks, and Node 22 for CI web tools. Run commands from the repository root.
Python tests
python3.11 -m venv .venv.venv/bin/python -m pip install --require-hashes -r requirements-dev.txt.venv/bin/python -m pip check.venv/bin/python -m pytest tests -v --junitxml=reports/junit.xml \ --cov --cov-report=term-missing --cov-report=xml:reports/coverage.xml \ --cov-report=html:reports/coverageThe tests mock provider and delivery operations; they do not send real emails.
Use pytest to run the whole suite, including the SBOM tests. unittest discover
does not collect pytest-style tests.
Coverage measures app/ and function_app.py, excluding test files, using
.coveragerc. Open reports/coverage/index.html for the local HTML report.
The pipeline uses the same command and publishes the coverage percentage and
reports in GitLab. Coverage is informational; no minimum percentage is enforced.
| Test area | What it protects |
|---|---|
| Pool and scoring | Square ownership, digit mapping, malformed scores |
| Scheduler | Kickoff timing, state reuse, publication, milestone failure recovery without duplicate game emails |
| State | Private container and conditional writes |
| Winnings and email | Decimal payouts, recipients, rendering, histories, milestone timing, standings order, zero-winnings filtering |
| Security | Escaping, sanitized logs, dry-run behavior |
| SBOM | Root relationships, package identity, stale direct-dependency locks |
Run the Function host
Install Azure Functions Core Tools, create an untracked local.settings.json,
and activate the environment before starting the host:
. .venv/bin/activatefunc startStart with DRY_RUN=true in the host settings. It skips the scheduled workflow,
including reads and publishing. Use the email preview script
to render sample reports locally.
The entry point is a timer only. Local startup does not imply a /api/ping
endpoint. See configuration for settings and
Azure setup for deployment.
Preview emails
After installing the Python development dependencies, run:
.venv/bin/python scripts/preview_emails.pyOpen reports/email-previews/index.html in a browser. It links to HTML and
plain-text versions of the weekly, winner, halfway, and end-of-season emails.
The script uses sample scores with the configured pool roster and payout amounts;
it does not fetch live results, write delivery state, or send email.
Run the command again after template changes, then refresh the browser.
Generated previews stay in the ignored reports/ directory. Browser previews
show the template design; email clients may apply different rendering rules.
For a quick test run or a single test file:
.venv/bin/python -m pytest tests -q.venv/bin/python -m pytest tests/test_season_email.py -qPreview the public site
python3 ci/preview.pyOpen http://localhost:8765. This loopback-only server proxies the existing live
results feed so localhost does not require Blob CORS changes. It rewrites the feed
URL only in the served JavaScript and disables Sentry reporting for the preview;
files in public/ remain unchanged. Use --port 8080 to choose another port.
For synthetic data, see the website guide.
Test player switching, list/board views, keyboard scrolling, the About dialog,
and both wide and narrow layouts.
Local Sentry configuration
The preview server above disables reporting. When using a plain static server,
the checked-in configuration reports errors to the configured Sentry project with
environment development. To use a different project for a local preview:
SENTRY_DSN='<test project DSN>' SENTRY_ENVIRONMENT=development node ci/swa/configure-sentry.cjsTo disable reporting locally:
SENTRY_DSN='' SENTRY_ENVIRONMENT=development node ci/swa/configure-sentry.cjsReload the page after changing configuration. Restore the checked-in contents of
public/sentry-config.js after testing, preserving any intentional changes of
your own. See the delivery check to verify
an enabled configuration.
Run the CI tools
npm ci --prefix ci/biomeci/biome/node_modules/.bin/biome lint public --max-diagnostics=none
python3.11 -m venv .ci-tools.ci-tools/bin/python -m pip install --require-hashes -r ci/requirements.txt.ci-tools/bin/bandit -r app function_app.py ci/generate_sbom.py -ll -iiFor Python lock updates, use the .in files and
the dependency maintenance workflow.
Do not hand-edit generated hashes.
Preview the documentation
npm ci --prefix ci/docsnpm run dev --prefix ci/docsUse Node.js 22.12 or newer. Open http://127.0.0.1:4321. Check the production build as CI does:
npm run build --prefix ci/docspython3 ci/docs/check_site.py docs-siteThis writes docs-site/ and never writes into the Azure application directory
public/. See documentation maintenance for navigation,
diagrams, and publication details.
Browser tests with Playwright
Use Node.js 22 or newer and Python 3 (for the local static server):
npm ci --prefix ci/playwrightci/playwright/node_modules/.bin/playwright install --with-deps chromiumnpm test --prefix ci/playwrightFor interactive debugging or to inspect the latest report:
npm run test:ui --prefix ci/playwrightnpm run report --prefix ci/playwrightPlaywright starts and stops a server on 127.0.0.1:8766; keep that port free.
Each test runs in desktop and mobile Chromium with a fresh browser context.
The suite uses synthetic players and scores, fixes the browser clock, and blocks
external requests including Sentry and CDN scripts. It checks loading, winners,
selection persistence, board/list assignments, the Eastern Tuesday boundary,
request errors, mobile scrolling, and dialog keyboard behavior.
Reports are written to ignored reports/playwright/. Failed tests retain traces
and screenshots. CI uses one worker and no retries so a failure blocks deployment.
These tests do not verify live Azure connectivity, Sentry delivery, or the external
confetti script. Mobile tests emulate a Chromium viewport; they do not test Safari.