Files
ReleaseKit/internal/validation/file_size_test.go

195 lines
7.1 KiB
Go

package validation
import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"gitea.maximumdirect.net/eric/ReleaseKit/internal/git"
)
func TestCheckFileSizeUsesStrictLimitsAndDefault(t *testing.T) {
tests := []struct {
name string
limit *int64
size int64
wantLimit int64
wantError bool
}{
{name: "below", limit: int64Pointer(10), size: 9, wantLimit: 10},
{name: "at", limit: int64Pointer(10), size: 10, wantLimit: 10},
{name: "above", limit: int64Pointer(10), size: 11, wantLimit: 10, wantError: true},
{name: "default at", size: 1_000_000, wantLimit: 1_000_000},
{name: "default above", size: 1_000_001, wantLimit: 1_000_000, wantError: true},
{name: "zero empty", limit: int64Pointer(0), size: 0},
{name: "zero nonempty", limit: int64Pointer(0), size: 1, wantError: true},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
tree := &CandidateTree{entries: []git.TreeEntry{{
Path: "file.bin", Type: "blob", Mode: "100644", Size: test.size, HasSize: true,
}}}
err := checkFileSize(tree, &FileSizeSettings{LimitBytes: test.limit})
if !test.wantError {
if err != nil {
t.Fatalf("checkFileSize() error = %v, want nil", err)
}
return
}
failure, ok := err.(*Failure)
if !ok || failure.Check != FileSize || len(failure.Findings) != 1 || failure.Findings[0].Path != "file.bin" {
t.Fatalf("checkFileSize() error = %v, want file-size finding", err)
}
if failure.Measurement == nil || failure.Measurement.Bytes != test.size || failure.Measurement.LimitBytes != test.wantLimit {
t.Fatalf("measurement = %#v, want %d bytes against %d", failure.Measurement, test.size, test.wantLimit)
}
})
}
}
func TestCheckFileSizeAppliesLiteralPathExclusions(t *testing.T) {
tests := []struct {
name string
entries []git.TreeEntry
exclusions []string
wantPath string
}{
{
name: "directory boundary and normalized paths",
entries: []git.TreeEntry{
{Path: "assets/icon.png", Type: "blob", Size: 20, HasSize: true},
{Path: "assets-other/icon.png", Type: "blob", Size: 20, HasSize: true},
},
exclusions: []string{"./assets/"},
wantPath: "assets-other/icon.png",
},
{
name: "glob metacharacters are literal",
entries: []git.TreeEntry{
{Path: "[ab].dat", Type: "blob", Size: 20, HasSize: true},
{Path: "a.dat", Type: "blob", Size: 20, HasSize: true},
},
exclusions: []string{"[ab].dat", "literal[set]"},
wantPath: "a.dat",
},
{
name: "directory metacharacters are literal",
entries: []git.TreeEntry{
{Path: "literal[set]/file.dat", Type: "blob", Size: 20, HasSize: true},
{Path: "literalx/file.dat", Type: "blob", Size: 20, HasSize: true},
},
exclusions: []string{"literal[set]"},
wantPath: "literalx/file.dat",
},
{
name: "unmatched exclusion",
entries: []git.TreeEntry{
{Path: "selected.dat", Type: "blob", Size: 20, HasSize: true},
},
exclusions: []string{"not-present"},
wantPath: "selected.dat",
},
{
name: "exact file exclusion",
entries: []git.TreeEntry{
{Path: "selected.dat", Type: "blob", Size: 20, HasSize: true},
{Path: "other.dat", Type: "blob", Size: 20, HasSize: true},
},
exclusions: []string{"selected.dat"},
wantPath: "other.dat",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := checkFileSize(&CandidateTree{entries: test.entries}, &FileSizeSettings{
LimitBytes: int64Pointer(10),
Exclusions: test.exclusions,
})
failure, ok := err.(*Failure)
if !ok || failure.Check != FileSize || len(failure.Findings) != 1 || failure.Findings[0].Path != test.wantPath {
t.Fatalf("checkFileSize() error = %v, want only %q to exceed the limit", err, test.wantPath)
}
})
}
}
func TestCheckFileSizeUsesCommittedBlobSizes(t *testing.T) {
repository := newTreeRepository(t)
writeTreeFile(t, repository, "small.bin", []byte{0, 1})
writeTreeFile(t, repository, "boundary.bin", []byte{0, 1, 2})
if err := os.Symlink("small.bin", filepath.Join(repository, "link")); err != nil {
t.Fatal(err)
}
submodule := newTreeRepository(t)
writeTreeFile(t, submodule, "submodule.txt", []byte("submodule"))
gitTree(t, submodule, "add", "--all")
gitTree(t, submodule, "commit", "-m", "create nested source")
submoduleCommit := strings.TrimSpace(gitTree(t, submodule, "rev-parse", "HEAD"))
gitTree(t, repository, "add", "--all")
gitTree(t, repository, "update-index", "--add", "--cacheinfo", "160000,"+submoduleCommit+",submodule")
commit := commitCandidateTree(t, repository)
tree, err := ReadCandidateTree(context.Background(), repository, commit)
if err != nil {
t.Fatal(err)
}
cleanupCandidateTree(t, tree)
err = checkFileSize(tree, &FileSizeSettings{LimitBytes: int64Pointer(3), Exclusions: []string{"link"}})
if err != nil {
t.Fatalf("checkFileSize() error = %v, want small binary and exact-limit blob to pass", err)
}
err = checkFileSize(tree, &FileSizeSettings{LimitBytes: int64Pointer(3), Exclusions: []string{"small.bin", "boundary.bin"}})
failure, ok := err.(*Failure)
if !ok || failure.Check != FileSize || len(failure.Findings) != 1 || failure.Findings[0].Path != "link" {
t.Fatalf("checkFileSize() error = %v, want symlink blob to exceed the limit", err)
}
if failure.Measurement == nil || failure.Measurement.Bytes != int64(len("small.bin")) {
t.Fatalf("symlink measurement = %#v, want link-text size %d", failure.Measurement, len("small.bin"))
}
err = checkFileSize(tree, &FileSizeSettings{LimitBytes: int64Pointer(0), Exclusions: []string{"small.bin", "boundary.bin", "link"}})
if err != nil {
t.Fatalf("checkFileSize() error = %v, want omitted gitlink size", err)
}
}
func TestCheckFileSizeUsesOnlyCandidateFilesIndividually(t *testing.T) {
repository := newTreeRepository(t)
writeTreeFile(t, repository, "historical-large.dat", []byte(strings.Repeat("h", 20)))
commitCandidateTree(t, repository)
if err := os.Remove(filepath.Join(repository, "historical-large.dat")); err != nil {
t.Fatal(err)
}
writeTreeFile(t, repository, "one.dat", []byte(strings.Repeat("a", 6)))
writeTreeFile(t, repository, "two.dat", []byte(strings.Repeat("b", 6)))
commit := commitCandidateTree(t, repository)
tree, err := ReadCandidateTree(context.Background(), repository, commit)
if err != nil {
t.Fatal(err)
}
cleanupCandidateTree(t, tree)
if err := checkFileSize(tree, &FileSizeSettings{LimitBytes: int64Pointer(10)}); err != nil {
t.Fatalf("checkFileSize() error = %v, want current individual files to pass", err)
}
}
func TestRunRejectsNegativeFileSizeLimitBeforeGoChecks(t *testing.T) {
negative := int64(-1)
err := Run(context.Background(), Request{Settings: Settings{
FileSize: &FileSizeSettings{LimitBytes: &negative},
}})
failure, ok := err.(*Failure)
if !ok || failure.Check != FileSize || len(failure.Findings) != 1 || !strings.Contains(failure.Findings[0].Message, "negative") {
t.Fatalf("Run() error = %v, want invalid file-size limit failure", err)
}
if errors.Unwrap(failure) != nil {
t.Fatalf("invalid limit failure cause = %v, want none", failure.Cause)
}
}
func int64Pointer(value int64) *int64 { return &value }