Skip to content

Commit b3bb7d9

Browse files
committed
### 1. Extended File Utility Tests ( internal/util/file.go ) -> 100.0% Coverage
Added new test cases to file_test.go covering: • Special Perm Symbolic Output: Tested all branch cases for named pipes, sockets, character devices, block devices, and SetUID/SetGID/Sticky bits with and without execute bits. • Owner IDs Edge Cases: Tested inputs where the file stat is nil , or the system property stat.Sys() returns nil or an unexpected type. • Lookup Error Paths: Tested invalid/non-existent user and group IDs to verify that LookupUserName and LookupGroupName successfully handle and bubble up errors. ### 2. UI Layout Helper Tests ( internal/ui/util/window.go ) -> 100.0% Coverage • Created window_test.go and added direct unit test coverage for SetupWindow and SetupDialogWindow on tview.Box to ensure window title configurations, alignment, and border themes are properly applied. ### 3. Loading Container Tests ( internal/ui/util/loading_container.go ) -> Increased Coverage • Expanded loading_container_test.go to verify SetBorderColor (for both tview.Box and tview.Flex child components) and SetMessage . ### 4. File Watcher Tests ( internal/util/file_watcher.go ) -> Introduced Coverage (~75%) • Created file_watcher_test.go to test FileWatcher events in both standard and recursive modes using temporary directories, validating file creation callbacks and proper cleanup routines when Stop() is called.
1 parent 9767461 commit b3bb7d9

4 files changed

Lines changed: 201 additions & 0 deletions

File tree

internal/ui/util/loading_container_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,28 @@ func TestLoadingView_EdgeCases(t *testing.T) {
7474
loadingViewNilApp.Start()
7575
time.Sleep(150 * time.Millisecond)
7676
loadingViewNilApp.Stop()
77+
78+
// Test SetMessage
79+
loadingView.SetMessage("New message")
80+
assert.Equal(t, "New message", loadingView.message)
81+
}
82+
83+
func TestLoadingContainer_SetBorderColorAndMessage(t *testing.T) {
84+
app := tview.NewApplication()
85+
86+
// Content is a Box
87+
boxContent := tview.NewBox()
88+
containerBox := NewLoadingContainer(app, boxContent, "Title", "Msg")
89+
containerBox.SetBorderColor(tcell.ColorBlue)
90+
assert.Equal(t, tcell.ColorBlue, boxContent.GetBorderColor())
91+
92+
// Content is a Flex
93+
flexContent := tview.NewFlex()
94+
containerFlex := NewLoadingContainer(app, flexContent, "Title", "Msg")
95+
containerFlex.SetBorderColor(tcell.ColorRed)
96+
assert.Equal(t, tcell.ColorRed, flexContent.GetBorderColor())
97+
98+
// Test SetMessage
99+
containerBox.SetMessage("Another message")
100+
assert.Equal(t, "Another message", containerBox.loadingView.message)
77101
}

