Skip to main content

Testing strategy

Choose evidence from the behavior changed, then run the narrowest Bazel target that proves it. Use the local-validation stack for integrated browser/API behavior and scripts/test-local.sh for repository-owned batteries. Do not bypass Bazel with direct Gradle, Maven, npm, or pnpm test commands.

Select targets before running them

Ask Bazel for the owning tests:

bazel query 'tests(//apps/microservices/merchant-api:all)'
bazel query 'tests(//apps/websites/portals/retail:all)'

Run exact labels when known:

bazel test //apps/microservices/merchant-api:core_test
bazel test //apps/websites/portals/retail:test
bazel test //apps/websites/portals/retail:lint

Use --build_tests_only when a wildcard can expand to build targets as well as tests:

bazel test --build_tests_only //apps/microservices/...
bazel test --build_tests_only //apps/websites/...

A manually tagged test is intentionally absent from normal broad lanes. Find it, read its prerequisites, and invoke its exact label only when the required service, device, credentials, or host is available:

bazel query 'attr("tags", "manual", tests(//apps/microservices/...))'

Do not interpret a skipped manual or external gate as a pass.

Repository test batteries

scripts/test-local.sh is the public local test runner. Every mode writes a Markdown report under .dev-logs/test-local/reports/ and per-step logs under .dev-logs/test-local/.

CommandPurpose
./scripts/test-local.sh doctorValidate Gateway Maven credentials and availability of Bazel, Docker, Python, and Google Cloud CLI.
./scripts/test-local.sh quickRun broad Bazel tests across scripts, libraries, specifications, services, websites, and static iOS/mobile lanes.
./scripts/test-local.sh contractsRun contract drift, local proxy, docs render, PCI redaction, and QA harness tests.
./scripts/test-local.sh localRun seed/boundary checks plus microservice and website component tests. It does not start the stack.
./scripts/test-local.sh no-heavyRun static/syntax/Python contract checks without Bazel, Docker Compose, Playwright, Android tooling, live databases, or Gateway Maven credentials.
./scripts/test-local.sh external-gatesRecord availability/results for device, Apple, real-provider, Cloudflare/Cloud Run, App Check, delivery, and store-console evidence.
./scripts/test-local.sh fullRun doctor, quick, contracts, local, and external-gates in sequence.
./scripts/test-local.sh statusShow local-validation stack status.

no-heavy is a constrained static battery, not proof that the repository compiles. external-gates is evidence collection; unavailable gates remain unavailable. For a one-command runtime boot and deep local smoke, use ./scripts/local-validation.sh full instead.

Local integrated behavior

Start the stack before an integrated browser/API smoke:

./scripts/local-validation.sh up
./scripts/local-validation.sh smoke all

Use a focused scope for a focused change:

./scripts/local-validation.sh smoke auth
./scripts/local-validation.sh smoke purchasing
./scripts/local-validation.sh smoke browser
./scripts/local-validation.sh smoke terminal-lifecycle

The smoke command owns its browser runner, seeded credentials, App Check token, and provider stubs. Do not replace it with a direct Playwright command as release evidence. See Local development and manual QA for startup, ports, and debugging.

Remote execution and host-specific work

Plain developer Bazel commands use the BuildBuddy remote cache and prefer BuildBuddy remote execution with local fallback. This is the normal mode for JVM, web, service, and other Linux-compatible targets.

Use the explicit local escape hatch only when the target requires local host state or a locally retained artifact:

bazel test --config=local //path/to/package:target_test

Apple work uses a macOS host and local execution:

bazel test --config=macos //apps/ios:peak_pos_client_lane_test

Android requires an Android platform configuration. The wrapper isolates Android's platform/NDK analysis state from the repository's default output base:

scripts/bazel-android.sh build //apps/android:android_app_local_staging
BAZEL_ANDROID_CONFIG=android_x86_64 scripts/bazel-android.sh test //apps/android/app/src/androidTest:android_app_launch_smoke_contract_test

A target that launches a device also requires the expected emulator or physical device. A successful APK build proves compilation/package creation, not installation, hardware behavior, or payment movement.

CI boundaries

CI is split by platform and deployment owner rather than implemented as one interchangeable local command:

  • .github/workflows/bazel-build.yml tests non-deploy targets with --config=ci --config=rbe and excludes services, websites, Android, and the private-docs build because those have dedicated lanes.
  • .github/workflows/container-images-deploy.yml queries non-manual microservice tests and runs them before image publication/deployment.
  • .github/workflows/cloudflare-workers.yml builds and tests website artifacts before the staging or production deployment job.
  • .github/workflows/android.yml builds/tests Android using Bazel's default ARM64 Android platform and explicitly excludes manual and requires-android-device tests from its regular unit-test query.
  • .github/workflows/docs.yml builds //docs/private-docs:build locally with network-capable execution and remote/disk caches disabled.

Use the same focused target locally to debug a failure. Use --config=ci --config=rbe only when the failure is specific to the CI environment or remote executor; the developer default already exercises remote execution with an outage-safe fallback.

Evidence expectations

For every changed behavior, record:

  1. the exact command and Bazel label or smoke scope;
  2. whether it ran locally, on BuildBuddy, in local validation, in staging, or on a device;
  3. the final pass/fail/skip result;
  4. the BuildBuddy invocation URL or .dev-logs report path when emitted;
  5. any required gate that was unavailable, without relabeling it as passing.

Use these minimum combinations:

ChangeRequired proof
Backend logic or APIFocused service test; contract test when the public shape changed; local smoke for the affected routed flow.
Portal behaviorPortal component test and lint target; local browser smoke for the changed user flow.
Android behaviorFocused JVM test and configured app build; device smoke when behavior depends on Android/runtime hardware.
Apple behaviorStatic lane test where applicable and Mac/Xcode build or simulator evidence for native behavior.
Bazel/config/toolingOwning target plus one representative consumer.
Documentationdocs-index, docs-render, and //docs/private-docs:build.

Do not substitute a broad green run for a missing behavior-specific check, and do not run a production or destructive operation to manufacture test evidence.