Skip to content

Comments

Add Test Framework for BootServer#248

Open
hardikdr wants to merge 1 commit intomainfrom
enh/improve-bootserver-tests
Open

Add Test Framework for BootServer#248
hardikdr wants to merge 1 commit intomainfrom
enh/improve-bootserver-tests

Conversation

@hardikdr
Copy link
Member

@hardikdr hardikdr commented Dec 10, 2025

Summary by CodeRabbit

  • Tests

    • Added integration and unit suites to verify Boot Server startup, /httpboot availability, response contents, YAML→JSON rendering, and error handling.
  • Refactor

    • Client matching now uses network-identifier based indexing for HTTP boot lookups.
  • Bug Fixes

    • Boot Server startup now surfaces startup errors cleanly instead of failing silently.

@github-actions github-actions bot added size/L api-change enhancement New feature or request labels Dec 10, 2025
@hardikdr hardikdr linked an issue Dec 10, 2025 that may be closed by this pull request
@hardikdr hardikdr force-pushed the enh/improve-bootserver-tests branch from 79263df to 9699701 Compare December 10, 2025 15:28
@hardikdr hardikdr requested a review from defo89 December 10, 2025 15:47
@hardikdr hardikdr force-pushed the enh/improve-bootserver-tests branch 3 times, most recently from 20f7de9 to 716d059 Compare December 10, 2025 16:18
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

Walkthrough

Added NetworkIdentifierIndexKey constant and switched HTTP boot indexing/lookup from SystemIPIndexKey to the new key. Converted RunBootServer to return an error. Added Ginkgo/Gomega tests and an integration-style suite exercising the Boot Server and /httpboot behavior.

Changes

Cohort / File(s) Summary
Index Key & runtime change
api/v1alpha1/constants.go, cmd/main.go, server/bootserver.go
Added exported constant NetworkIdentifierIndexKey ("spec.networkIdentifiers"). Updated indexing/lookup to use the new key instead of SystemIPIndexKey. Changed RunBootServer to return error and adjusted goroutine error handling in cmd/main.go.
Boot Server tests
server/bootserver_suit_test.go, server/bootserver_test.go
Added Ginkgo/Gomega test suite and unit tests covering the /httpboot endpoint, YAML→JSON rendering (renderIgnition), invalid YAML handling, and SetStatusCondition validation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is entirely missing, whereas the repository template requires sections for 'Proposed Changes' and issue references. Add a comprehensive description following the template with proposed changes and relevant issue references to document the purpose and scope of these test framework additions.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add Test Framework for BootServer' accurately summarizes the main changes, which include new test files and test framework setup for the BootServer functionality.

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

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch enh/improve-bootserver-tests

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
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: 2

🤖 Fix all issues with AI agents
In `@server/bootserver_suit_test.go`:
- Around line 40-47: The fake k8s client created with fake.NewClientBuilder() is
missing the field indexer for NetworkIdentifierIndexKey, so List calls using
client.MatchingFields{bootv1alpha1.NetworkIdentifierIndexKey: ip} in
handleHTTPBoot won't match; fix by configuring the fake builder with
WithIndex(bootv1alpha1.NetworkIdentifierIndexKey, func(obj client.Object)
[]string { /* return the field value used by the real indexer, e.g., extract
NetworkIdentifier from BootConfig/httpBootConfig */ }) before Build(), so the
k8sClient used by RunBootServer has the same field index behavior as the real
client.

In `@server/bootserver_test.go`:
- Around line 99-105: The call to k8sClient.Create(context.Background(), secret)
ignores its returned error; update the test to capture the error (e.g. err :=
k8sClient.Create(...)) and fail the test if creation fails (use
t.Fatalf/t.Fatalf or require.NoError(t, err)) so the Secret named "bad-type" was
actually created before continuing; ensure you reference the same secret
variable and context.Background() call when checking the error.
🧹 Nitpick comments (2)
server/bootserver_suit_test.go (1)

20-28: Consider using dynamic port allocation and adding cleanup.

The hardcoded port :30003 could cause test failures if the port is already in use. Consider using :0 to let the OS assign an available port, then retrieve the actual address. Additionally, there's no AfterSuite to gracefully shut down the server.

server/bootserver_test.go (1)

