Skip to content

Improve unit test coverage for controllers, webhooks and metrics (baseline @ a0f33cc) #3202

Open

Description

@vikas-saxena02

What feature you would like to be added?

Improve unit test coverage for the controllers, webhooks and metrics, starting from a measured baseline, and publish coverage in CI so it's tracked per PR.

Baseline commit: a0f33cccc54858516d8954e7aef2c2bad0eecc76 (2026-09-14, "fix: stop adding SparkApplication owner reference to executor pods (#3152)"). Master will move; all line numbers and gaps in this issue refer to this commit.

Headline: total statement coverage is 34.1%. Several operational packages are at 0%: internal/metrics, pkg/tls, and the mutating/validating webhook configuration controllers. The SparkConnect reconcile loop is never exercised by tests.

Why is this needed?

Measured baseline

make unit-test at this commit (Go 1.25, envtest 1.35, run locally on macOS): all packages pass, 0 failures.

Total statement coverage: 34.1% (go tool cover -func=cover.out). This total includes generated code (pkg/client/**, zz_generated*) and cmd/ wiring, so it understates coverage of hand-written logic.

Per-package figures below are statement coverage as reported by go test (coverage: X% of statements).

Package Coverage Src LOC Test cases Notes
pkg/features 100.0% 45 5
internal/scheduler/kubescheduler 90.2% 143 8
internal/scheduler/yunikorn 86.9% 130 2
internal/webhook 82.6% 1628 84 Good depth
internal/controller/sparkapplication 63.3% 3828 97 Some state-machine states have no scenario
pkg/certificate 61.1% 410 10 Secret sync paths not directly tested
internal/scheduler/volcano 58.3% 200 1
internal/controller/sparkconnect 52.3% 812 23 Helpers tested; Reconcile() never called in tests
pkg/util 48.1% 1001 122 30/81 funcs not named in tests
internal/controller/scheduledsparkapplication 26.1% 495 20 One Reconcile() call in tests
internal/scheduler/yunikorn/resourceusage 16.1% 226 5 Spark memory-overhead maths largely untested
api/v1beta2 3.6% 530 9 Figure includes generated deepcopy; hand-written defaults/helpers are small
internal/metrics 0% 563 0 No test files
pkg/tls 0% 70 0 No test files
internal/controller/mutatingwebhookconfiguration 0% 204 0 No test files
internal/controller/validatingwebhookconfiguration 0% 204 0 No test files
internal/scheduler, pkg/scheme, pkg/version, api/v1alpha1 0% small 0 Interfaces, registration, types
cmd/operator/** 0% ~980 0 Startup wiring; low priority

Describe the solution you would like

Candidate gaps, by priority

P1: zero or low coverage on core behaviour

  1. SparkConnect reconcile loop (52.3%). internal/controller/sparkconnect/reconciler.go: Reconcile() is never invoked by tests. createOrUpdateServerPod (L298), createOrUpdateServerService (L512), createOrUpdateConfigMap (L249), updateSparkConnectStatus (L607), updateExecutorStatus (L628) and listExecutorPods (L645) are only reachable through it. The suite already starts envtest (suite_test.go), so this can be added without new test infrastructure.
  2. SparkApplication states with no scenario (63.3%). internal/controller/sparkapplication/controller_test.go covers New/Submitted, Running, Completed, Failed, Succeeding, Failing, Terminated/TTL and Suspend/Resume. It has no Context for:
    • reconcileFailedSubmissionSparkApplication (controller.go L451)
    • reconcilePendingRerunSparkApplication (L547)
    • reconcileInvalidatingSparkApplication (L583)
    • reconcileUnknownSparkApplication (L773)
  3. Metrics (0%). internal/metrics/sparkapplication_metrics.go and sparkpod_metrics.go have no tests. Counter increments and decrements (for example, running count on state transitions) and the latency and execution-time observations are unverified. These can be tested with prometheus/testutil and need no cluster.
  4. TLS config (0%). pkg/tls/tls.go: SetupTLS (L26), ParseTLSVersion (L55) and ParseCipherSuites (L68) have no tests. They are pure functions, so table tests are cheap.
  5. ScheduledSparkApplication (26.1%). The controller has only one Reconcile() call in tests. Paths that need a scenario: shouldStartNextRun (L298), killLastRunIfNotFinished (L337), checkAndUpdatePastRuns (L351), bookkeepPastRuns (L433) and concurrency policies.
  6. YuniKorn memory maths (resourceusage 16.1%). internal/scheduler/yunikorn/resourceusage/memory.go: overhead factor, PySpark and off-heap memory, and DriverPodRequests/ExecutorPodRequests are only exercised indirectly via TestSchedule. Direct table tests would pin the Spark memory-overhead maths.

P2: partial coverage

  1. Webhook configuration controllers (0%). internal/controller/mutatingwebhookconfiguration and validatingwebhookconfiguration have no tests for CA-bundle injection (updateMutatingWebhookConfiguration, L87).
  2. Certificate secret sync (61.1%). pkg/certificate/certificate.go: syncInternalCertSecret (L77), SyncCertManagerSecret (L131) and the parse/update helpers have no direct test.
  3. Volcano (58.3%). internal/scheduler/volcano/scheduler.go: syncPodGroupInClientMode (L141) and syncPodGroupInClusterMode (L158).

P3: tooling

  1. Publish coverage in CI. make unit-test already writes cover.out (Makefile L204), but CI (.github/workflows/integration.yaml, "Run go unit tests") doesn't surface it. Proposal: print go tool cover -func=cover.out | tail -1 in the job summary and upload cover.html as an artifact, excluding pkg/client/** and zz_generated*. This is vendor-neutral and needs no external coverage service.
  2. Makefile / envtest coupling. In unit-test (Makefile L203) the KUBEBUILDER_ASSETS=... assignment runs on its own recipe line, so it has no effect on the go test line that follows. The suites work only because each suite_test.go hardcodes BinaryAssetsDirectory to bin/k8s/1.35.0-<os>-<arch>, while ENVTEST_K8S_VERSION is derived from the k8s.io/api version. A future k8s bump may therefore break the envtest suites until those paths are updated. Proposal: export the variable on the go test line, or read KUBEBUILDER_ASSETS in the suites.

Proposed breakdown (sub-issues)

  • CI: publish unit-test coverage summary and artifact, so the 34.1% baseline is tracked per PR (item 10)
  • Makefile/envtest: fix KUBEBUILDER_ASSETS and remove hardcoded 1.35.0 (item 11)
  • SparkConnect: envtest reconcile tests (item 1)
  • SparkApplication: missing state-machine scenarios (item 2)
  • Metrics: unit tests with prometheus/testutil (item 3)
  • pkg/tls: table tests (item 4)
  • ScheduledSparkApplication: schedule and concurrency-policy scenarios (item 5)
  • Webhook configuration controllers and certificate sync (items 7 and 8)
  • Scheduler memory maths: YuniKorn and Volcano (items 6 and 9)

Describe alternatives you have considered

  • External coverage service (Codecov/Coveralls): not proposed. It adds a third-party dependency and token. go tool cover output in the job summary plus an HTML artifact is vendor-neutral and sufficient.
  • Hard coverage gate in CI now: not proposed. A threshold should be agreed after the baseline has been published for a while, to avoid blocking unrelated PRs.
  • Relying on e2e tests for coverage: e2e (test/e2e) needs a kind cluster and is slower. The gaps listed here are testable with plain unit tests or the existing envtest suites.

Additional context

Method:

  • Measured statement coverage from make unit-test at this commit.
  • Static analysis of the test surface to locate specific gaps (test cases per package, and functions that no _test.go references). A function with no direct test may still be covered through Reconcile() or another public entry point.

Out of scope

  • Generated code (pkg/client/**, zz_generated.deepcopy.go)
  • e2e tests (test/e2e) and drift tests (test/drift)
  • A hard coverage gate in CI; that is to be discussed once the baseline is published

Love this feature?

Give it a 👍 We prioritize the features with most 👍

Activity

  1. vikas-saxena02 commented on Sep 27, 2026

    @vikas-saxena02
    ContributorAuthor

    🌱 Note for new contributors

    Thanks for your interest! Many of these tasks are well-scoped unit tests, so this is a good place to start contributing.

    Please don't assign this issue (#3202) to yourself. It's the parent tracking issue. Once maintainers have agreed on the breakdown, I'll create sub-issues and link them here, and those are the ones to pick up. Comment /assign on the sub-issue you'd like to take.

    Thank you! 🙏

  2. UchihaIthachi commented on Oct 17, 2026

    @UchihaIthachi

    Hi @vikas-saxena02, I'd like to take the metrics part (item 3).

    internal/metrics still has no tests on master and I didn't find a PR for it.
    I'd cover both files with table tests in the same style as pkg/tls/tls_test.go:
    the running/success/failure counts through create, update and delete
    (including Pending -> Succeeded and Running -> Unknown), the label fallbacks
    in getMetricLabels, and the submit/start latency histograms in
    sparkapplication_metrics.go. No envtest, and Register() isn't called so the
    global registry stays untouched.

    I can send it as one PR, or as two (executor metrics first, then application
    metrics) if smaller PRs are easier to review. Whichever you prefer.

    One small thing: incFailureCount logs "Increased Spark executor running count".
    I can fix that separately if you want.

    Could you open a sub-issue for this?

  3. vikas-saxena02 commented on Oct 18, 2026

    @vikas-saxena02
    ContributorAuthor

    7. Hi @vikas-saxena02, I'd like to take the metrics part (item 3).
    internal/metrics still has no tests on master and I didn't find a PR for it.
    I'd cover both files with table tests in the same style as pkg/tls/tls_test.go:
    the running/success/failure counts through create, update and delete
    (including Pending -> Succeeded and Running -> Unknown), the label fallbacks
    in getMetricLabels, and the submit/start latency histograms in
    sparkapplication_metrics.go. No envtest, and Register() isn't called so the
    global registry stays untouched.
    I can send it as one PR, or as two (executor metrics first, then application
    metrics) if smaller PRs are easier to review. Whichever you prefer.
    One small thing: incFailureCount logs "Increased Spark executor running count".
    I can fix that separately if you want.
    Could you open a sub-issue for this?

    @UchihaIthachi plz go for it... but please write the test cases in ginkgo. see #3231 and #3232 for reference. i dont have privilegese to create subtasks for features on the repo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions