Files
simslim-mirror/profile_file_test.go
Interlap ffd3a13d34 refactor: split into an importable library and a cmd/simslim CLI (#14)
The repo root becomes `package simslim`, an importable library holding every
piece of slimming logic; the CLI moves to `cmd/simslim`. Previously everything
was `package main`, so nothing here could be imported at all.

The library carries no external dependencies — urfave/cli stays on the CLI side,
along with all terminal-facing code: the interactive profile wizard, writeJSON,
truncate/humanBytes, fatal and usage. The library never writes to stdout; it
returns values and reports progress through the Reporter callback.

Renames forced by collisions with existing types: status() -> ReadStatus,
statusForDevice() -> ReadStatusForDevice, diskCleanupPlan() -> PlanDiskCleanup.
The shutdown/boot timeouts move to simctl.go as ShutdownTimeout/BootTimeout.

`go install` gains a path element: github.com/mobai-app/simslim/cmd/simslim.
The installed binary is still named simslim, and -X main.version still applies.
packaging/homebrew/simslim.rb.in is updated to match, since std_go_args passes
no package path.

Verified live against a real simulator: list, profiles, status, doctor, size,
disk-plan, measure, boot, shutdown, on, off, on --except, on --profile, clone,
rename, delete and the interactive wizard all behave identically to the previous
build. JSON output, human output, help text and exit codes are unchanged.
2026-07-23 12:39:20 +02:00

133 lines
4.0 KiB
Go

package simslim
import (
"os"
"path/filepath"
"reflect"
"testing"
)
// writeProfile writes contents to a temp file and returns its path.
func writeProfile(t *testing.T, contents string) string {
t.Helper()
path := filepath.Join(t.TempDir(), "profile.json")
if err := os.WriteFile(path, []byte(contents), 0o600); err != nil {
t.Fatalf("write profile: %v", err)
}
return path
}
func TestLoadSlimProfile(t *testing.T) {
path := writeProfile(t, `{
"name": "ci",
"description": "UI test runs",
"except": ["search", "store"],
"keep": ["com.apple.apsd"]
}`)
p, err := LoadSlimProfile(path)
if err != nil {
t.Fatalf("LoadSlimProfile() error = %v", err)
}
wantExcept := map[string]bool{"search": true, "store": true}
if !reflect.DeepEqual(p.ExceptCategories, wantExcept) {
t.Errorf("ExceptCategories = %v, want %v", p.ExceptCategories, wantExcept)
}
wantKeep := map[string]bool{"com.apple.apsd": true}
if !reflect.DeepEqual(p.Keep, wantKeep) {
t.Errorf("Keep = %v, want %v", p.Keep, wantKeep)
}
}
func TestLoadSlimProfileMinimal(t *testing.T) {
// name/description are optional and empty arrays yield an empty selection.
p, err := LoadSlimProfile(writeProfile(t, `{}`))
if err != nil {
t.Fatalf("LoadSlimProfile() error = %v", err)
}
if len(p.ExceptCategories) != 0 || len(p.Keep) != 0 {
t.Errorf("empty profile selected %v / %v, want empty", p.ExceptCategories, p.Keep)
}
}
func TestLoadSlimProfileRejects(t *testing.T) {
tests := []struct {
name string
contents string
}{
{name: "unknown category", contents: `{"except": ["nope"]}`},
{name: "unknown keep label", contents: `{"keep": ["com.apple.does-not-exist"]}`},
{name: "unknown field", contents: `{"disable": ["search"]}`},
{name: "malformed json", contents: `{"except": [`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if _, err := LoadSlimProfile(writeProfile(t, tt.contents)); err == nil {
t.Errorf("LoadSlimProfile(%q) error = nil, want error", tt.contents)
}
})
}
}
func TestLoadSlimProfileMissingFile(t *testing.T) {
if _, err := LoadSlimProfile(filepath.Join(t.TempDir(), "absent.json")); err == nil {
t.Error("LoadSlimProfile(absent) error = nil, want error")
}
}
func TestBuildProfile(t *testing.T) {
t.Run("flags build the profile", func(t *testing.T) {
p, err := BuildProfile("", "search", "com.apple.apsd")
if err != nil {
t.Fatalf("BuildProfile() error = %v", err)
}
if !p.ExceptCategories["search"] || !p.Keep["com.apple.apsd"] {
t.Errorf("buildProfile flags = %v / %v", p.ExceptCategories, p.Keep)
}
})
t.Run("unknown category flag errors", func(t *testing.T) {
if _, err := BuildProfile("", "nope", ""); err == nil {
t.Error("BuildProfile(unknown category) error = nil, want error")
}
})
t.Run("profile file resolves", func(t *testing.T) {
path := writeProfile(t, `{"except": ["search"]}`)
p, err := BuildProfile(path, "", "")
if err != nil {
t.Fatalf("BuildProfile(file) error = %v", err)
}
if !p.ExceptCategories["search"] {
t.Errorf("BuildProfile(file) except = %v", p.ExceptCategories)
}
})
t.Run("profile with except flag errors", func(t *testing.T) {
if _, err := BuildProfile("some.json", "search", ""); err == nil {
t.Error("BuildProfile(profile+except) error = nil, want error")
}
})
t.Run("profile with keep flag errors", func(t *testing.T) {
if _, err := BuildProfile("some.json", "", "com.apple.apsd"); err == nil {
t.Error("BuildProfile(profile+keep) error = nil, want error")
}
})
}
func TestProfileFileName(t *testing.T) {
tests := []struct {
name string
want string
}{
{name: "ci", want: "ci.json"},
{name: "UI Test Runs", want: "ui-test-runs.json"},
{name: "", want: "profile.json"},
{name: " ", want: "profile.json"},
{name: "prod.json", want: "prod.json"},
{name: "a/b:c", want: "a-b-c.json"},
{name: "--weird--", want: "weird.json"},
}
for _, tt := range tests {
if got := ProfileFileName(tt.name); got != tt.want {
t.Errorf("ProfileFileName(%q) = %q, want %q", tt.name, got, tt.want)
}
}
}