Summary
src/cluster/mod.rs (the cluster create/delete/list/kubeconfig/load-image/kubectl dispatch and handler layer) has 0% test coverage: no #[cfg(test)] mod tests block exists in the file at all, across 315 lines and 19 top-level functions (dispatch + 6 handle_* command handlers + 12 private helpers for lookup, state upsert, phase transitions, dry-run/success reporting). This is despite the logic being straightforward, unit-testable business logic — cluster-by-name lookup, KIND-name derivation, state upsert/phase transitions, and dry-run reporting — that doesn't require a live KIND cluster to exercise.
Sibling command modules already establish the exact pattern needed here, via the CommandRunner trait's MockRunner test double (src/command/runner.rs):
src/command/down.rs — has a #[cfg(test)] mod tests block (228 lines) using MockRunner
src/command/up.rs — has a #[cfg(test)] mod tests block (730 lines) using MockRunner
src/command/status.rs — has a #[cfg(test)] mod tests block (316 lines) using MockRunner
cluster/mod.rs needs no new test infrastructure — it can drive dispatch() (or the individual handle_* functions) with a MockRunner exactly the way down.rs/up.rs/status.rs already do for kind_ops-backed calls (kind_ops::create_cluster, kind_ops::delete_cluster, kind_ops::list_clusters, kind_ops::get_kubeconfig, kind_ops::load_image, kind_ops::run_kubectl all bottom out in ctx.runner: &dyn CommandRunner calls, per src/cluster/kind.rs).
Evidence
Re-verified against fresh upstream/main (9abe12b, 2026-08-07):
src/cluster/mod.rs: 315 lines total, 19 fn/pub fn declarations (line numbers: 25, 47, 71, 94, 100, 112, 133, 150, 163, 168, 184, 203, 212, 235, 247, 258, 269, 280, 300), zero occurrences of cfg(test) or mod tests.
- Contrast with
src/command/down.rs, src/command/up.rs, src/command/status.rs, each of which has its own #[cfg(test)] mod tests block built on crate::command::runner::MockRunner.
Untested logic includes, concretely:
lookup_cluster (line 150) — returns ForgeError::Config for an unknown cluster name; the "cluster not found" error path is completely unexercised.
upsert_cluster_state (line 184) — the insert-vs-update branch (new cluster vs. existing cluster whose phase changes) has no coverage for either branch.
create_if_missing (line 168) and update_phase_gone (line 203) — the phase-transition logic (Running → Gone, and short-circuiting when a KIND cluster already exists) is untested.
handle_create/handle_delete/handle_load_image dry-run branches (ctx.dry_run short-circuit before touching kind_ops or state) — untested.
render_list and report_* helpers' OutputFormat::Json vs. OutputFormat::Text branches — untested.
Risk
- This is the dispatch/handler layer for a user-facing CLI surface (
forge cluster create|delete|list|kubeconfig|load-image|kubectl) with no regression safety net; a change to lookup, state-upsert, or phase-transition logic can silently break dry-run reporting or state consistency and only surface in manual testing or a full E2E run.
- The gap is inconsistent with this repo's own
CONTRIBUTING.md testing requirements ("New capabilities require: 1. Unit tests covering the implementation... A feature without appropriate tests is not complete") and with sibling command modules' established coverage levels.
Suggested fix
Add a #[cfg(test)] mod tests block to src/cluster/mod.rs mirroring the MockRunner-based structure already used in src/command/{down,up,status}.rs:
- Unit-test the pure-logic helpers directly (no runner needed):
lookup_cluster (found / not-found), upsert_cluster_state (insert vs. update branch), cluster_kind_name.
- Integration-style tests for the
handle_* functions using MockRunner to stub kind_ops calls (cluster exists / doesn't exist, create success/failure, delete, list, kubeconfig write-to-file vs. stdout, load-image, kubectl passthrough), plus the ctx.dry_run short-circuit branch for each handler that has one.
- Cover both
OutputFormat::Text and OutputFormat::Json branches in the report_*/render_list helpers.
No new test infrastructure is required; this is a direct application of the existing MockRunner pattern.
Severity: Medium
Summary
src/cluster/mod.rs(thecluster create/delete/list/kubeconfig/load-image/kubectldispatch and handler layer) has 0% test coverage: no#[cfg(test)] mod testsblock exists in the file at all, across 315 lines and 19 top-level functions (dispatch+ 6handle_*command handlers + 12 private helpers for lookup, state upsert, phase transitions, dry-run/success reporting). This is despite the logic being straightforward, unit-testable business logic — cluster-by-name lookup, KIND-name derivation, state upsert/phase transitions, and dry-run reporting — that doesn't require a live KIND cluster to exercise.Sibling command modules already establish the exact pattern needed here, via the
CommandRunnertrait'sMockRunnertest double (src/command/runner.rs):src/command/down.rs— has a#[cfg(test)] mod testsblock (228 lines) usingMockRunnersrc/command/up.rs— has a#[cfg(test)] mod testsblock (730 lines) usingMockRunnersrc/command/status.rs— has a#[cfg(test)] mod testsblock (316 lines) usingMockRunnercluster/mod.rsneeds no new test infrastructure — it can drivedispatch()(or the individualhandle_*functions) with aMockRunnerexactly the waydown.rs/up.rs/status.rsalready do forkind_ops-backed calls (kind_ops::create_cluster,kind_ops::delete_cluster,kind_ops::list_clusters,kind_ops::get_kubeconfig,kind_ops::load_image,kind_ops::run_kubectlall bottom out inctx.runner: &dyn CommandRunnercalls, persrc/cluster/kind.rs).Evidence
Re-verified against fresh
upstream/main(9abe12b, 2026-08-07):src/cluster/mod.rs: 315 lines total, 19fn/pub fndeclarations (line numbers: 25, 47, 71, 94, 100, 112, 133, 150, 163, 168, 184, 203, 212, 235, 247, 258, 269, 280, 300), zero occurrences ofcfg(test)ormod tests.src/command/down.rs,src/command/up.rs,src/command/status.rs, each of which has its own#[cfg(test)] mod testsblock built oncrate::command::runner::MockRunner.Untested logic includes, concretely:
lookup_cluster(line 150) — returnsForgeError::Configfor an unknown cluster name; the "cluster not found" error path is completely unexercised.upsert_cluster_state(line 184) — the insert-vs-update branch (new cluster vs. existing cluster whose phase changes) has no coverage for either branch.create_if_missing(line 168) andupdate_phase_gone(line 203) — the phase-transition logic (Running→Gone, and short-circuiting when a KIND cluster already exists) is untested.handle_create/handle_delete/handle_load_imagedry-run branches (ctx.dry_runshort-circuit before touchingkind_opsor state) — untested.render_listandreport_*helpers'OutputFormat::Jsonvs.OutputFormat::Textbranches — untested.Risk
forge cluster create|delete|list|kubeconfig|load-image|kubectl) with no regression safety net; a change to lookup, state-upsert, or phase-transition logic can silently break dry-run reporting or state consistency and only surface in manual testing or a full E2E run.CONTRIBUTING.mdtesting requirements ("New capabilities require: 1. Unit tests covering the implementation... A feature without appropriate tests is not complete") and with sibling command modules' established coverage levels.Suggested fix
Add a
#[cfg(test)] mod testsblock tosrc/cluster/mod.rsmirroring theMockRunner-based structure already used insrc/command/{down,up,status}.rs:lookup_cluster(found / not-found),upsert_cluster_state(insert vs. update branch),cluster_kind_name.handle_*functions usingMockRunnerto stubkind_opscalls (cluster exists / doesn't exist, create success/failure, delete, list, kubeconfig write-to-file vs. stdout, load-image, kubectl passthrough), plus thectx.dry_runshort-circuit branch for each handler that has one.OutputFormat::TextandOutputFormat::Jsonbranches in thereport_*/render_listhelpers.No new test infrastructure is required; this is a direct application of the existing
MockRunnerpattern.Severity: Medium