// responseBean_test.go — HIF-4 F9 护栏: // 所有 HTTP 响应必须是 {code, msg, data} 三字段固定 shape,data 为空时也要 // 显式 "null"(不是丢字段)。App 端强类型 decoder 依赖这个 shape。 package result import ( "encoding/json" "strings" "testing" ) // TestResponseErrorBean_HasDataField 断言错误响应的 JSON 一定包含 data 字段 // 且为 null。如果未来有人给 Data 加了 omitempty,或者删掉 Data 字段,这里会挂。 func TestResponseErrorBean_HasDataField(t *testing.T) { body, err := json.Marshal(Error(40003, "User token is invalid")) if err != nil { t.Fatalf("marshal: %v", err) } s := string(body) if !strings.Contains(s, `"data":null`) { t.Fatalf("F9 regression: error response missing 'data:null', got %s", s) } // 顺带断言 code + msg 也在(防止未来无脑重构删字段) if !strings.Contains(s, `"code":40003`) || !strings.Contains(s, `"msg":"User token is invalid"`) { t.Fatalf("error response missing code/msg, got %s", s) } } // TestResponseSuccessBean_DataAlwaysPresent 断言成功响应在 data 为 nil 时 // 也显式返回 "data":null(不是丢字段)。这是 F9 的另一半——如果有人未来把 // omitempty 加回去,只解决错误响应的对称性会被打破。 func TestResponseSuccessBean_DataAlwaysPresent(t *testing.T) { body, err := json.Marshal(Success(nil)) if err != nil { t.Fatalf("marshal: %v", err) } s := string(body) if !strings.Contains(s, `"data":null`) { t.Fatalf("F9 regression: Success(nil) missing 'data:null' (omitempty regression?), got %s", s) } } // TestResponseSuccessBean_WithPayload 副断言:非 nil payload 时 // data 字段确实带上,且序列化正常。 func TestResponseSuccessBean_WithPayload(t *testing.T) { body, err := json.Marshal(Success(map[string]any{"id": 42, "name": "abc"})) if err != nil { t.Fatalf("marshal: %v", err) } s := string(body) if !strings.Contains(s, `"data":{`) || !strings.Contains(s, `"id":42`) { t.Fatalf("success payload not serialized correctly, got %s", s) } }