internal/ui/util/window_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package util
2+
3+
import (
4+
"testing"
5+
"zfs-file-history/internal/ui/theme"
6+
7+
"github.com/rivo/tview"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestSetupWindow(t *testing.T) {
12+
box := tview.NewBox()
13+
box.SetBorder(true)
14+
SetupWindow(box, "My Window Title")
15+
16+
assert.Equal(t, theme.CreateTitleText("My Window Title"), box.GetTitle())
17+
assert.Equal(t, theme.Colors.Layout.Border, box.GetBorderColor())
18+
}
19+
20+
func TestSetupDialogWindow(t *testing.T) {
21+
box := tview.NewBox()
22+
box.SetBorder(true)
23+
SetupDialogWindow(box, "My Dialog Title")
24+
25+
assert.Equal(t, theme.CreateTitleText("My Dialog Title"), box.GetTitle())
26+
assert.Equal(t, theme.Colors.Dialog.Border, box.GetBorderColor())
27+
}

internal/util/file_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,81 @@ func TestLookupCurrentUserAndGroupName(t *testing.T) {
152152
t.Fatalf("LookupGroupName(%d) returned empty group name", gid)
153153
}
154154
}
155+
156+
func TestUnixPermSymbolic_SpecialFiles(t *testing.T) {
157+
t.Parallel()
158+
159+
tests := []struct {
160+
name string
161+
mode os.FileMode
162+
want string
163+
}{
164+
{name: "named pipe", mode: os.ModeNamedPipe | 0o600, want: "prw-------"},
165+
{name: "socket", mode: os.ModeSocket | 0o666, want: "srw-rw-rw-"},
166+
{name: "char device", mode: os.ModeDevice | os.ModeCharDevice | 0o600, want: "crw-------"},
167+
{name: "block device", mode: os.ModeDevice | 0o600, want: "brw-------"},
168+
{name: "setuid with execute", mode: os.ModeSetuid | 0o700, want: "-rws------"},
169+
{name: "setuid without execute", mode: os.ModeSetuid | 0o600, want: "-rwS------"},
170+
{name: "setgid with execute", mode: os.ModeSetgid | 0o070, want: "----rws---"},
171+
{name: "setgid without execute", mode: os.ModeSetgid | 0o060, want: "----rwS---"},
172+
{name: "sticky with execute", mode: os.ModeSticky | 0o007, want: "-------rwt"},
173+
{name: "sticky without execute", mode: os.ModeSticky | 0o006, want: "-------rwT"},
174+
}
175+
176+
for _, tt := range tests {
177+
tt := tt
178+
t.Run(tt.name, func(t *testing.T) {
179+
t.Parallel()
180+
if got := UnixPermSymbolic(tt.mode); got != tt.want {
181+
t.Fatalf("UnixPermSymbolic() = %q, want %q", got, tt.want)
182+
}
183+
})
184+
}
185+
}
186+
187+
type dummyFileInfo struct {
188+
os.FileInfo
189+
sys interface{}
190+
}
191+
192+
func (d *dummyFileInfo) Sys() interface{} {
193+
return d.sys
194+
}
195+
196+
func TestUnixOwnerIDs_EdgeCases(t *testing.T) {
197+
t.Parallel()
198+
199+
// stat is nil
200+
uid, gid, ok := UnixOwnerIDs(nil)
201+
assert.False(t, ok)
202+
assert.Zero(t, uid)
203+
assert.Zero(t, gid)
204+
205+
// stat.Sys() is nil
206+
uid, gid, ok = UnixOwnerIDs(&dummyFileInfo{sys: nil})
207+
assert.False(t, ok)
208+
assert.Zero(t, uid)
209+
assert.Zero(t, gid)
210+
211+
// stat.Sys() is wrong type
212+
uid, gid, ok = UnixOwnerIDs(&dummyFileInfo{sys: "not-stat_t"})
213+
assert.False(t, ok)
214+
assert.Zero(t, uid)
215+
assert.Zero(t, gid)
216+
}
217+
218+
func TestLookupUserName_Error(t *testing.T) {
219+
t.Parallel()
220+
// Lookup a highly unlikely UID
221+
username, err := LookupUserName(999999)
222+
assert.Error(t, err)
223+
assert.Empty(t, username)
224+
}
225+
226+
func TestLookupGroupName_Error(t *testing.T) {
227+
t.Parallel()
228+
// Lookup a highly unlikely GID
229+
groupName, err := LookupGroupName(999999)
230+
assert.Error(t, err)
231+
assert.Empty(t, groupName)
232+
}

internal/util/file_watcher_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package util
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
"time"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestFileWatcher(t *testing.T) {
13+
tempDir := t.TempDir()
14+
15+
fw := NewFileWatcher(tempDir)
16+
assert.Equal(t, tempDir, fw.RootPath)
17+
18+
eventChan := make(chan string, 10)
19+
action := func(path string) {
20+
eventChan <- path
21+
}
22+
23+
err := fw.Watch(action)
24+
assert.NoError(t, err)
25+
26+
// Write a new file
27+
testFile := filepath.Join(tempDir, "test.txt")
28+
err = os.WriteFile(testFile, []byte("hello"), 0644)
29+
assert.NoError(t, err)
30+
31+
// Wait for event or timeout
32+
select {
33+
case eventPath := <-eventChan:
34+
assert.NotEmpty(t, eventPath)
35+
case <-time.After(3 * time.Second):
36+
t.Fatal("timed out waiting for file watcher event")
37+
}
38+
39+
fw.Stop()
40+
time.Sleep(100 * time.Millisecond)
41+
}
42+
43+
func TestFileWatcherRecursive(t *testing.T) {
44+
tempDir := t.TempDir()
45+
subDir := filepath.Join(tempDir, "subdir")
46+
err := os.Mkdir(subDir, 0755)
47+
assert.NoError(t, err)
48+
49+
fw := NewFileWatcher(tempDir)
50+
eventChan := make(chan string, 10)
51+
action := func(path string) {
52+
eventChan <- path
53+
}
54+
55+
fw.WatchRecursive(action)
56+
57+
// Write a new file in subdir
58+
testFile := filepath.Join(subDir, "test.txt")
59+
err = os.WriteFile(testFile, []byte("hello"), 0644)
60+
assert.NoError(t, err)
61+
62+
// Wait for event or timeout
63+
select {
64+
case eventPath := <-eventChan:
65+
assert.Contains(t, eventPath, "subdir")
66+
case <-time.After(3 * time.Second):
67+
t.Fatal("timed out waiting for recursive file watcher event")
68+
}
69+
70+
fw.Stop()
71+
time.Sleep(100 * time.Millisecond)
72+
}

0 commit comments

Comments
 (0)