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
Coverage—80% 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