@@ -0,0 +1,143 @@
|
||||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type trackingReadCloser struct {
|
||||
io.Reader
|
||||
closed bool
|
||||
}
|
||||
|
||||
type testMultipartFile struct {
|
||||
name string
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (r *trackingReadCloser) Close() error {
|
||||
r.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func testImageResponse(status int, body string) (*http.Response, *trackingReadCloser) {
|
||||
reader := &trackingReadCloser{Reader: strings.NewReader(body)}
|
||||
return &http.Response{StatusCode: status, Body: reader}, reader
|
||||
}
|
||||
|
||||
func readMultipartFields(t *testing.T, body []byte, contentType string) (map[string]string, map[string]testMultipartFile) {
|
||||
t.Helper()
|
||||
_, params, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
t.Fatalf("parse content type: %v", err)
|
||||
}
|
||||
reader := multipart.NewReader(strings.NewReader(string(body)), params["boundary"])
|
||||
fields, files := map[string]string{}, map[string]testMultipartFile{}
|
||||
for {
|
||||
part, err := reader.NextPart()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("next part: %v", err)
|
||||
}
|
||||
data, _ := io.ReadAll(part)
|
||||
if part.FileName() == "" {
|
||||
fields[part.FormName()] = string(data)
|
||||
} else {
|
||||
files[part.FormName()] = testMultipartFile{name: part.FileName(), data: data}
|
||||
}
|
||||
}
|
||||
return fields, files
|
||||
}
|
||||
|
||||
func TestImageMultipartIncludesFileAndName(t *testing.T) {
|
||||
fileName := "idp-icon-00112233445566778899aabbccddeeff.png"
|
||||
body, contentType, err := imageMultipart(fileName, []byte("image-data"))
|
||||
if err != nil {
|
||||
t.Fatalf("imageMultipart: %v", err)
|
||||
}
|
||||
fields, files := readMultipartFields(t, body, contentType)
|
||||
if got := fields["fileName"]; got != fileName {
|
||||
t.Errorf("fileName = %q, want %q", got, fileName)
|
||||
}
|
||||
file := files["file"]
|
||||
if file.name != fileName || string(file.data) != "image-data" {
|
||||
t.Errorf("file part = %q, %q; want %q, image-data", file.name, file.data, fileName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseImageUploadResponse(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, body, wantURL, wantName string
|
||||
status int
|
||||
callErr error
|
||||
wantErr bool
|
||||
}{
|
||||
{"complete", `{"fileUrl":"https://img/x","fileName":"images/x.png"}`, "https://img/x", "images/x.png", 201, nil, false},
|
||||
{"missing name", `{"fileUrl":"https://img/x"}`, "", "", 201, nil, true},
|
||||
{"missing url", `{"fileName":"images/x.png"}`, "", "", 200, nil, true},
|
||||
{"bad json", `{`, "", "", 200, nil, true},
|
||||
{"http error hides body", `secret-body`, "", "", 400, nil, true},
|
||||
{"call error", `{}`, "", "", 500, errors.New("upstream"), true},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
resp, body := testImageResponse(tc.status, tc.body)
|
||||
gotURL, gotName, err := parseImageUploadResponse(resp, tc.callErr)
|
||||
if gotURL != tc.wantURL || gotName != tc.wantName || (err != nil) != tc.wantErr {
|
||||
t.Errorf("result = %q, %q, %v; want %q, %q, err=%v", gotURL, gotName, err, tc.wantURL, tc.wantName, tc.wantErr)
|
||||
}
|
||||
if !body.closed {
|
||||
t.Error("response body was not closed")
|
||||
}
|
||||
if err != nil && strings.Contains(err.Error(), "secret-body") {
|
||||
t.Errorf("error leaked response body: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomainImageDeleteRequestEncodesFileName(t *testing.T) {
|
||||
fileName := "images/folder/a b+%.png"
|
||||
req, err := newDomainImageDeleteRequest(context.Background(), "https://id.example/", fileName)
|
||||
if err != nil {
|
||||
t.Fatalf("newDomainImageDeleteRequest: %v", err)
|
||||
}
|
||||
if req.Method != http.MethodDelete || req.URL.Path != domainImagesPath {
|
||||
t.Errorf("request = %s %s, want DELETE %s", req.Method, req.URL.Path, domainImagesPath)
|
||||
}
|
||||
if got := req.URL.Query().Get("fileName"); got != fileName {
|
||||
t.Errorf("decoded fileName = %q, want %q", got, fileName)
|
||||
}
|
||||
if strings.Contains(req.URL.RawQuery, " ") || strings.Contains(req.URL.RawQuery, "/") {
|
||||
t.Errorf("raw query is not encoded: %q", req.URL.RawQuery)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFinishDomainImageDelete(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
status int
|
||||
callErr error
|
||||
wantErr bool
|
||||
}{
|
||||
{"deleted", http.StatusNoContent, nil, false},
|
||||
{"already gone", http.StatusNotFound, errors.New("not found"), false},
|
||||
{"upstream failure", http.StatusInternalServerError, errors.New("upstream"), true},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
resp, body := testImageResponse(tc.status, "response")
|
||||
err := finishDomainImageDelete(resp, tc.callErr)
|
||||
if (err != nil) != tc.wantErr {
|
||||
t.Errorf("err = %v, wantErr %v", err, tc.wantErr)
|
||||
}
|
||||
if !body.closed {
|
||||
t.Error("response body was not closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user