Check committed file sizes
This commit is contained in:
192
internal/validation/file_size_test.go
Normal file
192
internal/validation/file_size_test.go
Normal file
@@ -0,0 +1,192 @@
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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 }
|
||||
Reference in New Issue
Block a user