Files
logwisp/internal/source/file/file_test.go
T

85 lines
2.4 KiB
Go

package file
import (
"context"
"os"
"path/filepath"
"syscall"
"testing"
"time"
"logwisp/internal/core"
)
func TestStoppedWatcherReturnsNormally(t *testing.T) {
path := filepath.Join(t.TempDir(), "session.jsonl")
if err := os.WriteFile(path, nil, 0o600); err != nil {
t.Fatal(err)
}
watcher := newFileWatcher(path, true, true, func(_ core.LogEntry) {}, nil)
watcher.stop()
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := watcher.watch(ctx); err != nil {
t.Fatalf("stopped watcher returned error: %v", err)
}
}
func TestRemoveWatcherPreservesReplacement(t *testing.T) {
oldWatcher := &fileWatcher{}
replacement := &fileWatcher{}
source := &FileSource{
watchers: map[string]*fileWatcher{
"session.jsonl": replacement,
},
}
source.removeWatcher("session.jsonl", oldWatcher)
if got := source.watchers["session.jsonl"]; got != replacement {
t.Fatalf("replacement watcher = %p, want %p", got, replacement)
}
source.removeWatcher("session.jsonl", replacement)
if _, exists := source.watchers["session.jsonl"]; exists {
t.Fatal("finished watcher was not removed")
}
}
// A rotated file reappears under its archive name with the same inode. Its
// replacement watcher resumes where the original stopped, so a `from = "start"`
// source does not re-emit every record the file already delivered.
func TestRotatedFileResumesInsteadOfReplaying(t *testing.T) {
dir := t.TempDir()
active := filepath.Join(dir, "session.jsonl")
if err := os.WriteFile(active, []byte("one\ntwo\n"), 0o600); err != nil {
t.Fatal(err)
}
info, err := os.Stat(active)
if err != nil {
t.Fatal(err)
}
inode := info.Sys().(*syscall.Stat_t).Ino
archive := filepath.Join(dir, "session_260916_120000.jsonl")
if err := os.Rename(active, archive); err != nil {
t.Fatal(err)
}
for name, watcher := range map[string]*fileWatcher{
"still tailing the renamed inode": {inode: inode, position: 8},
"already moved on from it": {inode: 99, prevInode: inode, prevPosition: 8},
} {
source := &FileSource{watchers: map[string]*fileWatcher{active: watcher}}
position, ok := source.readPosition(archive)
if !ok || position != 8 {
t.Errorf("%s: position = %d, ok = %v, want 8, true", name, position, ok)
}
}
unrelated := &FileSource{watchers: map[string]*fileWatcher{active: {inode: 99}}}
if _, ok := unrelated.readPosition(archive); ok {
t.Error("a file no watcher has read was treated as rotated")
}
}