test(14-06): tide reports every sidecar refusal and multipart mismatch
- LoadUpstream names the file for a missing, unknown-field, wrong-version, method-less, relative-URL or out-of-range-status sidecar - WriteUpstream writes nothing for an invalid sidecar or an uncreatable directory - compareParts reports count, name, filename, content type, sha256 and value mismatches and a non-multipart body
This commit is contained in:
110
modules/tide/upstream_coverage_test.go
Normal file
110
modules/tide/upstream_coverage_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package tide
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"mime/multipart"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestUpstreamSidecarRefusals covers the refusals of the sidecar loader,
|
||||
// writer and multipart comparison that the replay tests do not reach: a
|
||||
// missing, malformed or invalid sidecar is an error naming the file, a
|
||||
// write that cannot validate or create its directory writes nothing, every
|
||||
// part mismatch is reported, and an unresolved placeholder is an error.
|
||||
func TestUpstreamSidecarRefusals(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if _, err := LoadUpstream(filepath.Join(dir, "missing.upstream.yaml")); !errors.Is(err, fs.ErrNotExist) {
|
||||
t.Errorf("missing sidecar = %v", err)
|
||||
}
|
||||
write := func(name, body string) string {
|
||||
path := filepath.Join(dir, name)
|
||||
if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
for name, body := range map[string]string{
|
||||
"unknown-field.yaml": "version: 1\nsurprise: true\n",
|
||||
"version.yaml": "version: 9\n",
|
||||
"method.yaml": "version: 1\nexchanges:\n - request: {method: \" \", url: \"https://x.test/\"}\n response: {status: 200}\n",
|
||||
"url.yaml": "version: 1\nexchanges:\n - request: {method: GET, url: \"/relative\"}\n response: {status: 200}\n",
|
||||
"status.yaml": "version: 1\nexchanges:\n - request: {method: GET, url: \"https://x.test/\"}\n response: {status: 42}\n",
|
||||
} {
|
||||
if _, err := LoadUpstream(write(name, body)); err == nil || !strings.Contains(err.Error(), name) {
|
||||
t.Errorf("%s = %v, want an error naming the file", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
store, err := OpenStore("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bad := UpstreamSidecar{Exchanges: []UpstreamExchange{{Request: UpstreamRequest{Method: "", URL: "https://x.test/"}, Response: UpstreamResponse{Status: 200}}}}
|
||||
if err := WriteUpstream(filepath.Join(dir, "bad.upstream.yaml"), bad, store); err == nil {
|
||||
t.Error("an invalid sidecar was written")
|
||||
}
|
||||
blocker := write("blocker", "a file, not a directory")
|
||||
good := UpstreamSidecar{Exchanges: []UpstreamExchange{{Request: UpstreamRequest{Method: "GET", URL: "https://x.test/"}, Response: UpstreamResponse{Status: 200}}}}
|
||||
if err := WriteUpstream(filepath.Join(blocker, "x.upstream.yaml"), good, store); err == nil {
|
||||
t.Error("a sidecar was written under a file")
|
||||
}
|
||||
path := filepath.Join(dir, "nested", "good.upstream.yaml")
|
||||
if err := WriteUpstream(path, good, store); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s, err := LoadUpstream(path); err != nil || s.Version != CurrentVersion {
|
||||
t.Fatalf("written sidecar = %+v %v", s, err)
|
||||
}
|
||||
|
||||
var body bytes.Buffer
|
||||
mw := multipart.NewWriter(&body)
|
||||
_ = mw.WriteField("title", "Bug")
|
||||
h := textproto.MIMEHeader{}
|
||||
h.Set("Content-Disposition", `form-data; name="file"; filename="s.png"`)
|
||||
h.Set("Content-Type", "image/png")
|
||||
fw, _ := mw.CreatePart(h)
|
||||
_, _ = fw.Write([]byte("png"))
|
||||
_ = mw.Close()
|
||||
ctype := mw.FormDataContentType()
|
||||
fake := NewUpstreamFake(UpstreamSidecar{}, store)
|
||||
got, err := upstreamParts(ctype, body.Bytes())
|
||||
if err != nil || len(got) != 2 {
|
||||
t.Fatalf("parts %+v %v", got, err)
|
||||
}
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
want []UpstreamPart
|
||||
frag string
|
||||
}{
|
||||
{"count", got[:1], "want 1, got 2"},
|
||||
{"name", []UpstreamPart{{Name: "other", Value: "Bug"}, got[1]}, "name: want"},
|
||||
{"filename", []UpstreamPart{got[0], {Name: "file", Filename: "x.png", ContentType: "image/png", SHA256: got[1].SHA256}}, "filename"},
|
||||
{"content type", []UpstreamPart{got[0], {Name: "file", Filename: "s.png", ContentType: "image/jpeg", SHA256: got[1].SHA256}}, "content type"},
|
||||
{"sha256", []UpstreamPart{got[0], {Name: "file", Filename: "s.png", ContentType: "image/png", SHA256: strings.Repeat("0", 64)}}, "sha256"},
|
||||
{"value", []UpstreamPart{{Name: "title", Value: "Feature"}, got[1]}, "value differs"},
|
||||
{"placeholder", []UpstreamPart{{Name: "title", Value: "{{missing}}"}, got[1]}, "unresolved placeholder"},
|
||||
} {
|
||||
problems := fake.compareParts(c.want, ctype, body.Bytes())
|
||||
if len(problems) == 0 || !strings.Contains(strings.Join(problems, "; "), c.frag) {
|
||||
t.Errorf("%s mismatch reported %v", c.name, problems)
|
||||
}
|
||||
}
|
||||
if problems := fake.compareParts(got, "application/json", body.Bytes()); len(problems) != 1 || !strings.Contains(problems[0], "not multipart") {
|
||||
t.Errorf("a non-multipart body = %v", problems)
|
||||
}
|
||||
if problems := fake.compareParts(got, ctype, body.Bytes()); len(problems) != 0 {
|
||||
t.Errorf("identical parts = %v", problems)
|
||||
}
|
||||
if _, err := store.expandKeeping("{{absent}} and {{sha256:kept}}", isHashPlaceholder); err == nil || !strings.Contains(err.Error(), "absent") {
|
||||
t.Errorf("expandKeeping with a missing name = %v", err)
|
||||
}
|
||||
if out, err := store.expandKeeping("plain", isHashPlaceholder); err != nil || out != "plain" {
|
||||
t.Errorf("expandKeeping(plain) = %q %v", out, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user