package control import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" ) func TestConnectChannelCallbacksConnectedState(t *testing.T) { var callback ConnectionStateCallback api := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/admin/gateway/connections" { t.Fatalf("unexpected callback path: %s", r.URL.Path) } if err := json.NewDecoder(r.Body).Decode(&callback); err != nil { t.Fatalf("decode callback: %v", err) } w.WriteHeader(http.StatusOK) })) defer api.Close() handler := handlerWithDial(api.URL+"/api", func(context.Context, ConnectChannelCommand) error { return nil }) resp := httptest.NewRecorder() req := httptest.NewRequest(http.MethodPost, "/connections/connect", strings.NewReader(validConnectCommand())) handler.ServeHTTP(resp, req) if resp.Code != http.StatusOK { t.Fatalf("unexpected response status: %d body=%s", resp.Code, resp.Body.String()) } if callback.Status != "connected" || callback.CurrentConnections != 2 || callback.DesiredConnections != 2 { t.Fatalf("unexpected callback state: %+v", callback) } if callback.ChannelID != "channel-1" || callback.ConnectionID != "channel-1:primary" { t.Fatalf("unexpected callback identity: %+v", callback) } if callback.LastConnectedAt == "" || callback.LastHeartbeatAt == "" { t.Fatalf("expected connection timestamps: %+v", callback) } } func TestConnectChannelCallbacksFailedState(t *testing.T) { var callback ConnectionStateCallback api := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&callback); err != nil { t.Fatalf("decode callback: %v", err) } w.WriteHeader(http.StatusOK) })) defer api.Close() handler := handlerWithDial(api.URL+"/api", func(context.Context, ConnectChannelCommand) error { return errTestDial }) resp := httptest.NewRecorder() req := httptest.NewRequest(http.MethodPost, "/connections/connect", strings.NewReader(validConnectCommand())) handler.ServeHTTP(resp, req) if resp.Code != http.StatusOK { t.Fatalf("unexpected response status: %d body=%s", resp.Code, resp.Body.String()) } if callback.Status != "failed" || callback.CurrentConnections != 0 || callback.LastError == "" { t.Fatalf("unexpected callback state: %+v", callback) } } func TestConnectChannelRejectsInvalidCommand(t *testing.T) { handler := handlerWithDial("", func(context.Context, ConnectChannelCommand) error { return nil }) resp := httptest.NewRecorder() req := httptest.NewRequest(http.MethodPost, "/connections/connect", strings.NewReader(`{"messageType":"SubmitCommand"}`)) handler.ServeHTTP(resp, req) if resp.Code != http.StatusBadRequest { t.Fatalf("unexpected response status: %d", resp.Code) } } type testDialError struct{} func (testDialError) Error() string { return "dial failed" } var errTestDial testDialError func handlerWithDial(apiBaseURL string, dial DialFunc) http.Handler { mux := http.NewServeMux() Register(mux, Server{APIBaseURL: apiBaseURL, Dial: dial}) return mux } func validConnectCommand() string { return `{ "schemaVersion": "v1", "messageType": "ConnectChannel", "traceId": "trace-1", "channelId": "channel-1", "connectionId": "channel-1:primary", "reason": "channel_created", "desiredConnections": 2, "channel": { "code": "CMPP-A", "name": "主通道", "gatewayHost": "127.0.0.1", "gatewayPort": 17890, "account": "sp", "passwordCipher": "secret", "srcId": "10690000", "cmppVersion": "3.0", "rateLimitPerSecond": 100 } }` }