Define reusable response bodies in .api with typed code mappings, per-route overrides and matching OpenAPI schemas. Add application and callback encoders, early response policy selection, and isolated remote-consumer release verification. Release v0.8.0-preview after full race, vet, build and local MySQL workflow acceptance.
124 lines
4.1 KiB
Go
124 lines
4.1 KiB
Go
package response_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"math"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"git.yuntuo.hk/k2safe/go-omni/config"
|
|
"git.yuntuo.hk/k2safe/go-omni/response"
|
|
)
|
|
|
|
func TestCodeTypeWidthsAndWireValues(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
kind string
|
|
min, max int64
|
|
}{
|
|
{"int32", math.MinInt32, math.MaxInt32}, {"int64", math.MinInt64, math.MaxInt64},
|
|
{"integer", -9007199254740991, 9007199254740991},
|
|
} {
|
|
t.Run(tc.kind, func(t *testing.T) {
|
|
cfg := response.Config{CodeType: tc.kind, SuccessCode: tc.max, ErrorCode: tc.min, Codes: map[string]any{"ZERO": 0}}
|
|
formatter, err := response.New(cfg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, sample := range []struct {
|
|
result response.Result
|
|
want int64
|
|
}{
|
|
{response.Result{Status: 200, Code: "OK"}, tc.max},
|
|
{response.Result{Status: 500, Code: "INTERNAL"}, tc.min},
|
|
{response.Result{Status: 409, Code: "ZERO"}, 0},
|
|
} {
|
|
payload, err := formatter.Encode(sample.result)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
decoder := json.NewDecoder(bytes.NewReader(payload.Body))
|
|
decoder.UseNumber()
|
|
var body map[string]any
|
|
if err = decoder.Decode(&body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
code, ok := body["code"].(json.Number)
|
|
if !ok || code.String() != strconv.FormatInt(sample.want, 10) {
|
|
t.Fatalf("precision/type lost: %s", payload.Body)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
for _, tc := range []struct {
|
|
kind string
|
|
value any
|
|
}{
|
|
{"int32", int64(math.MaxInt32) + 1}, {"int32", int64(math.MinInt32) - 1},
|
|
{"int64", json.Number("9223372036854775808")}, {"int64", json.Number("-9223372036854775809")},
|
|
{"int64", uint64(math.MaxUint64)}, {"int64", float64(math.MaxInt64)},
|
|
{"int32", 1.5}, {"int64", json.Number("1.5")}, {"int64", "200"},
|
|
{"string", 200},
|
|
} {
|
|
for _, field := range []string{"successCode", "errorCode", "codes"} {
|
|
cfg := response.Config{CodeType: tc.kind, SuccessCode: 0, ErrorCode: 1}
|
|
if tc.kind == "string" {
|
|
cfg.SuccessCode = "OK"
|
|
cfg.ErrorCode = "ERROR"
|
|
}
|
|
switch field {
|
|
case "successCode":
|
|
cfg.SuccessCode = tc.value
|
|
case "errorCode":
|
|
cfg.ErrorCode = tc.value
|
|
case "codes":
|
|
cfg.Codes = map[string]any{"MAPPED": tc.value}
|
|
}
|
|
if _, err := response.New(cfg); err == nil {
|
|
t.Fatalf("%s %s accepted %#v", tc.kind, field, tc.value)
|
|
}
|
|
}
|
|
}
|
|
for _, kind := range []string{"int32", "int64"} {
|
|
if _, err := response.New(response.Config{CodeType: kind}); err == nil {
|
|
t.Fatalf("%s accepted missing numeric fallbacks", kind)
|
|
}
|
|
}
|
|
f := response.MustNew(response.Config{CodeType: "string", SuccessCode: "SUCCESS", ErrorCode: "ERROR", Codes: map[string]any{"DENIED": "403"}})
|
|
if got := f.Value(response.Result{Status: 403, Code: "DENIED"})["code"]; got != "403" {
|
|
t.Fatal(got)
|
|
}
|
|
}
|
|
|
|
func TestInt64CodesFromConfigurationKeepPrecision(t *testing.T) {
|
|
for ext, body := range map[string]string{
|
|
"json": `{"response":{"codeType":"int64","successCode":9223372036854775807,"errorCode":-9223372036854775808,"codes":{"EXACT":9007199254740993}}}`,
|
|
"yaml": "response:\n codeType: int64\n successCode: 9223372036854775807\n errorCode: -9223372036854775808\n codes:\n EXACT: 9007199254740993\n",
|
|
} {
|
|
path := filepath.Join(t.TempDir(), "config."+ext)
|
|
if err := os.WriteFile(path, []byte(body), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg, err := config.LoadFile(path, "RESPONSE_TEST_")
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", ext, err)
|
|
}
|
|
f := response.MustNew(cfg.Response)
|
|
if f.Config().SuccessCode != int64(math.MaxInt64) || f.Config().ErrorCode != int64(math.MinInt64) || f.Config().Codes["EXACT"] != int64(9007199254740993) {
|
|
t.Fatalf("%s: precision lost: %#v", ext, f.Config())
|
|
}
|
|
}
|
|
var cfg response.Config
|
|
if err := json.Unmarshal([]byte(`{"codeType":"int64","successCode":9223372036854775807,"errorCode":0}`), &cfg); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if response.MustNew(cfg).Config().SuccessCode != int64(math.MaxInt64) {
|
|
t.Fatal("json.Unmarshal lost precision")
|
|
}
|
|
if err := json.Unmarshal([]byte(`{"unknownOption":1}`), &cfg); err == nil {
|
|
t.Fatal("custom decoder bypassed unknown-field validation")
|
|
}
|
|
}
|