aboutsummaryrefslogtreecommitdiff
path: root/internal/source/source_test.go
diff options
context:
space:
mode:
authortsne <tsne.dev@outlook.com>2026-07-06 16:52:20 +0200
committertsne <tsne.dev@outlook.com>2026-07-06 19:21:15 +0200
commit2c41acab4c97f584e8e199b31dc456194b8d7e6c (patch)
tree42381deb6c4f6da0f32945432207a31b4d86664e /internal/source/source_test.go
downloadhopper-2c41acab4c97f584e8e199b31dc456194b8d7e6c.tar.gz
initial
Diffstat (limited to 'internal/source/source_test.go')
-rw-r--r--internal/source/source_test.go95
1 files changed, 95 insertions, 0 deletions
diff --git a/internal/source/source_test.go b/internal/source/source_test.go
new file mode 100644
index 0000000..933c67a
--- /dev/null
+++ b/internal/source/source_test.go
@@ -0,0 +1,95 @@
+package source
+
+import (
+ "bytes"
+ "io"
+ "testing"
+
+ "tsne.dev/hopper/internal/log"
+)
+
+func TestCleanArchivePath(t *testing.T) {
+ tests := []struct {
+ raw string
+ want string
+ wantErr bool
+ warn bool
+ }{
+ {"a/b.js", "a/b.js", false, false},
+ {"./a//b.js", "a/b.js", false, false},
+ {"/abs/x", "", true, false},
+ {"../escape", "", true, false},
+ {"a/../../escape", "", true, false},
+ {"a/../b.js", "b.js", false, true}, // contained .. but stays in root
+ }
+ for _, tt := range tests {
+ var warn bytes.Buffer
+ log.Init(io.Discard, &warn)
+ got, err := cleanArchivePath(tt.raw)
+ if tt.wantErr {
+ if err == nil {
+ t.Errorf("%q: expected error", tt.raw)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("%q: unexpected error %v", tt.raw, err)
+ continue
+ }
+ if got != tt.want {
+ t.Errorf("%q: got %q want %q", tt.raw, got, tt.want)
+ }
+ if tt.warn && warn.Len() == 0 {
+ t.Errorf("%q: expected warning", tt.raw)
+ }
+ if !tt.warn && warn.Len() != 0 {
+ t.Errorf("%q: unexpected warning %q", tt.raw, warn.String())
+ }
+ }
+}
+
+func TestSelectPath(t *testing.T) {
+ tests := []struct {
+ cleaned string
+ root string
+ want string
+ wantOK bool
+ }{
+ {"assets/x.js", "", "assets/x.js", true},
+ {"README.md", "", "README.md", true},
+ {"app/assets/x.js", "app", "assets/x.js", true},
+ {"README.md", "app", "", false},
+ {"app", "app", "", false},
+ {"appendix/x", "app", "", false},
+ {"dist/public/index.html", "dist/public", "index.html", true},
+ {"dist/private/x", "dist/public", "", false},
+ }
+ for _, tt := range tests {
+ s := &TarGzSource{root: tt.root}
+ got, ok := s.selectPath(tt.cleaned)
+ if ok != tt.wantOK {
+ t.Errorf("selectPath(%q, %q): ok = %v, want %v", tt.cleaned, tt.root, ok, tt.wantOK)
+ continue
+ }
+ if ok && got != tt.want {
+ t.Errorf("selectPath(%q, %q) = %q, want %q", tt.cleaned, tt.root, got, tt.want)
+ }
+ }
+}
+
+func TestContainsDotDot(t *testing.T) {
+ cases := map[string]bool{
+ "a/b/c": false,
+ "..": true,
+ "../x": true,
+ "a/../b": true,
+ "a/..b/c": false,
+ "": false,
+ "a/b/..": true,
+ }
+ for in, want := range cases {
+ if got := containsDotDot(in); got != want {
+ t.Errorf("containsDotDot(%q) = %v, want %v", in, got, want)
+ }
+ }
+}