package integration import ( "context" "encoding/json" "testing" ) type integrationConfigRepoTestDouble struct { saves int } func (*integrationConfigRepoTestDouble) ListIntegrationConfigs(context.Context, string) ([]*IntegrationConfig, error) { return nil, nil } func (*integrationConfigRepoTestDouble) FindIntegrationConfig(context.Context, string, string) (*IntegrationConfig, error) { return nil, nil } func (r *integrationConfigRepoTestDouble) SaveIntegrationConfig(context.Context, *IntegrationConfig) error { r.saves++ return nil } func (*integrationConfigRepoTestDouble) DeleteIntegrationConfig(context.Context, string, string) error { return nil } type integrationConnectionTesterDouble struct { calls int config *IntegrationConfig } func (t *integrationConnectionTesterDouble) TestIntegration(_ context.Context, config *IntegrationConfig) error { t.calls++ t.config = config return nil } func TestCommunicationIntegrationDefinitionsAndValidation(t *testing.T) { for _, target := range []struct{ kind, provider string }{ {IntegrationKindMQ, "emqx"}, {IntegrationKindMQ, "rabbitmq"}, {IntegrationKindWebSocket, "melody"}, } { values := DefaultIntegrationConfig(target.kind, target.provider) if len(values) == 0 { t.Fatalf("default config missing for %s/%s", target.kind, target.provider) } if err := ValidateIntegrationConfig(target.kind, target.provider, values); err != nil { t.Fatalf("default config invalid for %s/%s: %v", target.kind, target.provider, err) } } } func TestCommunicationIntegrationValidationRejectsInvalidValues(t *testing.T) { rabbit := DefaultIntegrationConfig(IntegrationKindMQ, "rabbitmq") rabbit["port"] = 0 if err := ValidateIntegrationConfig(IntegrationKindMQ, "rabbitmq", rabbit); err == nil { t.Fatal("invalid rabbitmq port was accepted") } websocket := DefaultIntegrationConfig(IntegrationKindWebSocket, "melody") websocket["path"] = "ws" if err := ValidateIntegrationConfig(IntegrationKindWebSocket, "melody", websocket); err == nil { t.Fatal("invalid websocket path was accepted") } } func TestIntegrationConfigTestDoesNotPersistCandidate(t *testing.T) { repo := &integrationConfigRepoTestDouble{} tester := &integrationConnectionTesterDouble{} usecase := NewIntegrationConfigUsecase(repo, tester) raw, _ := json.Marshal(map[string]any{"path": "/candidate"}) err := usecase.Test(context.Background(), &IntegrationConfig{ Kind: " WebSocket ", Provider: " Melody ", Enabled: false, Values: raw, }) if err != nil { t.Fatal(err) } if repo.saves != 0 { t.Fatalf("candidate config was persisted %d times", repo.saves) } if tester.calls != 1 || tester.config == nil { t.Fatalf("connection tester calls = %d, config = %#v", tester.calls, tester.config) } if tester.config.Kind != IntegrationKindWebSocket || tester.config.Provider != "melody" || !tester.config.Enabled { t.Fatalf("tested config = %#v", tester.config) } values := map[string]any{} if err = json.Unmarshal(tester.config.Values, &values); err != nil { t.Fatal(err) } if values["path"] != "/candidate" || values["write_wait"] != "10s" { t.Fatalf("tested values = %#v", values) } } func TestIntegrationConfigRejectsJSONNull(t *testing.T) { repo := &integrationConfigRepoTestDouble{} usecase := NewIntegrationConfigUsecase(repo, &integrationConnectionTesterDouble{}) if err := usecase.Save(context.Background(), &IntegrationConfig{Kind: IntegrationKindMQ, Provider: "emqx", Values: json.RawMessage("null")}); err == nil { t.Fatal("Save() accepted JSON null as an object") } if err := usecase.Test(context.Background(), &IntegrationConfig{Kind: IntegrationKindMQ, Provider: "emqx", Values: json.RawMessage("null")}); err == nil { t.Fatal("Test() accepted JSON null as an object") } }