Skip to content

Add tests for serve.go pure helpers (#33370)#44929

Merged
getvictor merged 5 commits into
fleetdm:mainfrom
raju249:33370-pure-helper-tests
May 12, 2026
Merged

Add tests for serve.go pure helpers (#33370)#44929
getvictor merged 5 commits into
fleetdm:mainfrom
raju249:33370-pure-helper-tests

Conversation

@raju249
Copy link
Copy Markdown
Contributor

@raju249 raju249 commented May 7, 2026

First PR in the staged plan from #33370. Adds unit tests for several testable helpers in cmd/fleet/serve.go — argument stringification, TLS profile config, license initialization, and the missing-migrations warning.

The migrations-warning test required threading io.Writer through printMissingMigrationsWarning so it can pass *bytes.Buffer instead of mutating os.Stdout. The other two database-state print functions stay as-is since they aren't tested in this PR.

Related issue: Part of #33370 (intentionally not using auto-close keywords since this is the first of multiple PRs against this issue).

Checklist for submitter

If some of the following don't apply, delete the relevant line.

Testing

  • Added/updated automated tests

Database migrations

N/A — no database migrations in this PR.

New Fleet configuration settings

N/A — no new configuration settings.

fleetd/orbit/Fleet Desktop

N/A — no agent code changes.

Covers argsToString, getTLSConfig, initLicense, nopPusher.Push, and the
three database/migration print warnings. Adds a small captureStdout helper
reused across the print tests. No production code changes.
@raju249 raju249 requested a review from a team as a code owner May 7, 2026 14:30
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Walkthrough

Serve printing helpers were changed to accept an io.Writer and runServeCmd now calls them with os.Stdout. Tests were added in cmd/fleet/serve_test.go: TestArgsToString, TestGetTLSConfig (modern/intermediate), TestInitLicense, and TestPrintMissingMigrationsWarning; test imports were updated to support TLS types, driver.NamedValue, and stdout capture.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding tests for pure helper functions in serve.go, which matches the actual changeset of +169 test lines in serve_test.go.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly outlines the purpose (adding unit tests for testable helpers), references the related issue (#33370), identifies specific functions being tested, explains the io.Writer threading change, and provides context about the staged plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/fleet/serve_test.go`:
- Around line 1478-1498: The captureStdout helper mutates the process-global
os.Stdout without synchronization causing flaky parallel tests; serialize it by
adding a package-level mutex (e.g., captureStdoutMu) and lock it at the start of
captureStdout and unlock only after os.Stdout is restored, so only one test can
swap stdout at a time; also explicitly close the read end (r.Close()) after the
copy goroutine finishes (after <-done) and ensure the write end is closed
(w.Close()) before waiting, keeping the existing helpers and return behavior
intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cc219da8-57c9-47b2-9a9a-9379f6eca4a8

📥 Commits

Reviewing files that changed from the base of the PR and between 292bab3 and 82467a5.

📒 Files selected for processing (1)
  • cmd/fleet/serve_test.go

Comment thread cmd/fleet/serve_test.go Outdated
Adds a package-level mutex around the os.Stdout swap so concurrent test
use is safe, and explicitly closes the read end of os.Pipe to avoid a
file-descriptor leak.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.85%. Comparing base (9bee62b) to head (e7d9070).
⚠️ Report is 148 commits behind head on main.

Files with missing lines Patch % Lines
cmd/fleet/serve.go 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #44929      +/-   ##
==========================================
+ Coverage   66.73%   66.85%   +0.12%     
==========================================
  Files        2684     2689       +5     
  Lines      216067   217154    +1087     
  Branches    10066    10066              
==========================================
+ Hits       144190   145180     +990     
- Misses      58768    58807      +39     
- Partials    13109    13167      +58     
Flag Coverage Δ
backend 68.71% <0.00%> (+0.13%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@raju249
Copy link
Copy Markdown
Contributor Author

raju249 commented May 8, 2026

@lucasmrod - Can I please get a review on this PR?

Thanks! 🙏 🙇

@getvictor
Copy link
Copy Markdown
Member

@coderabbitai

  1. Is "capture stdout" a good industry standard pattern here? I feel like it is better to inject stdout for testability and to enable these tests to run in parallel.
  2. Are all the tests here high value?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look at the test file
fd "serve_test.go" cmd/fleet --exec cat {}

echo "---PRODUCTION CODE---"
# Look at the relevant print functions in serve.go
rg -n "func printDatabaseNotInitializedError|func printMissingMigrationsWarning|func printFleetv4732|func argsToString|func nopPusher|func getTLSConfig|func initLicense" cmd/fleet/serve.go -A 30

Length of output: 56072


@getvictor Great questions. Here's my analysis:


1. captureStdout vs. injecting io.Writer

The captureStdout approach is not the idiomatic Go pattern for this situation. The concerns you raised are valid:

Problems with the current approach:

  • It mutates the global os.Stdout, which is a process-wide side effect.
  • The package-level captureStdoutMu mutex serializes all tests that use captureStdout, meaning those tests cannot run in parallel even if t.Parallel() were added later.
  • It's fragile: any background goroutine (e.g. from the runtime or other tests) that writes to os.Stdout during the swap could corrupt the captured output or produce flaky tests.

The idiomatic Go alternative is to refactor the three print functions to accept an io.Writer:

// Before
func printDatabaseNotInitializedError() {
    fmt.Printf("...", os.Args[0])
}

// After
func printDatabaseNotInitializedError(w io.Writer) {
    fmt.Fprintf(w, "...", os.Args[0])
}

Production call sites pass os.Stdout; tests pass a *bytes.Buffer. This eliminates the mutex entirely, enables t.Parallel(), and is the pattern used throughout the Go standard library itself. The captureStdout mutex is a band-aid around a design issue in the production functions.


2. Are all the tests high value?

Test Value Rationale
TestGetTLSConfig ✅ High Security-critical — validates exact cipher suites, min TLS version, and curve preferences
TestInitLicense ✅ High Real branching logic (dev / expired / no-key), meaningful invariants
TestArgsToString ✅ Medium-High Documents boundary cases (nil, named, positional, mixed types) of a utility formatter
TestPrintMissingMigrationsWarning ⚠️ Medium Worth keeping for the tablesAndDataToString branch coverage (tables-only, data-only, both, neither)
TestPrintDatabaseNotInitializedError ⚠️ Low Just asserts static substrings from a fmt.Printf with no branching logic
TestPrintFleetv4732FixNeededMessage ⚠️ Low Same — the function is a single static fmt.Printf, nothing to go wrong
TestNopPusher ❌ Very Low nopPusher.Push unconditionally returns nil, nil — there is no logic to verify

The two low-value print tests and TestNopPusher could be dropped without meaningfully reducing safety. The effort would be better spent on the io.Writer refactor, which would also make TestPrintMissingMigrationsWarning (the one with actual branches) both simpler and parallelizable.

@getvictor
Copy link
Copy Markdown
Member

@raju249 please address the Code Rabbit comment above.

raju249 added 2 commits May 11, 2026 13:10
Per review feedback, narrowed this PR's scope to test-only additions for
the high-value pure helpers. Removed TestNopPusher and the three
print-function tests along with the captureStdout helper they relied on.
The print-function refactor to accept io.Writer for proper test
injection will land in a separate follow-up PR.
Make the three database-state print functions take an io.Writer so
tests can pass a *bytes.Buffer instead of mutating os.Stdout globally.
Production callers in runServeCmd pass os.Stdout. The migrations-warning
test is restored under this pattern, and all four added tests now run
with t.Parallel() — which the previous captureStdout/mutex approach
could not support.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
cmd/fleet/serve_test.go (1)

1406-1445: ⚡ Quick win

Consider assert.Equal instead of assert.ElementsMatch for order-sensitive slices.

The test uses ElementsMatch for CurvePreferences and CipherSuites, which ignores order. However, the order of these slices determines TLS preference order when PreferServerCipherSuites is true, making it security-critical. Using assert.Equal would catch accidental reordering bugs.

🔒 Suggested hardening for TLS config validation
-		assert.ElementsMatch(t, expectedCurves, cfg.CurvePreferences)
-		assert.ElementsMatch(t, []uint16{
+		assert.Equal(t, expectedCurves, cfg.CurvePreferences)
+		assert.Equal(t, []uint16{
 			tls.TLS_AES_128_GCM_SHA256,
 			tls.TLS_AES_256_GCM_SHA384,
 			tls.TLS_CHACHA20_POLY1305_SHA256,
 			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
 			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
 		}, cfg.CipherSuites)

Apply the same change to the "intermediate" subtest.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/fleet/serve_test.go` around lines 1406 - 1445, The test TestGetTLSConfig
uses assert.ElementsMatch for order-sensitive fields which hides regressions in
preference order; change the assertions in both subtests to use assert.Equal on
cfg.CurvePreferences and cfg.CipherSuites (and keep the existing checks for
cfg.MinVersion and cfg.PreferServerCipherSuites) so the test compares order as
well as content when calling getTLSConfig; update assertions in the "modern" and
"intermediate" subtests to assert.Equal against the expected ordered slices.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cmd/fleet/serve_test.go`:
- Around line 1406-1445: The test TestGetTLSConfig uses assert.ElementsMatch for
order-sensitive fields which hides regressions in preference order; change the
assertions in both subtests to use assert.Equal on cfg.CurvePreferences and
cfg.CipherSuites (and keep the existing checks for cfg.MinVersion and
cfg.PreferServerCipherSuites) so the test compares order as well as content when
calling getTLSConfig; update assertions in the "modern" and "intermediate"
subtests to assert.Equal against the expected ordered slices.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e7e87c7a-af40-46ad-902c-e2c2b50ae4e2

📥 Commits

Reviewing files that changed from the base of the PR and between 12eefe1 and a8131c0.

📒 Files selected for processing (2)
  • cmd/fleet/serve.go
  • cmd/fleet/serve_test.go

@raju249
Copy link
Copy Markdown
Contributor Author

raju249 commented May 11, 2026

@coderabbitai - Can you take a review after your last suggestion?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

@raju249 — Based on the commit history and AI summary, it looks like you've already addressed both recommendations:

  1. io.Writer refactor ✅ — printDatabaseNotInitializedError, printMissingMigrationsWarning, and printFleetv4732FixNeededMessage now accept an io.Writer; production callers pass os.Stdout and tests pass a *bytes.Buffer.
  2. Low-value tests & captureStdout removed ✅ — TestNopPusher, TestPrintDatabaseNotInitializedError, TestPrintFleetv4732FixNeededMessage, and the captureStdout helper have all been dropped.

Let me trigger a fresh review of the current state of the PR.

✅ Actions performed

Full review triggered.

@raju249
Copy link
Copy Markdown
Contributor Author

raju249 commented May 11, 2026

@getvictor - I think this is ready for another round of review. Addressed suggestions from coderabbit.

Can you take a look, please?

Thanks! 🙏 🙇

Copy link
Copy Markdown
Member

@getvictor getvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall. Just a couple more small changes. Thank you.

Comment thread cmd/fleet/serve.go Outdated
Comment thread cmd/fleet/serve.go Outdated
Comment thread cmd/fleet/serve_test.go Outdated
Comment thread cmd/fleet/serve_test.go Outdated
Comment thread cmd/fleet/serve_test.go Outdated
Comment thread cmd/fleet/serve_test.go Outdated
Comment thread cmd/fleet/serve_test.go Outdated
Comment thread cmd/fleet/serve_test.go Outdated
Comment thread cmd/fleet/serve_test.go Outdated
Revert printDatabaseNotInitializedError and printFleetv4732FixNeededMessage
to their original parameter-less signatures since they have no test coverage.
Keep printMissingMigrationsWarning's io.Writer parameter, which the test
exercises. Drop t.Parallel from subtests per review — outer-level parallel
coverage is sufficient and avoids the per-subtest overhead.
@raju249
Copy link
Copy Markdown
Contributor Author

raju249 commented May 12, 2026

Thanks for the review @getvictor

This is ready for another round of review.

Thanks! 🙏 🙇

@raju249 raju249 requested a review from getvictor May 12, 2026 06:03
Copy link
Copy Markdown
Member

@getvictor getvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Thank you.

@getvictor getvictor merged commit ace3804 into fleetdm:main May 12, 2026
46 of 48 checks passed
getvictor pushed a commit that referenced this pull request May 29, 2026
Extracts the OTEL trace, metric, and log provider setup out of
`runServeCmd` and into `initOTELProviders` in a new `cmd/fleet/otel.go`.
Same pattern as the prior extractions on this issue (#44929, #45343,
#45583, #46166). Side effects (`otel.SetTracerProvider`,
`otel.SetMeterProvider`) are preserved inside the extracted function, so
runtime behavior is identical.

Three unit tests in `cmd/fleet/otel_test.go`:
- OTEL disabled (the common production path) returns `(nil, nil, nil)`
and never calls `initFatal`.
- OTEL enabled without log export returns non-nil trace and meter
providers; logger provider stays nil.
- Log export enabled returns all three providers non-nil.

One honest note on coverage: the four `initFatal` sites inside the
function are paranoid wrapping for OTEL SDK constructors that don't dial
at construction time, so the error paths are hard to drive in tests
without mocking the SDK. The tests above exercise the success paths and
the disabled gate, which is the bulk of the realistic flow.

This continues the path toward `serve.go` >60% coverage per the
discussion on #33370 — `serve.go` is now ~100 lines shorter and the OTEL
phase is testable as a unit. Remaining slices per the broader plan: MDM
Apple init, datastore init, Redis init.

**Related issue:** Refs #33370

# Checklist for submitter

- [x] Added/updated automated tests
- Changes file: not applicable — internal refactor with no user-visible
behavior change


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Centralized OpenTelemetry provider initialization into a single setup
path, simplifying startup and shutdown behavior and making observability
configuration clearer.

* **Tests**
* Added unit tests covering disabled/enabled telemetry paths and
optional log export, plus cleanup logic to ensure providers are shut
down correctly.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/46421?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants