package main import ( "bytes" "encoding/json" "os" "path/filepath" "strings" "testing" ) type profileListOutput struct { Profiles []string `json:"profiles"` } func withProfilesDir(t *testing.T, names ...string) { t.Helper() dir := t.TempDir() for _, n := range names { body := `{"name":"` + n + `","zones":[]}` if err := os.WriteFile(filepath.Join(dir, n+".json"), []byte(body), 0644); err != nil { t.Fatalf("write profile %s: %v", n, err) } } orig := profilesDirOverride profilesDirOverride = dir t.Cleanup(func() { profilesDirOverride = orig }) } func runProfileList(t *testing.T) string { t.Helper() var out bytes.Buffer cmd := NewProfileListCmd() cmd.SetOut(&out) cmd.SetErr(&out) if err := cmd.Execute(); err != nil { t.Fatalf("list returned error: %v", err) } return out.String() } // README.md advertises JSON for `list`. The shape must not change with the // number of profiles on disk. func TestProfileListEmitsJSONWithProfiles(t *testing.T) { withJSON(t) withProfilesDir(t, "desk", "movie") got := runProfileList(t) var parsed profileListOutput if err := json.Unmarshal([]byte(got), &parsed); err != nil { t.Fatalf("list output is not valid JSON: %v (output %q)", err, got) } if len(parsed.Profiles) != 2 { t.Errorf("profiles = %v, want 2 entries", parsed.Profiles) } } func TestProfileListEmitsJSONWithoutProfiles(t *testing.T) { withJSON(t) withProfilesDir(t) got := runProfileList(t) var parsed profileListOutput if err := json.Unmarshal([]byte(got), &parsed); err != nil { t.Fatalf("list output is not valid JSON: %v (output %q)", err, got) } if parsed.Profiles == nil { t.Error("profiles = null, want an empty array so consumers can iterate unconditionally") } } func TestProfileListUsesTheSharedJSONShape(t *testing.T) { withJSON(t) withProfilesDir(t, "desk") got := runProfileList(t) if !strings.Contains(got, "\n \"profiles\": [") { t.Errorf("list output = %q, want the same two-space indent as the other JSON commands", got) } }