Initial UNC file watcher web console

This commit is contained in:
Codex
2026-04-17 17:08:49 +08:00
commit 1d283d540c
10 changed files with 1931 additions and 0 deletions
+214
View File
@@ -0,0 +1,214 @@
package main
import (
"os"
"path/filepath"
"reflect"
"testing"
"time"
)
func TestExtractedDirName(t *testing.T) {
t.Parallel()
tests := []struct {
name string
in string
want string
}{
{name: "plain", in: "demo.tar.gz", want: "demo"},
{name: "mixed case suffix", in: "Demo.TAR.GZ", want: "Demo"},
{name: "other name", in: "demo.zip", want: "demo.zip"},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := extractedDirName(tt.in)
if got != tt.want {
t.Fatalf("extractedDirName(%q) = %q, want %q", tt.in, got, tt.want)
}
})
}
}
func TestIsStableEnough(t *testing.T) {
t.Parallel()
now := time.Date(2026, 4, 17, 12, 0, 0, 0, time.UTC)
cfg := config{
stableFor: 30 * time.Second,
stableScans: 3,
}
tests := []struct {
name string
stableCount int
modTime time.Time
want bool
}{
{name: "not enough scans", stableCount: 2, modTime: now.Add(-1 * time.Minute), want: false},
{name: "not enough age", stableCount: 3, modTime: now.Add(-20 * time.Second), want: false},
{name: "enough scans and age", stableCount: 3, modTime: now.Add(-45 * time.Second), want: true},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := isStableEnough(cfg, tt.stableCount, fileSnapshot{modTime: tt.modTime}, now)
if got != tt.want {
t.Fatalf("isStableEnough(...) = %t, want %t", got, tt.want)
}
})
}
}
func TestParseExtensions(t *testing.T) {
t.Parallel()
got, err := parseExtensions(".csv, txt;PDF \n tar.gz")
if err != nil {
t.Fatalf("parseExtensions() error = %v", err)
}
want := []string{".csv", ".txt", ".pdf", ".tar.gz"}
if !reflect.DeepEqual(got, want) {
t.Fatalf("parseExtensions() = %#v, want %#v", got, want)
}
}
func TestEvaluateGenericDuplicateNameSize(t *testing.T) {
t.Parallel()
dir := t.TempDir()
sourcePath := filepath.Join(dir, "source.csv")
targetPath := filepath.Join(dir, "target.csv")
if err := os.WriteFile(sourcePath, []byte("abcd"), 0o644); err != nil {
t.Fatalf("write source error = %v", err)
}
if err := os.WriteFile(targetPath, []byte("wxyz"), 0o644); err != nil {
t.Fatalf("write target error = %v", err)
}
decision, duplicate, err := evaluateGenericDuplicate(sourcePath, targetPath, genericDedupeNameSize)
if err != nil {
t.Fatalf("evaluateGenericDuplicate() error = %v", err)
}
if !duplicate {
t.Fatalf("expected same-size file to be treated as duplicate")
}
if decision == "" {
t.Fatalf("expected non-empty decision")
}
}
func TestEvaluateGenericDuplicateNameHash(t *testing.T) {
t.Parallel()
dir := t.TempDir()
sourcePath := filepath.Join(dir, "source.csv")
targetPath := filepath.Join(dir, "target.csv")
if err := os.WriteFile(sourcePath, []byte("same-content"), 0o644); err != nil {
t.Fatalf("write source error = %v", err)
}
if err := os.WriteFile(targetPath, []byte("same-content"), 0o644); err != nil {
t.Fatalf("write target error = %v", err)
}
_, duplicate, err := evaluateGenericDuplicate(sourcePath, targetPath, genericDedupeNameHash)
if err != nil {
t.Fatalf("evaluateGenericDuplicate() error = %v", err)
}
if !duplicate {
t.Fatalf("expected same-hash file to be treated as duplicate")
}
if err := os.WriteFile(targetPath, []byte("DIFF-content"), 0o644); err != nil {
t.Fatalf("rewrite target error = %v", err)
}
_, duplicate, err = evaluateGenericDuplicate(sourcePath, targetPath, genericDedupeNameHash)
if err != nil {
t.Fatalf("evaluateGenericDuplicate() error = %v", err)
}
if duplicate {
t.Fatalf("expected different-hash file not to be treated as duplicate")
}
}
func TestSaveAndLoadSettings(t *testing.T) {
t.Parallel()
settingsPath := filepath.Join(t.TempDir(), "settings.json")
want := formValues{
ArchiveEnabled: true,
ArchiveSourceDir: `\\server\share\incoming`,
ArchiveZipDir: `D:\zip`,
ArchiveUnzipDir: `D:\unzip`,
GenericEnabled: true,
GenericSourceDir: `\\server\share\others`,
GenericTargetDir: `D:\files`,
GenericExts: ".csv,.txt",
GenericDedupe: genericDedupeNameHash,
Interval: "15s",
StableFor: "1m",
StableScans: "3",
Mode: "move",
Recursive: true,
}
if err := saveSettings(settingsPath, want); err != nil {
t.Fatalf("saveSettings() error = %v", err)
}
got, err := loadSettings(settingsPath)
if err != nil {
t.Fatalf("loadSettings() error = %v", err)
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("loadSettings() = %#v, want %#v", got, want)
}
}
func TestLoadLegacySettings(t *testing.T) {
t.Parallel()
settingsPath := filepath.Join(t.TempDir(), "legacy.json")
legacyJSON := `{
"source_dir": "\\\\server\\share\\incoming",
"zip_dir": "D:\\zip",
"unzip_dir": "D:\\unzip",
"interval": "15s",
"stable_for": "1m",
"stable_scans": "3",
"mode": "copy",
"recursive": true
}`
if err := os.WriteFile(settingsPath, []byte(legacyJSON), 0o644); err != nil {
t.Fatalf("write legacy settings error = %v", err)
}
got, err := loadSettings(settingsPath)
if err != nil {
t.Fatalf("loadSettings() error = %v", err)
}
if !got.ArchiveEnabled {
t.Fatalf("expected archive rule to be enabled for legacy settings")
}
if got.ArchiveSourceDir != `\\server\share\incoming` {
t.Fatalf("unexpected archive source = %q", got.ArchiveSourceDir)
}
if got.GenericEnabled {
t.Fatalf("did not expect generic rule to be enabled for legacy settings")
}
if got.GenericDedupe != genericDedupeNameSize {
t.Fatalf("expected legacy settings to default to name_size dedupe")
}
}