-
Notifications
You must be signed in to change notification settings - Fork 44
test: improve cmd coverage from 63.4% to 67.6% #68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "net/http" | ||
| "net/http/httptest" | ||
| "strings" | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestWriteJSON_WritesIndentedJSONAndContentType(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| payload map[string]interface{} | ||
| }{ | ||
| { | ||
| name: "simple map", | ||
| payload: map[string]interface{}{ | ||
| "status": "ok", | ||
| "count": 2, | ||
| }, | ||
| }, | ||
| { | ||
| name: "nested payload", | ||
| payload: map[string]interface{}{ | ||
| "outer": map[string]interface{}{"inner": true}, | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| rr := httptest.NewRecorder() | ||
| writeJSON(rr, tt.payload) | ||
|
|
||
| if got := rr.Header().Get("Content-Type"); got != "application/json" { | ||
| t.Fatalf("expected content-type application/json, got %q", got) | ||
| } | ||
| if rr.Code != http.StatusOK { | ||
| t.Fatalf("expected status 200, got %d", rr.Code) | ||
| } | ||
| if !strings.Contains(rr.Body.String(), "\n ") { | ||
| t.Fatalf("expected indented JSON output, got %q", rr.Body.String()) | ||
| } | ||
|
|
||
| var decoded map[string]interface{} | ||
| if err := json.Unmarshal(rr.Body.Bytes(), &decoded); err != nil { | ||
| t.Fatalf("expected valid JSON body: %v", err) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestWriteError_WritesStatusJSONAndContentType(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| code int | ||
| msg string | ||
| wantCode int | ||
| wantSubstr string | ||
| }{ | ||
| {name: "bad request", code: http.StatusBadRequest, msg: "bad input", wantCode: http.StatusBadRequest, wantSubstr: "bad input"}, | ||
| {name: "internal server error", code: http.StatusInternalServerError, msg: "boom", wantCode: http.StatusInternalServerError, wantSubstr: "boom"}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| rr := httptest.NewRecorder() | ||
| writeError(rr, tt.code, tt.msg) | ||
|
|
||
| if rr.Code != tt.wantCode { | ||
| t.Fatalf("expected status %d, got %d", tt.wantCode, rr.Code) | ||
| } | ||
| if got := rr.Header().Get("Content-Type"); got != "application/json" { | ||
| t.Fatalf("expected content-type application/json, got %q", got) | ||
| } | ||
|
|
||
| var decoded map[string]string | ||
| if err := json.Unmarshal(rr.Body.Bytes(), &decoded); err != nil { | ||
| t.Fatalf("expected valid JSON body: %v", err) | ||
| } | ||
| if decoded["error"] != tt.msg { | ||
| t.Fatalf("expected error message %q, got %q", tt.msg, decoded["error"]) | ||
| } | ||
| if !strings.Contains(rr.Body.String(), tt.wantSubstr) { | ||
| t.Fatalf("expected body to contain %q, got %q", tt.wantSubstr, rr.Body.String()) | ||
| } | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestRunSkill_PrintsUsageForUnknownOrMissingSubcommand(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| args []string | ||
| }{ | ||
| {name: "no arguments", args: nil}, | ||
| {name: "unknown subcommand", args: []string{"bogus"}}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| out := captureOutput(func() { | ||
| RunSkill(tt.args, t.TempDir()) | ||
| }) | ||
|
|
||
| checks := []string{ | ||
| "Usage: codemap skill <list|show|init>", | ||
| "Commands:", | ||
| "list", | ||
| "show <name>", | ||
| "init", | ||
| } | ||
| for _, check := range checks { | ||
| if !strings.Contains(out, check) { | ||
| t.Fatalf("expected output to contain %q, got:\n%s", check, out) | ||
| } | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestRunSkillList_PrintsBuiltinSkills(t *testing.T) { | ||
| root := t.TempDir() | ||
|
|
||
| out := captureOutput(func() { | ||
| runSkillList(root) | ||
| }) | ||
|
Comment on lines
+41
to
+46
|
||
|
|
||
| checks := []string{ | ||
| "Available skills", | ||
| "[builtin]", | ||
| } | ||
| for _, check := range checks { | ||
| if !strings.Contains(out, check) { | ||
| t.Fatalf("expected output to contain %q, got:\n%s", check, out) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func TestRunSkillShow_PrintsBuiltinSkillDetails(t *testing.T) { | ||
| root := t.TempDir() | ||
|
|
||
| out := captureOutput(func() { | ||
| runSkillShow(root, "explore") | ||
| }) | ||
|
|
||
| checks := []string{ | ||
| "# explore", | ||
| "Source: builtin", | ||
| "Description:", | ||
| } | ||
| for _, check := range checks { | ||
| if !strings.Contains(out, check) { | ||
| t.Fatalf("expected output to contain %q, got:\n%s", check, out) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func TestRunSkillInit_CreatesTemplateAndIsIdempotent(t *testing.T) { | ||
| root := t.TempDir() | ||
| path := filepath.Join(root, ".codemap", "skills", "my-skill.md") | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| run func() string | ||
| wantContains []string | ||
| }{ | ||
| { | ||
| name: "first run creates template", | ||
| run: func() string { | ||
| return captureOutput(func() { | ||
| runSkillInit(root) | ||
| }) | ||
| }, | ||
| wantContains: []string{ | ||
| "Created skill template", | ||
| "Run 'codemap skill list'", | ||
| }, | ||
| }, | ||
| { | ||
| name: "second run reports already exists", | ||
| run: func() string { | ||
| return captureOutput(func() { | ||
| runSkillInit(root) | ||
| }) | ||
| }, | ||
| wantContains: []string{ | ||
| "Skill template already exists", | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| out := tt.run() | ||
| for _, check := range tt.wantContains { | ||
| if !strings.Contains(out, check) { | ||
| t.Fatalf("expected output to contain %q, got:\n%s", check, out) | ||
| } | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| data, err := os.ReadFile(path) | ||
| if err != nil { | ||
| t.Fatalf("expected template file at %s: %v", path, err) | ||
| } | ||
| if !strings.Contains(string(data), "name: my-skill") { | ||
| t.Fatalf("expected template content in %s, got:\n%s", path, string(data)) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The field name
wantPrefixis a bit misleading here: the assertion checks for an exact match (p == tt.wantPrefix) rather than a prefix. Consider renaming it to something likewantPath/wantEntry(or update the assertion to actually check a prefix) to keep the test intent clear.