46-48: Simplify redundant assertion.

SatisfyAny(BeEmpty(), Equal("")) is redundant for strings since BeEmpty() already matches an empty string. Consider simplifying to just BeEmpty().

♻️ Proposed fix
 			By("not setting a SystemUUID in the default case")
-			Expect(body.SystemUUID).To(SatisfyAny(BeEmpty(), Equal("")))
+			Expect(body.SystemUUID).To(BeEmpty())
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc3f3dd and c155713.

📒 Files selected for processing (5)
  • api/v1alpha1/constants.go
  • cmd/main.go
  • server/bootserver.go
  • server/bootserver_suit_test.go
  • server/bootserver_test.go
🧰 Additional context used
🧬 Code graph analysis (4)
cmd/main.go (1)
api/v1alpha1/constants.go (1)
  • NetworkIdentifierIndexKey (11-11)
server/bootserver_suit_test.go (2)
api/v1alpha1/groupversion_info.go (1)
  • AddToScheme (22-22)
server/bootserver.go (1)
  • RunBootServer (51-95)
server/bootserver_test.go (2)
api/v1alpha1/ipxebootconfig_types.go (1)
  • IPXEBootConfig (73-79)
server/bootserver.go (1)
  • SetStatusCondition (446-474)
server/bootserver.go (1)
api/v1alpha1/constants.go (1)
  • NetworkIdentifierIndexKey (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: lint
  • GitHub Check: Run on Ubuntu
  • GitHub Check: Test
  • GitHub Check: Run on Ubuntu
  • GitHub Check: Run on Ubuntu
  • GitHub Check: Run on Ubuntu
🔇 Additional comments (6)
api/v1alpha1/constants.go (1)

7-13: LGTM!

The new NetworkIdentifierIndexKey constant follows the established naming conventions and provides a clear, documented index key for network identifiers (IP addresses and MAC addresses). The aligned comments maintain good readability.

server/bootserver.go (1)

394-405: LGTM!

The update from SystemIPIndexKey to NetworkIdentifierIndexKey aligns with the new constant and the updated indexer in cmd/main.go. The field indexer for NetworkIdentifierIndexKey is properly configured via IndexHTTPBootConfigByNetworkIDs.

cmd/main.go (1)

353-363: LGTM!

The indexer correctly uses the new NetworkIdentifierIndexKey constant, and the returned values (HTTPBootConfig.Spec.NetworkIdentifiers) properly align with the indexed field path.

server/bootserver_test.go (3)

25-49: Good test coverage for the default httpboot behavior.

The test correctly validates the default response structure, including status code, content type, and the expected default UKI URL when no HTTPBootConfig matches.


51-71: Good coverage for renderIgnition function.

The tests effectively cover both the happy path (valid Butane YAML conversion) and the error path (invalid YAML handling).


73-117: Good error path coverage for SetStatusCondition.

The tests properly validate error handling for unknown condition types and unsupported resource types, verifying the expected error messages.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/bootserver_suit_test.go`:
- Around line 21-22: Replace the hardcoded testServerAddr and testServerURL with
an ephemeral bind: create a listener (e.g., net.Listen("tcp", ":0")) in the test
setup, capture the actual bound address via listener.Addr().String(), assign
that to testServerAddr and build testServerURL from it before starting the
server goroutine (or use httptest.NewServer to get an ephemeral URL), and ensure
the server uses the provided listener so the bind cannot fail due to port
collision; update any places referencing testServerAddr/testServerURL
accordingly.
- Around line 48-51: The anonymous polling function passed to Eventually uses
http.Get but never closes the returned *http.Response, leaking resources; modify
that closure (the func used in Eventually) to capture the response with resp,
err := http.Get(...), and when resp is non-nil ensure resp.Body.Close() is
called before returning (e.g., close immediately after error check or via a
short-lived defer), and return err as before so the reachability check still
works; update the closure in bootserver_suit_test.go accordingly.
- Line 10: Remove the unused import "github.com/go-logr/logr" (or reference the
declared logr variable) and start the boot server in a background goroutine
before the Eventually check: spawn a goroutine that invokes the Boot Server
start/serve method (e.g. bootServer.Serve/Start or whatever method
constructs/starts the HTTP server) using testServerAddr, ipxeServiceURL,
k8sClient and defaultUKIURL (the variables already declared) so the server is
listening while Eventually polls; ensure any returned error from the start call
is logged or sent to a channel for test failure reporting.

---

Duplicate comments:
In `@server/bootserver_suit_test.go`:
- Around line 40-46: No change required: the field indexer for
NetworkIdentifierIndexKey has been correctly registered on HTTPBootConfig by
indexing Spec.NetworkIdentifiers in the fake client builder; ensure the
WithIndex call in the test (that registers
bootv1alpha1.NetworkIdentifierIndexKey and uses func(obj client.Object) []string
{ config := obj.(*bootv1alpha1.HTTPBootConfig); return
config.Spec.NetworkIdentifiers }) remains unchanged so client.MatchingFields
lookups in handleHTTPBoot continue to work against the fake k8s client.

@hardikdr hardikdr force-pushed the enh/improve-bootserver-tests branch from 9471d8b to 0116a0b Compare February 19, 2026 14:43
@hardikdr
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
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 (3)
server/bootserver_test.go (1)

46-47: Redundant assertion: BeEmpty() and Equal("") are equivalent for strings.

SatisfyAny(BeEmpty(), Equal("")) can be simplified to just BeEmpty().

-			Expect(body.SystemUUID).To(SatisfyAny(BeEmpty(), Equal("")))
+			Expect(body.SystemUUID).To(BeEmpty())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/bootserver_test.go` around lines 46 - 47, The assertion in the test
uses a redundant SatisfyAny(BeEmpty(), Equal("")) for body.SystemUUID; replace
that expectation with a single BeEmpty() matcher in the test (look for the
Expect(body.SystemUUID).To(...) line in bootserver_test.go) to simplify and
remove the duplicate Equal("") branch.
server/bootserver.go (1)

51-97: http.DefaultServeMux usage limits testability and safety.

RunBootServer registers routes on http.DefaultServeMux via http.HandleFunc. This means:

  1. Routes accumulate globally — calling RunBootServer twice (e.g., in parallel tests) silently double-registers handlers.
  2. Other code in the process that also uses http.DefaultServeMux can interfere.

Consider accepting or creating a dedicated *http.ServeMux and passing it to http.ListenAndServe. This also makes it easier to write isolated tests with httptest.NewServer(mux).

♻️ Sketch
-func RunBootServer(ipxeServerAddr string, ipxeServiceURL string, k8sClient client.Client, log logr.Logger, defaultUKIURL string) error {
-	http.HandleFunc("/ipxe/", func(w http.ResponseWriter, r *http.Request) {
+func RunBootServer(ipxeServerAddr string, ipxeServiceURL string, k8sClient client.Client, log logr.Logger, defaultUKIURL string) error {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/ipxe/", func(w http.ResponseWriter, r *http.Request) {
 		handleIPXE(w, r, k8sClient, log, ipxeServiceURL)
 	})
 
-	http.HandleFunc("/httpboot", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/httpboot", func(w http.ResponseWriter, r *http.Request) {
 		handleHTTPBoot(w, r, k8sClient, log, defaultUKIURL)
 	})
 
-	http.HandleFunc("/ignition/", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/ignition/", func(w http.ResponseWriter, r *http.Request) {
 		// ...
 	})
 
 	log.Info("Starting boot server", "address", ipxeServerAddr)
-	if err := http.ListenAndServe(ipxeServerAddr, nil); err != nil {
+	if err := http.ListenAndServe(ipxeServerAddr, mux); err != nil {
 		log.Error(err, "failed to start boot server")
 		return err
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/bootserver.go` around lines 51 - 97, RunBootServer currently registers
handlers on the global http.DefaultServeMux via http.HandleFunc which makes
tests flaky and handlers leak; change RunBootServer to accept a *http.ServeMux
(or create a new mux inside when nil) and replace all http.HandleFunc calls with
mux.HandleFunc, then pass that mux to http.ListenAndServe (or use it in
httptest.NewServer in tests); update callers to provide a mux or rely on the
function creating one when a nil argument is passed so tests can supply isolated
httptest servers and production code uses its own dedicated ServeMux.
cmd/main.go (1)

306-311: Consider using mgr.Add instead of a bare goroutine + panic for the boot server.

The controller-runtime Manager supports adding custom Runnables via mgr.Add(manager.RunnableFunc(...)). This integrates the boot server lifecycle with the manager's shutdown/health signals, so a boot-server failure triggers a graceful manager shutdown rather than an abrupt panic crash with no cleanup. This provides better lifecycle consistency and error handling.

♻️ Example refactor
mgr.Add(manager.RunnableFunc(func(ctx context.Context) error {
    return bootserver.RunBootServer(bootserverAddr, ipxeServiceURL, mgr.GetClient(), serverLog.WithName("bootserver"), *defaultHttpUKIURL)
}))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/main.go` around lines 306 - 311, Replace the bare goroutine + panic
pattern that starts the boot server with adding it to the controller-runtime
Manager so its lifecycle and errors are handled gracefully: remove the go func
that calls bootserver.RunBootServer and instead register a manager.Runnable (use
manager.RunnableFunc) with mgr.Add that calls bootserver.RunBootServer and
returns its error; this ensures bootserver failures propagate as returned errors
to the manager (mgr) for graceful shutdown rather than causing a panic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@server/bootserver_suit_test.go`:
- Around line 22-23: Replace the hardcoded testServerAddr=":30003" and
testServerURL="http://localhost:30003" by creating a net.Listener on ":0" to get
an ephemeral port, then set testServerAddr from the listener.Addr().String() and
build testServerURL from that actual bound address; start the server with
http.Serve or (&http.Server{}).Serve(listener) instead of ListenAndServe, and
update any test helpers that read testServerAddr/testServerURL so Eventually and
Listen failures use the real ephemeral port.
- Around line 41-43: The fake k8s client used in the test doesn't register the
NetworkIdentifierIndexKey indexer, so handleHTTPBoot's
k8sClient.List(client.MatchingFields{NetworkIdentifierIndexKey: ip}) errors out
and the test follows the error path; fix by registering the same field indexer
on the test fake client used to build k8sClient (i.e., extend the
fake.NewClientBuilder() setup) with the identical index function used in
production for HTTPBootConfig so that NetworkIdentifierIndexKey resolves
correctly and List(...) can find matching HTTPBootConfig objects.

In `@server/bootserver_test.go`:
- Around line 98-105: The Secret creation call currently ignores the returned
error; change the `_ = k8sClient.Create(context.Background(), secret)` to
capture and assert the result (e.g., `err :=
k8sClient.Create(context.Background(), secret)` followed by a test assertion
like `Expect(err).NotTo(HaveOccurred())` or `require.NoError(t, err)`) so
failures surface and the test cannot pass due to a silent create error; update
the code around the secret variable and the k8sClient.Create call accordingly.

---

Nitpick comments:
In `@cmd/main.go`:
- Around line 306-311: Replace the bare goroutine + panic pattern that starts
the boot server with adding it to the controller-runtime Manager so its
lifecycle and errors are handled gracefully: remove the go func that calls
bootserver.RunBootServer and instead register a manager.Runnable (use
manager.RunnableFunc) with mgr.Add that calls bootserver.RunBootServer and
returns its error; this ensures bootserver failures propagate as returned errors
to the manager (mgr) for graceful shutdown rather than causing a panic.

In `@server/bootserver_test.go`:
- Around line 46-47: The assertion in the test uses a redundant
SatisfyAny(BeEmpty(), Equal("")) for body.SystemUUID; replace that expectation
with a single BeEmpty() matcher in the test (look for the
Expect(body.SystemUUID).To(...) line in bootserver_test.go) to simplify and
remove the duplicate Equal("") branch.

In `@server/bootserver.go`:
- Around line 51-97: RunBootServer currently registers handlers on the global
http.DefaultServeMux via http.HandleFunc which makes tests flaky and handlers
leak; change RunBootServer to accept a *http.ServeMux (or create a new mux
inside when nil) and replace all http.HandleFunc calls with mux.HandleFunc, then
pass that mux to http.ListenAndServe (or use it in httptest.NewServer in tests);
update callers to provide a mux or rely on the function creating one when a nil
argument is passed so tests can supply isolated httptest servers and production
code uses its own dedicated ServeMux.

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

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Improve Test Coverage for Boot Server

2 participants