| 1 | // SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | |
| 3 | package webedit |
| 4 | |
| 5 | import ( |
| 6 | "errors" |
| 7 | "testing" |
| 8 | ) |
| 9 | |
| 10 | func TestValidateFilePath(t *testing.T) { |
| 11 | t.Parallel() |
| 12 | valid := []string{ |
| 13 | "README.md", |
| 14 | "docs/CONTRIBUTING.md", |
| 15 | ".github/workflows/ci.yml", |
| 16 | "space name/file.txt", |
| 17 | } |
| 18 | for _, p := range valid { |
| 19 | if err := ValidateFilePath(p); err != nil { |
| 20 | t.Errorf("ValidateFilePath(%q) = %v, want nil", p, err) |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | invalid := []string{ |
| 25 | "", |
| 26 | "/README.md", |
| 27 | "docs/", |
| 28 | "docs//README.md", |
| 29 | "../README.md", |
| 30 | "docs/../README.md", |
| 31 | "docs/./README.md", |
| 32 | `docs\README.md`, |
| 33 | "bad\x00name", |
| 34 | } |
| 35 | for _, p := range invalid { |
| 36 | if err := ValidateFilePath(p); !errors.Is(err, ErrInvalidPath) { |
| 37 | t.Errorf("ValidateFilePath(%q) = %v, want ErrInvalidPath", p, err) |
| 38 | } |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | func TestDefaultMessage(t *testing.T) { |
| 43 | t.Parallel() |
| 44 | cases := []struct { |
| 45 | op Op |
| 46 | source string |
| 47 | target string |
| 48 | files []File |
| 49 | want string |
| 50 | }{ |
| 51 | {op: OpEdit, source: "README.md", want: "Update README.md"}, |
| 52 | {op: OpCreate, target: "docs/usage.md", want: "Create docs/usage.md"}, |
| 53 | {op: OpRename, source: "old.md", target: "new.md", want: "Rename old.md to new.md"}, |
| 54 | {op: OpDelete, source: "SECURITY.md", want: "Delete SECURITY.md"}, |
| 55 | {op: OpUpload, files: []File{{Path: "asset.png"}}, want: "Upload asset.png"}, |
| 56 | {op: OpUpload, files: []File{{Path: "a"}, {Path: "b"}}, want: "Upload files"}, |
| 57 | } |
| 58 | for _, c := range cases { |
| 59 | if got := DefaultMessage(c.op, c.source, c.target, c.files); got != c.want { |
| 60 | t.Errorf("DefaultMessage(%q) = %q, want %q", c.op, got, c.want) |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | func TestIsBinary(t *testing.T) { |
| 66 | t.Parallel() |
| 67 | if IsBinary([]byte("plain text\n")) { |
| 68 | t.Fatal("text detected as binary") |
| 69 | } |
| 70 | if !IsBinary([]byte{'h', 'i', 0, 'x'}) { |
| 71 | t.Fatal("NUL-containing content was not detected as binary") |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | func TestValidBranchNameRejectsDetachedInputs(t *testing.T) { |
| 76 | t.Parallel() |
| 77 | if !validBranchName("feature/editor-ui") { |
| 78 | t.Fatal("branch with slash rejected") |
| 79 | } |
| 80 | if validBranchName("0123456789abcdef0123456789abcdef01234567") { |
| 81 | t.Fatal("40-hex detached oid accepted as branch") |
| 82 | } |
| 83 | if validBranchName("release.lock") { |
| 84 | t.Fatal(".lock branch accepted") |
| 85 | } |
| 86 | } |
| 87 |