Expand test coverage: path validation, round-trip, error cases
Add tests for ValidatePath, AddFile size mismatch, invalid paths, progress reporting, manifest round-trip, invalid magic, truncated input, empty input, and manifest String() method.
This commit is contained in:
parent
40c94fe168
commit
735f409f6c
@ -162,6 +162,159 @@ func TestBuilderBuildDeterministicOrder(t *testing.T) {
|
||||
assert.Equal(t, "z.txt", files1[4].Path)
|
||||
}
|
||||
|
||||
func TestValidatePath(t *testing.T) {
|
||||
valid := []string{
|
||||
"file.txt",
|
||||
"dir/file.txt",
|
||||
"a/b/c/d.txt",
|
||||
"file with spaces.txt",
|
||||
"日本語.txt",
|
||||
}
|
||||
for _, p := range valid {
|
||||
t.Run("valid:"+p, func(t *testing.T) {
|
||||
assert.NoError(t, ValidatePath(p))
|
||||
})
|
||||
}
|
||||
|
||||
invalid := []struct {
|
||||
path string
|
||||
desc string
|
||||
}{
|
||||
{"", "empty"},
|
||||
{"/absolute", "absolute path"},
|
||||
{"has\\backslash", "backslash"},
|
||||
{"has/../traversal", "dot-dot segment"},
|
||||
{"has//double", "empty segment"},
|
||||
{"..", "just dot-dot"},
|
||||
{string([]byte{0xff, 0xfe}), "invalid UTF-8"},
|
||||
}
|
||||
for _, tt := range invalid {
|
||||
t.Run("invalid:"+tt.desc, func(t *testing.T) {
|
||||
assert.Error(t, ValidatePath(tt.path))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderAddFileSizeMismatch(t *testing.T) {
|
||||
b := NewBuilder()
|
||||
content := []byte("short")
|
||||
reader := bytes.NewReader(content)
|
||||
|
||||
// Declare wrong size
|
||||
_, err := b.AddFile("test.txt", FileSize(100), ModTime(time.Now()), reader, nil)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "size mismatch")
|
||||
}
|
||||
|
||||
func TestBuilderAddFileInvalidPath(t *testing.T) {
|
||||
b := NewBuilder()
|
||||
content := []byte("data")
|
||||
reader := bytes.NewReader(content)
|
||||
|
||||
_, err := b.AddFile("", FileSize(len(content)), ModTime(time.Now()), reader, nil)
|
||||
assert.Error(t, err)
|
||||
|
||||
reader.Reset(content)
|
||||
_, err = b.AddFile("/absolute", FileSize(len(content)), ModTime(time.Now()), reader, nil)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestBuilderAddFileWithProgress(t *testing.T) {
|
||||
b := NewBuilder()
|
||||
content := bytes.Repeat([]byte("x"), 1000)
|
||||
reader := bytes.NewReader(content)
|
||||
progress := make(chan FileHashProgress, 100)
|
||||
|
||||
bytesRead, err := b.AddFile("test.txt", FileSize(len(content)), ModTime(time.Now()), reader, progress)
|
||||
close(progress)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, FileSize(1000), bytesRead)
|
||||
|
||||
var updates []FileHashProgress
|
||||
for p := range progress {
|
||||
updates = append(updates, p)
|
||||
}
|
||||
assert.NotEmpty(t, updates)
|
||||
// Last update should show all bytes
|
||||
assert.Equal(t, FileSize(1000), updates[len(updates)-1].BytesRead)
|
||||
}
|
||||
|
||||
func TestBuilderBuildRoundTrip(t *testing.T) {
|
||||
// Build a manifest, deserialize it, verify all fields survive round-trip
|
||||
b := NewBuilder()
|
||||
now := time.Date(2025, 6, 15, 12, 0, 0, 0, time.UTC)
|
||||
|
||||
files := []struct {
|
||||
path string
|
||||
content []byte
|
||||
}{
|
||||
{"alpha.txt", []byte("alpha content")},
|
||||
{"beta/gamma.txt", []byte("gamma content")},
|
||||
{"beta/delta.txt", []byte("delta content")},
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
reader := bytes.NewReader(f.content)
|
||||
_, err := b.AddFile(RelFilePath(f.path), FileSize(len(f.content)), ModTime(now), reader, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, b.Build(&buf))
|
||||
|
||||
m, err := NewManifestFromReader(&buf)
|
||||
require.NoError(t, err)
|
||||
|
||||
mfiles := m.Files()
|
||||
require.Len(t, mfiles, 3)
|
||||
|
||||
// Verify sorted order
|
||||
assert.Equal(t, "alpha.txt", mfiles[0].Path)
|
||||
assert.Equal(t, "beta/delta.txt", mfiles[1].Path)
|
||||
assert.Equal(t, "beta/gamma.txt", mfiles[2].Path)
|
||||
|
||||
// Verify sizes
|
||||
assert.Equal(t, int64(len("alpha content")), mfiles[0].Size)
|
||||
|
||||
// Verify hashes are present
|
||||
for _, f := range mfiles {
|
||||
require.NotEmpty(t, f.Hashes, "file %s should have hashes", f.Path)
|
||||
assert.NotEmpty(t, f.Hashes[0].MultiHash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewManifestFromReaderInvalidMagic(t *testing.T) {
|
||||
_, err := NewManifestFromReader(bytes.NewReader([]byte("NOT_VALID")))
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid file format")
|
||||
}
|
||||
|
||||
func TestNewManifestFromReaderEmpty(t *testing.T) {
|
||||
_, err := NewManifestFromReader(bytes.NewReader([]byte{}))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestNewManifestFromReaderTruncated(t *testing.T) {
|
||||
// Just the magic with nothing after
|
||||
_, err := NewManifestFromReader(bytes.NewReader([]byte(MAGIC)))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestManifestString(t *testing.T) {
|
||||
b := NewBuilder()
|
||||
content := []byte("test")
|
||||
reader := bytes.NewReader(content)
|
||||
_, err := b.AddFile("test.txt", FileSize(len(content)), ModTime(time.Now()), reader, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, b.Build(&buf))
|
||||
|
||||
m, err := NewManifestFromReader(&buf)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, m.String(), "count=1")
|
||||
}
|
||||
|
||||
func TestBuilderBuildEmpty(t *testing.T) {
|
||||
b := NewBuilder()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user