Files
viettranx a8f0f05600 test(config,skills,backup,mcp): wave 2 integration tests
Add Wave 2 package coverage:
- config/config_extras_test.go: NormalizeAgentID, ExpandHome, ResolveAgent, env overlays
- skills/search_bm25_test.go: BM25 index/search/ranking
- skills/loader_test.go: SKILL.md loader, frontmatter, dedup, versioning
- backup/fs_archive_test.go: ArchiveDirectory, skip fn, tar prefix
- backup/manifest_dsn_test.go: SanitizeDSN, ParseDSN, WritePgpass, CleanEnv
- mcp/util_bm25_test.go: pool, manager, bridge BM25, env resolution
2026-04-11 21:22:23 +07:00

226 lines
5.7 KiB
Go

package backup
import (
"archive/tar"
"bytes"
"io"
"os"
"path/filepath"
"strings"
"testing"
)
// readTarEntries reads all entries from a tar archive into a map[name]content.
func readTarEntries(t *testing.T, data []byte) map[string]string {
t.Helper()
tr := tar.NewReader(bytes.NewReader(data))
entries := make(map[string]string)
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("tar read error: %v", err)
}
content, _ := io.ReadAll(tr)
entries[hdr.Name] = string(content)
}
return entries
}
// --- ArchiveDirectory ---
func TestArchiveDirectory_EmptyDir(t *testing.T) {
src := t.TempDir()
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
files, bytes, err := ArchiveDirectory(tw, src, "workspace", nil)
tw.Close()
if err != nil {
t.Fatalf("ArchiveDirectory on empty dir: %v", err)
}
if files != 0 {
t.Errorf("expected 0 files, got %d", files)
}
if bytes != 0 {
t.Errorf("expected 0 bytes, got %d", bytes)
}
}
func TestArchiveDirectory_SingleFile(t *testing.T) {
src := t.TempDir()
content := "hello world content"
os.WriteFile(filepath.Join(src, "hello.txt"), []byte(content), 0644)
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
files, totalBytes, err := ArchiveDirectory(tw, src, "ws", nil)
tw.Close()
if err != nil {
t.Fatalf("error: %v", err)
}
if files != 1 {
t.Errorf("expected 1 file, got %d", files)
}
if totalBytes != int64(len(content)) {
t.Errorf("expected %d bytes, got %d", len(content), totalBytes)
}
entries := readTarEntries(t, buf.Bytes())
if _, ok := entries["ws/hello.txt"]; !ok {
t.Errorf("expected ws/hello.txt in tar, got keys: %v", keys(entries))
}
if entries["ws/hello.txt"] != content {
t.Errorf("content mismatch: got %q", entries["ws/hello.txt"])
}
}
func TestArchiveDirectory_NestedDirs(t *testing.T) {
src := t.TempDir()
os.MkdirAll(filepath.Join(src, "subdir"), 0755)
os.WriteFile(filepath.Join(src, "top.txt"), []byte("top"), 0644)
os.WriteFile(filepath.Join(src, "subdir", "nested.txt"), []byte("nested"), 0644)
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
files, _, err := ArchiveDirectory(tw, src, "data", nil)
tw.Close()
if err != nil {
t.Fatalf("error: %v", err)
}
if files != 2 {
t.Errorf("expected 2 files, got %d", files)
}
entries := readTarEntries(t, buf.Bytes())
if _, ok := entries["data/top.txt"]; !ok {
t.Errorf("missing data/top.txt, got: %v", keys(entries))
}
if _, ok := entries["data/subdir/nested.txt"]; !ok {
t.Errorf("missing data/subdir/nested.txt, got: %v", keys(entries))
}
}
func TestArchiveDirectory_SkipsHiddenFiles(t *testing.T) {
src := t.TempDir()
os.WriteFile(filepath.Join(src, ".hidden"), []byte("secret"), 0644)
os.WriteFile(filepath.Join(src, "visible.txt"), []byte("visible"), 0644)
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
files, _, err := ArchiveDirectory(tw, src, "ws", nil)
tw.Close()
if err != nil {
t.Fatalf("error: %v", err)
}
if files != 1 {
t.Errorf("expected 1 file (hidden skipped), got %d", files)
}
entries := readTarEntries(t, buf.Bytes())
if _, ok := entries["ws/.hidden"]; ok {
t.Error("hidden file should not be in archive")
}
}
func TestArchiveDirectory_SkipsHiddenDirs(t *testing.T) {
src := t.TempDir()
os.MkdirAll(filepath.Join(src, ".git"), 0755)
os.WriteFile(filepath.Join(src, ".git", "config"), []byte("git stuff"), 0644)
os.WriteFile(filepath.Join(src, "README.txt"), []byte("readme"), 0644)
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
files, _, err := ArchiveDirectory(tw, src, "ws", nil)
tw.Close()
if err != nil {
t.Fatalf("error: %v", err)
}
if files != 1 {
t.Errorf("expected 1 file (.git dir skipped), got %d", files)
}
entries := readTarEntries(t, buf.Bytes())
for k := range entries {
if strings.Contains(k, ".git") {
t.Errorf("hidden dir contents should not be archived: %q", k)
}
}
}
func TestArchiveDirectory_SkipsTmpDirs(t *testing.T) {
src := t.TempDir()
os.MkdirAll(filepath.Join(src, "tmp"), 0755)
os.WriteFile(filepath.Join(src, "tmp", "temp.txt"), []byte("temp"), 0644)
os.WriteFile(filepath.Join(src, "real.txt"), []byte("real"), 0644)
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
files, _, err := ArchiveDirectory(tw, src, "ws", nil)
tw.Close()
if err != nil {
t.Fatalf("error: %v", err)
}
if files != 1 {
t.Errorf("expected 1 file (tmp dir skipped), got %d", files)
}
}
func TestArchiveDirectory_CustomSkipFn(t *testing.T) {
src := t.TempDir()
os.WriteFile(filepath.Join(src, "keep.txt"), []byte("keep"), 0644)
os.WriteFile(filepath.Join(src, "skip.log"), []byte("skip me"), 0644)
skipLogs := func(path string) bool {
return strings.HasSuffix(path, ".log")
}
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
files, _, err := ArchiveDirectory(tw, src, "ws", skipLogs)
tw.Close()
if err != nil {
t.Fatalf("error: %v", err)
}
if files != 1 {
t.Errorf("expected 1 file (.log skipped), got %d", files)
}
entries := readTarEntries(t, buf.Bytes())
if _, ok := entries["ws/keep.txt"]; !ok {
t.Error("keep.txt should be in archive")
}
if _, ok := entries["ws/skip.log"]; ok {
t.Error("skip.log should not be in archive")
}
}
func TestArchiveDirectory_TarPrefix(t *testing.T) {
src := t.TempDir()
os.WriteFile(filepath.Join(src, "file.txt"), []byte("x"), 0644)
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
ArchiveDirectory(tw, src, "custom/prefix", nil)
tw.Close()
entries := readTarEntries(t, buf.Bytes())
if _, ok := entries["custom/prefix/file.txt"]; !ok {
t.Errorf("expected custom/prefix/file.txt in archive, got: %v", keys(entries))
}
}
// keys returns sorted map keys for test output readability.
func keys(m map[string]string) []string {
out := make([]string, 0, len(m))
for k := range m {
out = append(out, k)
}
return out
}