MaaS Testing and Contribution Guide
This guide covers what tests exist in the MaaS project, how to run them, and how to add new tests when contributing a feature or fix.
Test Overview
| Type | Location | Language | Runner |
|---|---|---|---|
| Unit tests | maas-api/internal/, maas-api/cmd/ |
Go | make test |
| Unit tests | maas-controller/pkg/ |
Go | make test |
| E2E tests | test/e2e/tests/ |
Python (pytest) | run-tests-quick.sh, smoke.sh |
| CI smoke / E2E | test/e2e/scripts/prow_run_smoke_test.sh |
Bash + pytest | Konflux integration |
| Integration tests | opendatahub-tests | Python (pytest) | ODH CI / Nightly |
Repository Structure (Testing)
maas-api/
├── internal/**/*_test.go # Unit tests for API handlers, services, auth
├── cmd/*_test.go # Unit tests for CLI/server setup
└── test/fixtures/ # Shared Go test helpers (fakes, test data)
maas-controller/
└── pkg/**/*_test.go # Unit tests for CRD reconcilers
test/e2e/
├── tests/ # Pytest E2E test modules
│ ├── conftest.py # Shared session-scoped fixtures
│ └── test_*.py # Test modules (see table below)
├── fixtures/ # Kustomize overlays for test CRs
├── scripts/prow_run_smoke_test.sh # Full deploy + E2E (CI entrypoint)
├── smoke.sh # Run all E2E modules locally
├── run-tests-quick.sh # Quick pytest run (no deploy)
└── requirements.txt # Python dependencies
E2E Test Modules
| Module | What it covers |
|---|---|
test_smoke.py |
Health checks, catalog shape, basic inference |
test_api_keys.py |
API key CRUD, admin authorization, validation |
test_subscription.py |
Subscription enforcement, rate limiting, auth flows |
test_subscription_list_endpoints.py |
Subscription listing endpoints |
test_models_endpoint.py |
/v1/models subscription-aware filtering |
test_namespace_scoping.py |
Namespace isolation behavior |
test_external_oidc.py |
External OIDC token flows (skipped unless EXTERNAL_OIDC=true) |
Running Tests
Unit Tests (Go)
Both generate a coverage.html report in their respective directories.
E2E Tests (Python)
Prerequisites
OpenShift cluster with MaaS deployed, oc logged in as cluster-admin, Python 3.9+.
cd test/e2e
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export GATEWAY_HOST="maas.apps.your-cluster.example.com"
export E2E_SKIP_TLS_VERIFY=true
pytest tests/ -v # all tests
pytest tests/test_subscription.py -v # one module
pytest tests/test_api_keys.py::TestAPIKeyCreation -v # one class
Key Environment Variables
The E2E framework auto-discovers most values from the cluster. These are the most common overrides:
| Variable | Description |
|---|---|
GATEWAY_HOST |
Gateway hostname (required unless MAAS_API_BASE_URL is set) |
MAAS_API_BASE_URL |
Full MaaS API URL (auto-derived if not set) |
TOKEN |
User bearer token (falls back to oc whoami -t) |
ADMIN_OC_TOKEN |
Admin token; admin tests skip if unset |
E2E_SKIP_TLS_VERIFY |
Set true to skip TLS verification |
MODEL_NAME |
Override model ID (defaults to first from catalog) |
EXTERNAL_OIDC |
Set true to enable external OIDC tests |
See test/e2e/tests/conftest.py and individual test module docstrings for the full set of supported variables.
CI Pipeline
CI runs automatically on every PR and push to main. Here's how the different test types fit together:
graph TB
A[PR opened / push to main] --> B{What changed?}
B -->|maas-api/**| C[GitHub Actions: lint + unit tests]
B -->|maas-controller/api/**| D[GitHub Actions: verify-codegen]
B -->|Any non-docs change| E[Konflux: Build images]
E --> F[Integration test on ephemeral cluster]
F --> G[prow_run_smoke_test.sh → E2E pytest]
B -->|docs/** or *.md only| H[Skip build + E2E]
| System | Trigger | What runs |
|---|---|---|
| GitHub Actions | maas-api/** changes |
golangci-lint, make test (Go unit tests), image build |
| GitHub Actions | maas-controller/api/** or deployment/** changes |
make verify-codegen, kustomize manifest validation |
| Konflux | Any non-docs PR or push to main |
Builds multi-arch images, then runs full E2E on an ephemeral OpenShift cluster |
Konflux provisions a fresh cluster, deploys ODH + MaaS with the PR's built images, and runs test/e2e/scripts/prow_run_smoke_test.sh. Nightly builds use the same script against the latest main images — there is no separate nightly test suite.
Docs-only changes
PRs that only touch docs/** or *.md files skip Konflux builds and E2E entirely (controlled via CEL expressions in .tekton/ pipeline definitions).
Adding New Tests
Adding a Go Unit Test
- Create a
*_test.gofile next to the code you're testing (standard Go convention) - Use
testify/assertandtestify/requirefor assertions - For
maas-apihandlers: usenet/http/httptestand the fake listers inmaas-api/test/fixtures/ - For
maas-controllerreconcilers: usecontroller-runtime/pkg/client/fakewith scheme setup - Run
make testin the relevant directory to verify
Quick reference — a minimal handler test in maas-api:
func TestMyHandler_Returns200(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/v1/my-endpoint", nil)
rec := httptest.NewRecorder()
handler := NewMyHandler(/* fake dependencies */)
handler.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
}
Quick reference — a minimal reconciler test in maas-controller:
func TestMyReconciler_Succeeds(t *testing.T) {
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(/* seed */).Build()
r := &MyReconciler{Client: client, Scheme: scheme}
result, err := r.Reconcile(ctx, reconcile.Request{NamespacedName: key})
require.NoError(t, err)
require.False(t, result.Requeue)
}
CRD codegen
If you modify API types under maas-controller/api/, run make -C maas-controller generate manifests and commit the generated files. CI will fail if they're stale.
Adding an E2E Test
-
Pick the right module — add your test to an existing
test_*.pyfile if it fits the scope (see the E2E test modules table). Create a new module only if your feature doesn't fit any existing one. -
Use shared fixtures —
conftest.pyprovides session-scoped fixtures likemaas_api_base_url,headers,token,admin_headers,api_key, etc. ImportTLS_VERIFYfromconftestfor HTTP calls. -
Add test resources if needed — if your feature requires new MaaS CRs (models, subscriptions, auth policies), add a kustomize overlay under
test/e2e/fixtures/and include it in the basekustomization.yaml. -
Register new modules in CI —
prow_run_smoke_test.shruns an explicit file list. If you create a new test module, add it to the pytest invocation inrun_e2e_tests():# In test/e2e/scripts/prow_run_smoke_test.sh pytest \ "$test_dir/tests/test_api_keys.py" \ "$test_dir/tests/test_namespace_scoping.py" \ ... "$test_dir/tests/test_my_new_feature.py" # ← add hereWarning
smoke.shauto-discovers all files undertests/, butprow_run_smoke_test.shdoes not. Your new module will not run in Konflux CI or nightly builds unless you add it to that explicit list. -
Use skip markers for optional features — if your test depends on optional infrastructure (e.g., external OIDC), gate it with
pytest.mark.skipifso the same module runs cleanly in all environments.
Integration Testing with ODH Operator
MaaS is a component of Open Data Hub (ODH). Beyond the in-repo E2E tests, MaaS is also validated as part of the broader ODH integration test suite in opendatahub-tests.
How It Works
- The opendatahub-tests repo contains cross-component integration tests for the entire ODH platform, including model serving and MaaS functionality.
- These tests run in ODH CI and nightly pipelines against full ODH deployments with all components installed.
- MaaS-related integration tests validate that MaaS works correctly when deployed through the ODH operator alongside KServe, Authorino, and other platform components.
Contributing Integration Tests
If your change affects how MaaS integrates with the ODH operator or other ODH components, you may need to add or update tests in the opendatahub-tests repo:
- Follow the opendatahub-tests contributing guide and developer guide
- Tests are organized by component — look for MaaS-related tests under the relevant directory
- The project uses openshift-python-wrapper for Kubernetes/OpenShift API interactions
- Follow the project's style guide and run
pre-commitchecks before submitting
When to Add In-Repo vs Integration Tests
| Scenario | Where to add tests |
|---|---|
| New MaaS API endpoint or controller behavior | In-repo: Go unit tests + E2E in test/e2e/ |
| MaaS CRD changes or subscription logic | In-repo: controller unit tests + E2E |
| Interaction with ODH operator, KServe, or Authorino | opendatahub-tests repo |
| End-to-end model serving through the full ODH stack | opendatahub-tests repo |
| Bug fix with regression test | In-repo (unit or E2E depending on scope) |
Checklist: Adding a New Test
- Unit test: Add
*_test.goalongside your source inmaas-api/ormaas-controller/; runmake test - E2E test: Add to an existing
test_*.pyor create a new module intest/e2e/tests/ - Test fixtures: If new CRs are needed, add a kustomize overlay in
test/e2e/fixtures/ - CI registration: Add new E2E modules to the explicit list in
prow_run_smoke_test.sh - Skip markers: Use
pytest.mark.skipiffor tests requiring optional infrastructure - Local validation: Run
make testand/or./test/e2e/run-tests-quick.shbefore pushing - Integration tests: If your change affects ODH operator integration, update tests in opendatahub-tests