1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
package api
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// TestWriteJSON_Success covers the happy path: a value that encodes cleanly
// should reach the client with the caller-supplied status and the expected
// Content-Type header. This guards against accidental regressions in the
// buffered-encode path (e.g. forgetting to write buf.Bytes()).
func TestWriteJSON_Success(t *testing.T) {
rr := httptest.NewRecorder()
writeJSON(rr, http.StatusCreated, map[string]string{"hello": "world"})
if rr.Code != http.StatusCreated {
t.Fatalf("expected status %d, got %d", http.StatusCreated, rr.Code)
}
if ct := rr.Header().Get("Content-Type"); ct != "application/json" {
t.Fatalf("expected Content-Type application/json, got %q", ct)
}
var decoded map[string]string
if err := json.Unmarshal(rr.Body.Bytes(), &decoded); err != nil {
t.Fatalf("body is not valid JSON: %v (body=%q)", err, rr.Body.String())
}
if decoded["hello"] != "world" {
t.Fatalf("expected hello=world, got %v", decoded)
}
}
// TestWriteJSON_EncodeFailureYields500 is the regression test for the bug
// fixed in y9: previously writeJSON wrote the status header BEFORE attempting
// to encode the body, so a value that json cannot marshal (channels, funcs)
// produced a 200 with a corrupt body. After the refactor we marshal into a
// bytes.Buffer first; if that fails we MUST emit 500 plus a fallback JSON
// error payload instead of the caller's success status.
func TestWriteJSON_EncodeFailureYields500(t *testing.T) {
rr := httptest.NewRecorder()
// chan int is not marshalable by encoding/json; this forces the failure
// branch without needing a custom MarshalJSON implementation.
unmarshalable := make(chan int)
writeJSON(rr, http.StatusOK, unmarshalable)
if rr.Code != http.StatusInternalServerError {
t.Fatalf("expected status %d on encode failure, got %d", http.StatusInternalServerError, rr.Code)
}
if ct := rr.Header().Get("Content-Type"); ct != "application/json" {
t.Fatalf("expected Content-Type application/json, got %q", ct)
}
body := rr.Body.String()
// Sanity: body must be valid JSON and look like an error envelope.
if !strings.Contains(body, `"error"`) {
t.Fatalf("expected fallback error body to contain \"error\" key, got %q", body)
}
var decoded map[string]string
if err := json.Unmarshal([]byte(body), &decoded); err != nil {
t.Fatalf("fallback body is not valid JSON: %v (body=%q)", err, body)
}
if decoded["error"] == "" {
t.Fatalf("expected non-empty error field, got %v", decoded)
}
}
|