Skip to main content

Testing

Purpose: For contributors, provides testing patterns, mocks, fixtures, and CI expectations across repos.

General rule

Any behavior change must include tests that fail before the change and pass after. Tests must cover error paths, edge cases, and boundary validation.

openCenter-cli (Go)

The CLI uses three testing approaches:

Unit tests

Standard Go tests in *_test.go files alongside the code they test. Use testify/assert for assertions.

mise run test    # go test ./internal/...

BDD tests (godog)

Gherkin feature files in tests/features/ define end-to-end scenarios. The godog framework maps steps to Go functions.

mise run godog          # Run all (skip @wip)
mise run godog-wip # Run only @wip scenarios

Tag scenarios with @wip during development. Remove the tag before merging.

Property-based tests (gopter)

Property tests verify invariants over randomized inputs. Test functions follow the TestProperty* naming convention.

mise run test-properties

Example property test pattern:

func TestPropertyConfigValidation(t *testing.T) {
properties := gopter.NewProperties(gopter.DefaultTestParameters())
properties.Property("valid config always passes validation", prop.ForAll(
func(name string) bool {
cfg := validConfigWith(name)
return cfg.Validate() == nil
},
gen.AlphaString(),
))
properties.TestingRun(t)
}

Test helpers

Shared test utilities live in internal/testing/ and internal/testutil/. Use these for creating test fixtures, temporary directories, and mock configurations.

openCenter-AirGap (Python)

pytest

Tests live in tests/. pytest runs with strict markers and a 300-second timeout.

pytest                          # All tests
pytest -m "not slow" # Skip slow tests
pytest -m property # Property tests only
pytest --cov=src --cov-report=html # With coverage

Coverage must stay above 80% (fail_under = 80.0 in pyproject.toml).

Hypothesis (property-based)

Property tests use the @pytest.mark.property marker and Hypothesis strategies.

from hypothesis import given, strategies as st

@pytest.mark.property
@given(version=st.from_regex(r"\d+\.\d+\.\d+", fullmatch=True))
def test_version_parsing_roundtrip(version: str) -> None:
parsed = parse_version(version)
assert str(parsed) == version

headlamp-branding-plugin (TypeScript)

Jest tests live in plugins/<name>/__tests__/.

pnpm test                                                    # All plugins
pnpm --filter @opencenter/headlamp-plugin-branding test # Specific plugin

CI expectations

All repositories run these checks on every PR:

CheckCLIAirGapHeadlamp
Formattinggofmtblack --checkprettier --check
Lintinggolangci-lintpylint + mypyESLint
Unit testsgo testpytestJest
BDD testsgodog
Property testsgopterHypothesis
Buildgo buildpip installwebpack
Coverage80% minimum

PRs that fail any check cannot be merged.

Deterministic tests

Tests must not depend on real time, network access, or unseeded randomness. Use:

  • Fakes and mocks for external services
  • Fixed timestamps for time-dependent logic
  • Seeded random generators for property tests
  • testdata/ directories for fixture files