mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
114 lines
3.1 KiB
Go
114 lines
3.1 KiB
Go
package servermock
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
)
|
|
|
|
// RequestBodyJSONLink validates JSON request bodies.
|
|
type RequestBodyJSONLink struct {
|
|
body []byte
|
|
filename string
|
|
data any
|
|
}
|
|
|
|
// CheckRequestJSONBody creates a [RequestBodyJSONLink] initialized with a string.
|
|
func CheckRequestJSONBody(body string) *RequestBodyJSONLink {
|
|
return &RequestBodyJSONLink{body: []byte(body)}
|
|
}
|
|
|
|
// CheckRequestJSONBodyFromStruct creates a [RequestBodyJSONLink] initialized with a struct.
|
|
func CheckRequestJSONBodyFromStruct(data any) *RequestBodyJSONLink {
|
|
return &RequestBodyJSONLink{data: data}
|
|
}
|
|
|
|
// CheckRequestJSONBodyFromFile creates a [RequestBodyJSONLink] initialized with the provided request body file.
|
|
func CheckRequestJSONBodyFromFile(filename string) *RequestBodyJSONLink {
|
|
return &RequestBodyJSONLink{
|
|
filename: filename,
|
|
}
|
|
}
|
|
|
|
// CheckRequestJSONBodyFromFixture creates a [RequestBodyJSONLink] initialized with the provided request body file from the `fixtures` directory.
|
|
func CheckRequestJSONBodyFromFixture(filename string) *RequestBodyJSONLink {
|
|
return CheckRequestJSONBodyFromFile(filepath.Join("fixtures", filename))
|
|
}
|
|
|
|
// CheckRequestJSONBodyFromInternal creates a [RequestBodyJSONLink] initialized with the provided request body file from the `internal/fixtures` directory.
|
|
func CheckRequestJSONBodyFromInternal(filename string) *RequestBodyJSONLink {
|
|
return CheckRequestJSONBodyFromFile(filepath.Join("internal", "fixtures", filename))
|
|
}
|
|
|
|
func (l *RequestBodyJSONLink) Bind(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
if req.ContentLength == 0 {
|
|
http.Error(rw, fmt.Sprintf("%s: empty request body", req.URL.Path), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
body, err := io.ReadAll(req.Body)
|
|
if err != nil {
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
_ = req.Body.Close()
|
|
|
|
var expected, actual any
|
|
|
|
expectedRaw := slices.Clone(l.body)
|
|
|
|
switch {
|
|
case l.filename != "":
|
|
expectedRaw, err = os.ReadFile(l.filename)
|
|
if err != nil {
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
case l.data != nil:
|
|
expectedRaw, err = json.Marshal(l.data)
|
|
if err != nil {
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(expectedRaw) == 0 {
|
|
http.Error(rw, fmt.Sprintf("%s: empty expected request body", req.URL.Path), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(expectedRaw, &expected)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("%s: the expected request body is not valid JSON: %v", req.URL.Path, err)
|
|
http.Error(rw, msg, http.StatusBadRequest)
|
|
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(body, &actual)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("%s: request body is not valid JSON: %v", req.URL.Path, err)
|
|
http.Error(rw, msg, http.StatusBadRequest)
|
|
|
|
return
|
|
}
|
|
|
|
if !cmp.Equal(actual, expected) {
|
|
msg := fmt.Sprintf("%s: request body differences: %s", req.URL.Path, cmp.Diff(actual, expected))
|
|
http.Error(rw, msg, http.StatusBadRequest)
|
|
|
|
return
|
|
}
|
|
|
|
next.ServeHTTP(rw, req)
|
|
})
|
|
}
|