00634fb648
* api/render: initial implementation of the package * acme/api: refactored to support api/render * authority/admin: refactored to support api/render * ca: refactored to support api/render * api: refactored to support api/render * api/render: implemented Error * api: refactored to support api/render.Error * acme/api: refactored to support api/render.Error * authority/admin: refactored to support api/render.Error * ca: refactored to support api/render.Error * ca: fixed broken tests * api/render, api/log: moved error logging to this package * acme: refactored Error so that it implements render.RenderableError * authority/admin: refactored Error so that it implements render.RenderableError * api/render: implemented RenderableError * api/render: added test coverage for Error * api/render: implemented statusCodeFromError * api: refactored RootsPEM to work with render.Error * acme, authority/admin: fixed pointer receiver name for consistency * api/render, errs: moved StatusCoder & StackTracer to the render package
115 lines
2.4 KiB
Go
115 lines
2.4 KiB
Go
package render
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/smallstep/certificates/logging"
|
|
)
|
|
|
|
func TestJSON(t *testing.T) {
|
|
rec := httptest.NewRecorder()
|
|
rw := logging.NewResponseLogger(rec)
|
|
|
|
JSON(rw, map[string]interface{}{"foo": "bar"})
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Result().StatusCode)
|
|
assert.Equal(t, "application/json", rec.Header().Get("Content-Type"))
|
|
assert.Equal(t, "{\"foo\":\"bar\"}\n", rec.Body.String())
|
|
|
|
assert.Empty(t, rw.Fields())
|
|
}
|
|
|
|
func TestJSONPanics(t *testing.T) {
|
|
assert.Panics(t, func() {
|
|
JSON(httptest.NewRecorder(), make(chan struct{}))
|
|
})
|
|
}
|
|
|
|
type renderableError struct {
|
|
Code int `json:"-"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func (err renderableError) Error() string {
|
|
return err.Message
|
|
}
|
|
|
|
func (err renderableError) Render(w http.ResponseWriter) {
|
|
w.Header().Set("Content-Type", "something/custom")
|
|
|
|
JSONStatus(w, err, err.Code)
|
|
}
|
|
|
|
type statusedError struct {
|
|
Contents string
|
|
}
|
|
|
|
func (err statusedError) Error() string { return err.Contents }
|
|
|
|
func (statusedError) StatusCode() int { return 432 }
|
|
|
|
func TestError(t *testing.T) {
|
|
cases := []struct {
|
|
err error
|
|
code int
|
|
body string
|
|
header string
|
|
}{
|
|
0: {
|
|
err: renderableError{532, "some string"},
|
|
code: 532,
|
|
body: "{\"message\":\"some string\"}\n",
|
|
header: "something/custom",
|
|
},
|
|
1: {
|
|
err: statusedError{"123"},
|
|
code: 432,
|
|
body: "{\"Contents\":\"123\"}\n",
|
|
header: "application/json",
|
|
},
|
|
}
|
|
|
|
for caseIndex := range cases {
|
|
kase := cases[caseIndex]
|
|
|
|
t.Run(strconv.Itoa(caseIndex), func(t *testing.T) {
|
|
rec := httptest.NewRecorder()
|
|
|
|
Error(rec, kase.err)
|
|
|
|
assert.Equal(t, kase.code, rec.Result().StatusCode)
|
|
assert.Equal(t, kase.body, rec.Body.String())
|
|
assert.Equal(t, kase.header, rec.Header().Get("Content-Type"))
|
|
})
|
|
}
|
|
}
|
|
|
|
type causedError struct {
|
|
cause error
|
|
}
|
|
|
|
func (err causedError) Error() string { return fmt.Sprintf("cause: %s", err.cause) }
|
|
func (err causedError) Cause() error { return err.cause }
|
|
|
|
func TestStatusCodeFromError(t *testing.T) {
|
|
cases := []struct {
|
|
err error
|
|
exp int
|
|
}{
|
|
0: {nil, http.StatusInternalServerError},
|
|
1: {io.EOF, http.StatusInternalServerError},
|
|
2: {statusedError{"123"}, 432},
|
|
3: {causedError{statusedError{"432"}}, 432},
|
|
}
|
|
|
|
for caseIndex, kase := range cases {
|
|
assert.Equal(t, kase.exp, statusCodeFromError(kase.err), "case: %d", caseIndex)
|
|
}
|
|
}
|