package response_test import ( "encoding/json" "errors" "reflect" "strings" "testing" "git.yuntuo.hk/k2safe/go-omni/response" ) func TestResponseBodyTemplates(t *testing.T) { config := response.Config{ Body: map[string]any{"success": "${ok}", "payload": "${data}", "meta": map[string]any{"trace": "${requestId}", "status": "${status}"}, "literal": "$${data}", "version": 1}, ErrorBody: map[string]any{"success": "${ok}", "error": map[string]any{"id": "${code}", "reason": "${message}"}}, CodeType: "integer", SuccessCode: 0, ErrorCode: 9999, Codes: map[string]any{"DENIED": 403001}, } f := response.MustNew(config) config.Body["meta"].(map[string]any)["status"] = "mutated" f.Config().ErrorBody["error"].(map[string]any)["reason"] = "mutated" good := f.Value(response.Result{Status: 201, Code: "CREATED", Data: []string{"a"}}) want := map[string]any{"success": true, "payload": []string{"a"}, "meta": map[string]any{"status": 201}, "literal": "${data}", "version": 1} if !reflect.DeepEqual(good, want) { t.Fatalf("%#v", good) } bad := f.Value(response.Result{Status: 403, Code: "DENIED", Message: "denied", Err: errors.New("PRIVATE")}) want = map[string]any{"success": false, "error": map[string]any{"id": int64(403001), "reason": "denied"}} if !reflect.DeepEqual(bad, want) { t.Fatalf("%#v", bad) } // Returning or serializing configuration preserves reusable templates, even {}. for _, cfg := range []response.Config{f.Config(), {Body: map[string]any{}, ErrorBody: map[string]any{}}, {Body: map[string]any{"values": []any{"${data}", true, nil}}}} { before := response.MustNew(cfg).Value(response.Result{Status: 200}) data, err := json.Marshal(cfg) if err != nil { t.Fatal(err) } var copy response.Config if err = json.Unmarshal(data, ©); err != nil { t.Fatal(err) } a, _ := json.Marshal(before) b, _ := json.Marshal(response.MustNew(copy).Value(response.Result{Status: 200})) if string(a) != string(b) { t.Fatalf("round trip: %s / %s", a, b) } } cyclic := map[string]any{} cyclic["cycle"] = cyclic for _, cfg := range []response.Config{ {Body: map[string]any{"value": "${missing}"}}, {Body: map[string]any{"value": "${data}-suffix"}}, {Body: cyclic}, {Body: map[string]any{"value": make(chan int)}}, {Body: map[string]any{}, DataField: "data"}, {ErrorBody: map[string]any{}}, } { if _, err := response.New(cfg.Clone()); err == nil { t.Fatal("invalid template accepted") } } if strings.Contains(string(mustJSON(t, bad)), "PRIVATE") { t.Fatal("private cause leaked") } } func mustJSON(t *testing.T, value any) []byte { t.Helper() data, err := json.Marshal(value) if err != nil { t.Fatal(err) } return data }