Skip to content

Commit 35b6e4e

Browse files
committed
test: add unit tests for cli and snapshot packages
1 parent f4b2d7d commit 35b6e4e

3 files changed

Lines changed: 1027 additions & 0 deletions

File tree

internal/cli/edit_helpers_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package cli
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
// ---------------------------------------------------------------------------
10+
// splitBefore (edit.go) — pure string utility
11+
// ---------------------------------------------------------------------------
12+
13+
func TestSplitBefore_WithSeparator(t *testing.T) {
14+
result := splitBefore("my-config — My Config Name", " — ")
15+
assert.Equal(t, "my-config", result)
16+
}
17+
18+
func TestSplitBefore_NoSeparator(t *testing.T) {
19+
result := splitBefore("my-config", " — ")
20+
assert.Equal(t, "my-config", result)
21+
}
22+
23+
func TestSplitBefore_EmptyString(t *testing.T) {
24+
result := splitBefore("", " — ")
25+
assert.Equal(t, "", result)
26+
}
27+
28+
func TestSplitBefore_SeparatorAtStart(t *testing.T) {
29+
result := splitBefore(" — rest", " — ")
30+
assert.Equal(t, "", result)
31+
}
32+
33+
func TestSplitBefore_SeparatorAtEnd(t *testing.T) {
34+
// " — " is at the very end — nothing after separator, but before is the slug.
35+
result := splitBefore("slug — ", " — ")
36+
assert.Equal(t, "slug", result)
37+
}
38+
39+
func TestSplitBefore_MultipleSeparators(t *testing.T) {
40+
// Should split at the FIRST occurrence.
41+
result := splitBefore("a — b — c", " — ")
42+
assert.Equal(t, "a", result)
43+
}
44+
45+
func TestSplitBefore_SingleCharSeparator(t *testing.T) {
46+
result := splitBefore("hello:world", ":")
47+
assert.Equal(t, "hello", result)
48+
}
49+
50+
func TestSplitBefore_StringShorterThanSeparator(t *testing.T) {
51+
result := splitBefore("ab", "abcd")
52+
assert.Equal(t, "ab", result)
53+
}
54+
55+
func TestSplitBefore_IdenticalToSeparator(t *testing.T) {
56+
result := splitBefore(" — ", " — ")
57+
assert.Equal(t, "", result)
58+
}
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package snapshot
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
// ---------------------------------------------------------------------------
13+
// isBrewInstalled
14+
// ---------------------------------------------------------------------------
15+
16+
// TestIsBrewInstalled_ReturnsBool verifies the function returns a bool without panicking.
17+
func TestIsBrewInstalled_ReturnsBool(t *testing.T) {
18+
result := isBrewInstalled()
19+
// We don't know whether brew is installed in the CI environment, but the
20+
// function must not panic and must return a bool.
21+
_ = result
22+
}
23+
24+
// ---------------------------------------------------------------------------
25+
// captureBrewList — when brew is absent, returns empty list
26+
// ---------------------------------------------------------------------------
27+
28+
// TestCaptureBrewList_NoPanic verifies the function does not panic regardless of brew availability.
29+
func TestCaptureBrewList_NoPanic(t *testing.T) {
30+
list, err := captureBrewList("leaves")
31+
// When brew is absent this returns ([]string{}, nil).
32+
// When brew is present and succeeds it returns (names, nil).
33+
// When brew fails it returns ([]string{}, err).
34+
// In all cases the function must not panic.
35+
_ = list
36+
_ = err
37+
}
38+
39+
// ---------------------------------------------------------------------------
40+
// CaptureFormulae / CaptureCasks / CaptureTaps
41+
// ---------------------------------------------------------------------------
42+
43+
// TestCaptureFormulae_NoPanic ensures CaptureFormulae does not panic.
44+
func TestCaptureFormulae_NoPanic(t *testing.T) {
45+
formulae, err := CaptureFormulae()
46+
// Returns empty slice when brew is absent — never an error.
47+
if !isBrewInstalled() {
48+
require.NoError(t, err)
49+
assert.NotNil(t, formulae)
50+
} else {
51+
// With brew installed the list may or may not be empty but must not panic.
52+
_ = formulae
53+
_ = err
54+
}
55+
}
56+
57+
// TestCaptureCasks_NoPanic ensures CaptureCasks does not panic.
58+
func TestCaptureCasks_NoPanic(t *testing.T) {
59+
casks, err := CaptureCasks()
60+
if !isBrewInstalled() {
61+
require.NoError(t, err)
62+
assert.NotNil(t, casks)
63+
} else {
64+
_ = casks
65+
_ = err
66+
}
67+
}
68+
69+
// TestCaptureTaps_NoPanic ensures CaptureTaps does not panic.
70+
func TestCaptureTaps_NoPanic(t *testing.T) {
71+
taps, err := CaptureTaps()
72+
if !isBrewInstalled() {
73+
require.NoError(t, err)
74+
assert.NotNil(t, taps)
75+
} else {
76+
_ = taps
77+
_ = err
78+
}
79+
}
80+
81+
// ---------------------------------------------------------------------------
82+
// CaptureNpm
83+
// ---------------------------------------------------------------------------
84+
85+
// TestCaptureNpm_NoPanic ensures CaptureNpm does not panic.
86+
func TestCaptureNpm_NoPanic(t *testing.T) {
87+
packages, err := CaptureNpm()
88+
// On a machine without npm this returns ([]string{}, nil).
89+
// On a machine with npm this returns installed globals.
90+
// In both cases: must not panic and err must be nil (best-effort capture).
91+
require.NoError(t, err)
92+
assert.NotNil(t, packages)
93+
}
94+
95+
// ---------------------------------------------------------------------------
96+
// CaptureMacOSPrefs
97+
// ---------------------------------------------------------------------------
98+
99+
// TestCaptureMacOSPrefs_NoPanic verifies CaptureMacOSPrefs does not panic.
100+
func TestCaptureMacOSPrefs_NoPanic(t *testing.T) {
101+
prefs, err := CaptureMacOSPrefs()
102+
// On macOS with defaults(1) available this may return real prefs.
103+
// In both cases must not return an error and must not panic.
104+
require.NoError(t, err)
105+
assert.NotNil(t, prefs)
106+
}
107+
108+
// ---------------------------------------------------------------------------
109+
// CaptureGit
110+
// ---------------------------------------------------------------------------
111+
112+
// TestCaptureGit_ReturnsSnap verifies CaptureGit returns a non-nil snapshot.
113+
func TestCaptureGit_ReturnsSnap(t *testing.T) {
114+
snap, err := CaptureGit()
115+
require.NoError(t, err)
116+
require.NotNil(t, snap)
117+
// UserName and UserEmail may be empty strings on a machine with no git config —
118+
// that is valid. The function must not error.
119+
}
120+
121+
// TestCaptureGit_FieldsAreStrings verifies the returned fields are strings (not nil).
122+
func TestCaptureGit_FieldsAreStrings(t *testing.T) {
123+
snap, err := CaptureGit()
124+
require.NoError(t, err)
125+
126+
// These are plain string fields — no nil check needed, but we assert they
127+
// don't contain unexpected whitespace from mis-trimmed output.
128+
assert.NotContains(t, snap.UserName, "\n")
129+
assert.NotContains(t, snap.UserEmail, "\n")
130+
}
131+
132+
// ---------------------------------------------------------------------------
133+
// CaptureDevTools
134+
// ---------------------------------------------------------------------------
135+
136+
// TestCaptureDevTools_ReturnsSlice verifies CaptureDevTools returns a slice (possibly empty).
137+
func TestCaptureDevTools_ReturnsSlice(t *testing.T) {
138+
tools, err := CaptureDevTools()
139+
require.NoError(t, err)
140+
assert.NotNil(t, tools)
141+
}
142+
143+
// TestCaptureDevTools_VersionsNotEmpty verifies any captured tool has a non-empty name.
144+
func TestCaptureDevTools_VersionsNotEmpty(t *testing.T) {
145+
tools, err := CaptureDevTools()
146+
require.NoError(t, err)
147+
for _, dt := range tools {
148+
assert.NotEmpty(t, dt.Name, "captured DevTool must have a name")
149+
}
150+
}
151+
152+
// ---------------------------------------------------------------------------
153+
// CaptureDotfiles — isolation scenarios
154+
// ---------------------------------------------------------------------------
155+
156+
// TestCaptureDotfiles_EmptyHome verifies empty snapshot when HOME has no .dotfiles.
157+
func TestCaptureDotfiles_EmptyHome(t *testing.T) {
158+
tmpDir := t.TempDir()
159+
t.Setenv("HOME", tmpDir)
160+
161+
snap, err := CaptureDotfiles()
162+
require.NoError(t, err)
163+
require.NotNil(t, snap)
164+
// No .dotfiles directory → RepoURL should be empty.
165+
assert.Empty(t, snap.RepoURL)
166+
}
167+
168+
// TestCaptureDotfiles_DotfilesDirMissingGitSubdir verifies empty snapshot when .dotfiles has no .git.
169+
func TestCaptureDotfiles_DotfilesDirMissingGitSubdir(t *testing.T) {
170+
tmpDir := t.TempDir()
171+
t.Setenv("HOME", tmpDir)
172+
173+
// Create .dotfiles directory without a .git subdirectory.
174+
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, ".dotfiles"), 0755))
175+
176+
snap, err := CaptureDotfiles()
177+
require.NoError(t, err)
178+
require.NotNil(t, snap)
179+
// No .git → RepoURL should be empty.
180+
assert.Empty(t, snap.RepoURL)
181+
}
182+
183+
// ---------------------------------------------------------------------------
184+
// Capture — top-level function
185+
// ---------------------------------------------------------------------------
186+
187+
// TestCapture_ReturnsSnapshotWithoutError verifies the top-level Capture function
188+
// returns a complete Snapshot and does not error in a normal environment.
189+
func TestCapture_ReturnsSnapshotWithoutError(t *testing.T) {
190+
tmpDir := t.TempDir()
191+
t.Setenv("HOME", tmpDir)
192+
193+
snap, err := Capture()
194+
require.NoError(t, err)
195+
require.NotNil(t, snap)
196+
assert.Equal(t, 1, snap.Version)
197+
assert.NotEmpty(t, snap.Hostname)
198+
// Packages may be empty in CI but must not be nil.
199+
assert.NotNil(t, snap.Packages.Formulae)
200+
assert.NotNil(t, snap.Packages.Casks)
201+
assert.NotNil(t, snap.Packages.Npm)
202+
assert.NotNil(t, snap.DevTools)
203+
}
204+
205+
// TestCapture_CapturedAtIsRecent verifies CapturedAt is set to a recent time.
206+
func TestCapture_CapturedAtIsRecent(t *testing.T) {
207+
tmpDir := t.TempDir()
208+
t.Setenv("HOME", tmpDir)
209+
210+
snap, err := Capture()
211+
require.NoError(t, err)
212+
213+
assert.False(t, snap.CapturedAt.IsZero(), "CapturedAt must be set")
214+
}

0 commit comments

Comments
 (0)