package attach
import (
"strings"
"testing"
)
const sum = "4f9a0000000000000000000000000000000000000000000000000000000ae01c"
// The stub round-trips through its own bytes, and those bytes are the
// RFC's five lines in the RFC's order — a fossil client shows them as
// a small readable file, so the order is part of the contract.
func TestStubFormatsAndParsesItself(t *testing.T) {
want := Stub{SHA256: sum, Size: 18349022, Type: "video/mp4", Name: "screen recording.mp4"}
raw := want.Format()
lines := strings.Split(strings.TrimSuffix(string(raw), "\n"), "\n")
if len(lines) != 5 || lines[0] != Header || !strings.HasPrefix(lines[1], "sha256 ") ||
!strings.HasPrefix(lines[2], "size ") || !strings.HasPrefix(lines[3], "type ") || !strings.HasPrefix(lines[4], "name ") {
t.Fatalf("FAIL: format = %q", raw)
}
got, err := Parse(raw)
if err != nil || got != want {
t.Fatalf("FAIL: parse = %+v, %v", got, err)
}
if !IsStub(raw[:len(Header)+1]) || !IsStub(raw) {
t.Errorf("FAIL: IsStub refused the stub's own front")
}
if IsStub([]byte("quarry-attachment 2\n")) || IsStub([]byte("%PDF-1.7")) {
t.Errorf("FAIL: IsStub accepted a foreign front")
}
t.Log("ok: the stub round-trips")
}
// Parse is strict: any drift from the written shape is refused rather
// than guessed at. Only the trailing newline is forgiven.
func TestStubParseIsStrict(t *testing.T) {
good := Stub{SHA256: sum, Size: 12, Type: "image/png", Name: "a.png"}.Format()
if _, err := Parse([]byte(strings.TrimSuffix(string(good), "\n"))); err != nil {
t.Errorf("FAIL: no trailing newline: %v", err)
}
bad := map[string]string{
"no header": strings.Replace(string(good), Header, "quarry-attachment 0", 1),
"short sha": strings.Replace(string(good), sum, sum[:63], 1),
"upper sha": strings.Replace(string(good), sum, strings.ToUpper(sum), 1),
"size words": strings.Replace(string(good), "size 12", "size twelve", 1),
"size sign": strings.Replace(string(good), "size 12", "size +12", 1),
"unknown key": string(good) + "url http://x\n",
"missing name": strings.Replace(string(good), "name a.png\n", "", 1),
"twice": strings.Replace(string(good), "name a.png\n", "name a.png\nname b.png\n", 1),
"empty value": strings.Replace(string(good), "type image/png", "type ", 1),
"two newlines": string(good) + "\n",
"too long": string(good) + strings.Repeat("x", MaxStubBytes),
}
for name, raw := range bad {
if _, err := Parse([]byte(raw)); err == nil {
t.Errorf("FAIL: %s parsed", name)
}
}
t.Log("ok: every drift is refused")
}
// The policy: text always, the listed images, documents and media, the
// archives only when opened, executables never; and inline is narrower
// than allowed, SVG in particular.
func TestPolicy(t *testing.T) {
for _, mt := range []string{"text/plain", "text/x-diff; charset=utf-8", "image/PNG", "application/pdf", "video/mp4", "audio/mpeg", "application/json"} {
if !Allowed(mt, false) {
t.Errorf("FAIL: %s refused", mt)
}
}
for _, mt := range []string{"application/zip", "application/gzip", "application/x-tar"} {
if Allowed(mt, false) || !Allowed(mt, true) {
t.Errorf("FAIL: archive %s: closed=%v open=%v", mt, Allowed(mt, false), Allowed(mt, true))
}
}
for _, mt := range []string{"application/x-msdownload", "application/x-sh", "application/octet-stream", "", "text"} {
if Allowed(mt, true) {
t.Errorf("FAIL: %q allowed", mt)
}
}
if Inline("image/svg+xml") || !Inline("image/png") || !Inline("text/plain") || Inline("application/pdf") {
t.Errorf("FAIL: inline allowlist")
}
if Kind("image/svg+xml") != "image" || Kind("text/csv") != "text" || Kind("video/webm") != "media" || Kind("application/zip") != "other" {
t.Errorf("FAIL: kinds")
}
t.Log("ok: policy holds")
}