Skip to content

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

Terminal window
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/coverage

The 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 areaWhat it protects
Pool and scoringSquare ownership, digit mapping, malformed scores
SchedulerKickoff timing, state reuse, publication, milestone failure recovery without duplicate game emails
StatePrivate container and conditional writes
Winnings and emailDecimal payouts, recipients, rendering, histories, milestone timing, standings order, zero-winnings filtering
SecurityEscaping, sanitized logs, dry-run behavior
SBOMRoot 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:

Terminal window
. .venv/bin/activate
func start

Start 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:

Terminal window
.venv/bin/python scripts/preview_emails.py

Open 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:

Terminal window
.venv/bin/python -m pytest tests -q
.venv/bin/python -m pytest tests/test_season_email.py -q

Preview the public site

Terminal window
python3 ci/preview.py

Open 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:

Terminal window
SENTRY_DSN='<test project DSN>' SENTRY_ENVIRONMENT=development node ci/swa/configure-sentry.cjs

To disable reporting locally:

Terminal window
SENTRY_DSN='' SENTRY_ENVIRONMENT=development node ci/swa/configure-sentry.cjs

Reload 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

Terminal window
npm ci --prefix ci/biome
ci/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 -ii

For Python lock updates, use the .in files and the dependency maintenance workflow. Do not hand-edit generated hashes.

Preview the documentation

Terminal window
npm ci --prefix ci/docs
npm run dev --prefix ci/docs

Use Node.js 22.12 or newer. Open http://127.0.0.1:4321. Check the production build as CI does:

Terminal window
npm run build --prefix ci/docs
python3 ci/docs/check_site.py docs-site

This 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):

Terminal window
npm ci --prefix ci/playwright
ci/playwright/node_modules/.bin/playwright install --with-deps chromium
npm test --prefix ci/playwright

For interactive debugging or to inspect the latest report:

Terminal window
npm run test:ui --prefix ci/playwright
npm run report --prefix ci/playwright

Playwright 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.