---
phase: 02-command-test-coverage
plan: 02
type: tdd
wave: 1
depends_on: []
files_modified:
  - cmd/submit.go
  - cmd/submit_test.go
autonomous: true
requirements:
  - TST-02
must_haves:
  truths:
    - "Running `task test:unit` exercises submit.go RunE — all four guard paths are tested"
    - "An invalid slug passed to submit RunE returns a non-nil error without any API call"
    - "A nil progress response causes submit RunE to return nil (challenge not started guard)"
    - "A 'completed' progress status causes submit RunE to return nil (already completed guard)"
    - "A simulated API failure in GetChallenge returns a non-nil error from RunE — no panic"
  artifacts:
    - path: "cmd/submit.go"
      provides: "Function vars apiGetChallengeForSubmit, apiGetProgressForSubmit fronting direct api.* calls"
      contains: "var apiGetChallengeForSubmit"
    - path: "cmd/submit_test.go"
      provides: "Unit tests for submit.go RunE guards"
      exports: ["TestSubmitRunE_InvalidSlug", "TestSubmitRunE_ProgressNil", "TestSubmitRunE_AlreadyCompleted", "TestSubmitRunE_APIFailure"]
  key_links:
    - from: "cmd/submit_test.go"
      to: "cmd/submit.go"
      via: "package cmd — direct unexported var access"
      pattern: "apiGetChallengeForSubmit\\s*="
---

<objective>
Add function-variable injection points to submit.go and write four unit tests covering its flow guards.

Purpose: TST-02 — submit.go RunE has zero test coverage. The guards (slug validation, nil progress, completed progress, API failure) are all testable without a Kubernetes cluster since they return before line 82 where kube.GetKubernetesClient() is called.
Output: cmd/submit.go with two function vars; cmd/submit_test.go with four passing tests.
</objective>

<execution_context>
@/Users/paul/.claude/get-shit-done/workflows/execute-plan.md
@/Users/paul/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-command-test-coverage/02-CONTEXT.md
@.planning/phases/02-command-test-coverage/02-RESEARCH.md
</context>

<interfaces>
<!-- Key types and contracts the executor needs. Extracted from codebase. -->

From internal/api/types.go:
```go
type ChallengeResponse struct {
    Title string `json:"title"`
    Slug  string `json:"slug"`
    // ... other fields
}
type ChallengeStatusResponse struct {
    Status string `json:"status"` // "not_started" | "in_progress" | "completed"
}
type ChallengeEntity = ChallengeResponse
```

From internal/api/client.go:
```go
func GetChallenge(slug string) (*ChallengeEntity, error)
func GetChallengeProgress(slug string) (*ChallengeStatusResponse, error)
```

From cmd/submit.go (call sites to front with vars):
```go
// Line 32 — inside WaitMessage closure
_, err := api.GetChallenge(challengeSlug)

// Line 44 — inside WaitMessage closure
progress, err = api.GetChallengeProgress(challengeSlug)
```

Note: The Kubernetes path starts at line 82 (`kube.GetKubernetesClient()`). All four guard paths return before reaching it:
- Invalid slug → return at line 24-26
- apiGetChallengeForSubmit error → return at line 35-38
- apiGetProgressForSubmit error → return at line 47-50
- progress == nil → return nil at line 52-56
- progress.Status == "completed" → return nil at line 58-62
</interfaces>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Add function vars to submit.go and write submit_test.go</name>
  <files>cmd/submit.go, cmd/submit_test.go</files>
  <behavior>
    - TestSubmitRunE_InvalidSlug: args=["INVALID_SLUG"] → RunE returns non-nil error containing "invalid challenge slug"
    - TestSubmitRunE_ProgressNil: apiGetChallengeForSubmit returns ChallengeEntity{Title:"Test"}, apiGetProgressForSubmit returns (nil, nil) → RunE returns nil
    - TestSubmitRunE_AlreadyCompleted: apiGetChallengeForSubmit returns ChallengeEntity{Title:"Test"}, apiGetProgressForSubmit returns ChallengeStatusResponse{Status:"completed"} → RunE returns nil
    - TestSubmitRunE_APIFailure: apiGetChallengeForSubmit returns (nil, fmt.Errorf("network error")) → RunE returns non-nil error; assert.NotPanics confirms no panic
  </behavior>
  <action>
**RED phase — write tests first, then make them pass.**

**Step 1: Write failing tests in cmd/submit_test.go**

Create cmd/submit_test.go in package cmd. Import "fmt", "testing", "github.com/stretchr/testify/assert", "github.com/stretchr/testify/require", "github.com/kubeasy-dev/kubeasy-cli/internal/api".

Write the four test functions described in the behavior block. Each test using function vars saves the original and restores with t.Cleanup. Call `submitCmd.RunE(submitCmd, []string{"pod-evicted"})` directly.

Note the naming choice: submit.go already has the package-level `apiGetChallenge` and `apiGetChallengeProgress` vars IF plan 02-01 ran first. Since plans run in parallel (both Wave 1), submit.go must declare its own vars with distinct names to avoid conflict:
- `var apiGetChallengeForSubmit = api.GetChallenge`
- `var apiGetProgressForSubmit = api.GetChallengeProgress`

This naming avoids collision with the vars in start.go. Tests replace only the submit-specific vars.

Run `go test ./cmd/... -run TestSubmitRunE -v` — tests FAIL because vars do not exist yet.

**Step 2: Add function vars to cmd/submit.go**

At package level (after imports, before var submitCmd), add:
```go
var (
    apiGetChallengeForSubmit = api.GetChallenge
    apiGetProgressForSubmit  = api.GetChallengeProgress
)
```

Replace the two direct api.* call sites in RunE:
- Line 32: `_, err := api.GetChallenge(challengeSlug)` → `_, err := apiGetChallengeForSubmit(challengeSlug)`
- Line 44: `progress, err = api.GetChallengeProgress(challengeSlug)` → `progress, err = apiGetProgressForSubmit(challengeSlug)`

**Step 3: Run tests GREEN**

Run `go test ./cmd/... -run TestSubmitRunE -v -race`. All four tests must pass.

**Constraint:** Tests only cover guards at lines 24-62. Do NOT test paths that reach line 82+ (kube.GetKubernetesClient). The three guards — nil progress, completed progress — both return nil. The API failure returns a non-nil error. All are reachable without a cluster.
  </action>
  <verify>
    <automated>cd /Users/paul/Workspace/kubeasy/kubeasy-cli && go test ./cmd/... -run TestSubmitRunE -v -race</automated>
  </verify>
  <done>Four TestSubmitRunE_* tests pass with -race. cmd/submit.go compiles with two function vars. `task test:unit` remains green.</done>
</task>

</tasks>

<verification>
Run `task test:unit` — full unit test suite must pass green.
Run `go test ./cmd/... -v -race` — all cmd tests pass including the four new TestSubmitRunE_* tests.
</verification>

<success_criteria>
- cmd/submit_test.go exists with TestSubmitRunE_InvalidSlug, TestSubmitRunE_ProgressNil, TestSubmitRunE_AlreadyCompleted, TestSubmitRunE_APIFailure
- cmd/submit.go has vars apiGetChallengeForSubmit, apiGetProgressForSubmit at package level
- All four tests pass with `go test ./cmd/... -run TestSubmitRunE -v -race`
- TST-02 requirement satisfied: slug validation, nil/completed progress guards, API failure exercised
</success_criteria>

<output>
After completion, create `.planning/phases/02-command-test-coverage/02-02-SUMMARY.md`
</output>
