Update tests, avoid relying on pre-existed test data set
This commit is contained in:
@@ -13,8 +13,12 @@ func TestLoadConfig(t *testing.T) {
|
|||||||
C = Config{}
|
C = Config{}
|
||||||
|
|
||||||
testDataPath := filepath.Join(t.TempDir(), "test_data")
|
testDataPath := filepath.Join(t.TempDir(), "test_data")
|
||||||
os.MkdirAll(filepath.Join(testDataPath, "test1", "foo"), 0755)
|
if err := os.MkdirAll(filepath.Join(testDataPath, "test1", "foo"), 0755); err != nil {
|
||||||
os.MkdirAll(filepath.Join(testDataPath, "test1", "bar"), 0755)
|
t.Fatalf("Failed to create test directory foo: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Join(testDataPath, "test1", "bar"), 0755); err != nil {
|
||||||
|
t.Fatalf("Failed to create test directory bar: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create test config file
|
// Create test config file
|
||||||
testConfigPath := filepath.Join(testDataPath, "test_config.json")
|
testConfigPath := filepath.Join(testDataPath, "test_config.json")
|
||||||
|
|||||||
103
internal/backer/main_test.go
Normal file
103
internal/backer/main_test.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package backer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testDataPath = "../../test_data"
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
if err := setupTestData(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "FATAL: Failed to setup test data: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
code := m.Run()
|
||||||
|
|
||||||
|
if err := cleanupTestData(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "WARNING: Failed to cleanup test data: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTestData() error {
|
||||||
|
testDir := filepath.Join(testDataPath, "test1")
|
||||||
|
|
||||||
|
dirs := []string{
|
||||||
|
filepath.Join(testDir, "foo", "mydir"),
|
||||||
|
filepath.Join(testDir, "bar", "goodbye.txt"),
|
||||||
|
filepath.Join(testDir, "hardlinks"),
|
||||||
|
filepath.Join(testDir, "symlinks"),
|
||||||
|
filepath.Join(testDir, "foo", "empty_dir"),
|
||||||
|
filepath.Join(testDataPath, "tmp"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir := range dirs {
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("Failed to create directory %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files := map[string]string{
|
||||||
|
filepath.Join(testDir, "foo", "hello_breakfast.txt"): "hello breakfast content",
|
||||||
|
filepath.Join(testDir, "foo", "some_text.txt"): "some text content",
|
||||||
|
filepath.Join(testDir, "foo", "mydir", "myfile.txt"): "my file content",
|
||||||
|
filepath.Join(testDir, "bar", "hello.txt"): "hello content",
|
||||||
|
filepath.Join(testDir, "bar", "goodbye.txt", "text.txt"): "text content",
|
||||||
|
filepath.Join(testDir, "hardlinks", "original.txt"): "original content",
|
||||||
|
filepath.Join(testDir, "symlinks", "target.txt"): "target content",
|
||||||
|
}
|
||||||
|
|
||||||
|
for path, content := range files {
|
||||||
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
||||||
|
return fmt.Errorf("Failed to write file %s: %w", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
orig := filepath.Join(testDir, "hardlinks", "original.txt")
|
||||||
|
link1 := filepath.Join(testDir, "hardlinks", "hardlink1.txt")
|
||||||
|
if err := os.Link(orig, link1); err != nil {
|
||||||
|
return fmt.Errorf("Failed to create hardlink %s -> %s: %w", link1, orig, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
link2 := filepath.Join(testDir, "hardlinks", "hardlink2.txt")
|
||||||
|
if err := os.Link(orig, link2); err != nil {
|
||||||
|
return fmt.Errorf("Failed to create hardlink %s -> %s: %w", link2, orig, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
target := filepath.Join(testDir, "symlinks", "target.txt")
|
||||||
|
link := filepath.Join(testDir, "symlinks", "link.txt")
|
||||||
|
if err := os.Symlink(target, link); err != nil {
|
||||||
|
return fmt.Errorf("Failed to create symlink %s -> %s: %w", link, target, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanupTestData() error {
|
||||||
|
testDir := filepath.Join(testDataPath, "test1")
|
||||||
|
|
||||||
|
if err := os.RemoveAll(testDir); err != nil {
|
||||||
|
return fmt.Errorf("Failed to remove test directory %s: %w", testDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDir := filepath.Join(testDataPath, "tmp")
|
||||||
|
if err := os.RemoveAll(tmpDir); err != nil {
|
||||||
|
return fmt.Errorf("Failed to remove tmp directory %s: %w", tmpDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTestDataPath() string {
|
||||||
|
return testDataPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTestFilesFromDirectories(ctx context.Context, directories []string) ([]string, error) {
|
||||||
|
return GetFilesFromDirectories(ctx, directories)
|
||||||
|
}
|
||||||
@@ -125,46 +125,43 @@ func TestGetClientIP(t *testing.T) {
|
|||||||
|
|
||||||
// TestGetCompressionAlgorithm tests the getCompressionAlgorithm function.
|
// TestGetCompressionAlgorithm tests the getCompressionAlgorithm function.
|
||||||
func TestGetCompressionAlgorithm(t *testing.T) {
|
func TestGetCompressionAlgorithm(t *testing.T) {
|
||||||
|
// Save original config
|
||||||
|
original := C
|
||||||
|
defer func() { C = original }()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
ext string
|
ext string
|
||||||
expected string
|
expected string
|
||||||
|
defaultCompression string
|
||||||
}{
|
}{
|
||||||
{
|
{name: "tar.bz2", ext: "tar.bz2", expected: "bzip2"},
|
||||||
name: "tar.bz2",
|
{name: "tar.zstd", ext: "tar.zstd", expected: "zstd"},
|
||||||
ext: "tar.bz2",
|
{name: "tar.zst", ext: "tar.zst", expected: "zstd"},
|
||||||
expected: "bzip2",
|
{name: "tar.lz4", ext: "tar.lz4", expected: "lz4"},
|
||||||
},
|
{name: "tar.xz", ext: "tar.xz", expected: "xz"},
|
||||||
{
|
{name: "tar.gz", ext: "tar.gz", expected: "pgzip"},
|
||||||
name: "tar.zst",
|
{name: "bz2", ext: "bz2", expected: "bzip2"},
|
||||||
ext: "tar.zst",
|
{name: "zst", ext: "zst", expected: "zstd"},
|
||||||
expected: "zstd",
|
{name: "lz4", ext: "lz4", expected: "lz4"},
|
||||||
},
|
{name: "xz", ext: "xz", expected: "xz"},
|
||||||
{
|
{name: "gz", ext: "gz", expected: "pgzip"},
|
||||||
name: "tar.lz4",
|
{name: "zstd", ext: "zstd", expected: "zstd"},
|
||||||
ext: "tar.lz4",
|
{name: "full path /archive.tar.gz", ext: "/archive.tar.gz", expected: "pgzip"},
|
||||||
expected: "lz4",
|
{name: "full path /archive.tar.xz", ext: "/archive.tar.xz", expected: "xz"},
|
||||||
},
|
{name: "full path /archive", ext: "/archive", expected: "gzip"},
|
||||||
{
|
{name: "empty uses default", ext: "", expected: "gzip"},
|
||||||
name: "tar.xz",
|
{name: "unknown uses default", ext: "unknown", expected: "gzip"},
|
||||||
ext: "tar.xz",
|
{name: "unknown with default zstd", ext: "unknown", expected: "zstd", defaultCompression: "zstd"},
|
||||||
expected: "xz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "tar.gz",
|
|
||||||
ext: "tar.gz",
|
|
||||||
expected: "pgzip",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "default with gzip config",
|
|
||||||
ext: "",
|
|
||||||
expected: "gzip",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
C = Config{DefaultCompression: "gzip"}
|
if tt.defaultCompression != "" {
|
||||||
|
C = Config{DefaultCompression: tt.defaultCompression}
|
||||||
|
} else {
|
||||||
|
C = Config{DefaultCompression: "gzip"}
|
||||||
|
}
|
||||||
result := getCompressionAlgorithm(tt.ext)
|
result := getCompressionAlgorithm(tt.ext)
|
||||||
if result != tt.expected {
|
if result != tt.expected {
|
||||||
t.Errorf("getCompressionAlgorithm(%q) = %q, expected %q", tt.ext, result, tt.expected)
|
t.Errorf("getCompressionAlgorithm(%q) = %q, expected %q", tt.ext, result, tt.expected)
|
||||||
@@ -191,6 +188,46 @@ func TestNewServerSuccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestWithServerHeader tests that the Server header is added to responses.
|
||||||
|
func TestWithServerHeader(t *testing.T) {
|
||||||
|
handler := withServerHeader(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}))
|
||||||
|
|
||||||
|
rec := &responseWriterWrapper{
|
||||||
|
ResponseWriter: &testResponseWriter{},
|
||||||
|
statusCode: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", "/test", nil)
|
||||||
|
handler.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
if rec.Header().Get("Server") != "backer" {
|
||||||
|
t.Error("Expected Server header to be set to 'backer'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testResponseWriter implements http.ResponseWriter for testing.
|
||||||
|
type testResponseWriter struct {
|
||||||
|
header http.Header
|
||||||
|
status int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tw *testResponseWriter) Header() http.Header {
|
||||||
|
if tw.header == nil {
|
||||||
|
tw.header = make(http.Header)
|
||||||
|
}
|
||||||
|
return tw.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tw *testResponseWriter) Write(data []byte) (int, error) {
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tw *testResponseWriter) WriteHeader(status int) {
|
||||||
|
tw.status = status
|
||||||
|
}
|
||||||
|
|
||||||
// TestNewServerInvalidConfig tests server creation with invalid config.
|
// TestNewServerInvalidConfig tests server creation with invalid config.
|
||||||
func TestNewServerInvalidConfig(t *testing.T) {
|
func TestNewServerInvalidConfig(t *testing.T) {
|
||||||
C = Config{} // Reset global config.
|
C = Config{} // Reset global config.
|
||||||
@@ -618,4 +655,30 @@ func TestMethodNotAllowedHandler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestTimingSafeMatch tests the timingSafeMatch function for constant-time comparison.
|
||||||
|
func TestTimingSafeMatch(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
a string
|
||||||
|
b string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"empty strings", "", "", true},
|
||||||
|
{"identical strings", "test", "test", true},
|
||||||
|
{"different strings", "test", "Test", false},
|
||||||
|
{"different length", "test", "testing", false},
|
||||||
|
{"first empty", "", "test", false},
|
||||||
|
{"second empty", "test", "", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := timingSafeMatch(tt.a, tt.b)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("timingSafeMatch(%q, %q) = %v, expected %v", tt.a, tt.b, result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* vim: setlocal ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: setlocal ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
17
internal/backer/types_test.go
Normal file
17
internal/backer/types_test.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package backer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestErrDirectoryScanTimeout(t *testing.T) {
|
||||||
|
err := ErrDirectoryScanTimeout{ScannedFiles: 42}
|
||||||
|
|
||||||
|
if err.Error() != "Directory scan timed out" {
|
||||||
|
t.Errorf("Expected error message 'Directory scan timed out', got %q", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.ScannedFiles != 42 {
|
||||||
|
t.Errorf("Expected ScannedFiles=42, got %d", err.ScannedFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestCreateTarGzStreamBasic tests basic tar.gz creation with a single file.
|
// TestCreateTarGzStreamBasic tests basic tar.gz creation with a single file.
|
||||||
@@ -617,4 +619,115 @@ func TestCreateTarXzStream(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCreateTarPgzipStream tests pgzip (parallel gzip) compression.
|
||||||
|
func TestCreateTarPgzipStream(t *testing.T) {
|
||||||
|
C = Config{CompressionLevel: 6}
|
||||||
|
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
testFile := filepath.Join(tmpDir, "test.txt")
|
||||||
|
content := []byte("test content for pgzip")
|
||||||
|
if err := os.WriteFile(testFile, content, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
reader := CreateTarPgzipStream(ctx, []string{testFile})
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
gzReader, err := gzip.NewReader(reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create gzip reader: %v", err)
|
||||||
|
}
|
||||||
|
defer gzReader.Close()
|
||||||
|
|
||||||
|
tarReader := tar.NewReader(gzReader)
|
||||||
|
|
||||||
|
header, err := tarReader.Next()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read tar header: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.Name != testFile && filepath.Base(header.Name) != "test.txt" {
|
||||||
|
t.Errorf("Expected file name 'test.txt', got '%s'", header.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := io.ReadAll(tarReader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read file content: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(data) != string(content) {
|
||||||
|
t.Errorf("Expected content '%s', got '%s'", content, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCreateTarZstdStream tests zstd compression.
|
||||||
|
func TestCreateTarZstdStream(t *testing.T) {
|
||||||
|
C = Config{CompressionLevel: 6}
|
||||||
|
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
testFile := filepath.Join(tmpDir, "test.txt")
|
||||||
|
content := []byte("test content for zstd")
|
||||||
|
if err := os.WriteFile(testFile, content, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
reader := CreateTarZstdStream(ctx, []string{testFile})
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
// zstd.Reader is in github.com/klauspost/compress/zstd
|
||||||
|
zstdReader, err := zstd.NewReader(reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create zstd reader: %v", err)
|
||||||
|
}
|
||||||
|
defer zstdReader.Close()
|
||||||
|
|
||||||
|
tarReader := tar.NewReader(zstdReader)
|
||||||
|
|
||||||
|
header, err := tarReader.Next()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read tar header: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.Name != testFile && filepath.Base(header.Name) != "test.txt" {
|
||||||
|
t.Errorf("Expected file name 'test.txt', got '%s'", header.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := io.ReadAll(tarReader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read file content: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(data) != string(content) {
|
||||||
|
t.Errorf("Expected content '%s', got '%s'", content, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestZstdLevel tests zstd level mapping.
|
||||||
|
func TestZstdLevel(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input int
|
||||||
|
expected zstd.EncoderLevel
|
||||||
|
}{
|
||||||
|
{1, zstd.SpeedFastest},
|
||||||
|
{2, zstd.SpeedFastest},
|
||||||
|
{3, zstd.SpeedFastest},
|
||||||
|
{4, zstd.SpeedDefault},
|
||||||
|
{5, zstd.SpeedDefault},
|
||||||
|
{6, zstd.SpeedDefault},
|
||||||
|
{7, zstd.SpeedBetterCompression},
|
||||||
|
{8, zstd.SpeedBetterCompression},
|
||||||
|
{9, zstd.SpeedBetterCompression},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
C = Config{CompressionLevel: tt.input}
|
||||||
|
result := zstdLevel(tt.input)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("zstdLevel(%d) = %v, expected %v", tt.input, result, tt.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* vim: setlocal ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: setlocal ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
Fucked up beyond recognotion.
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
My dear Tweety,
|
|
||||||
I'd like to greet you as my breakfast.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
This is MY file!
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
shared content for hardlinks
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
shared content for hardlinks
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
shared content for hardlinks
|
|
||||||
Reference in New Issue
Block a user