package matrix import ( "errors" "fmt" "log" "net/url" "os" "testing" "github.com/jarcoal/httpmock" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/nicholas-fedor/shoutrrr/internal/testutils" ) func TestMatrix(t *testing.T) { gomega.RegisterFailHandler(ginkgo.Fail) ginkgo.RunSpecs(t, "Shoutrrr Matrix Suite") } var _ = ginkgo.Describe("the matrix service", func() { var service *Service logger := log.New(ginkgo.GinkgoWriter, "Test", log.LstdFlags) envMatrixURL := os.Getenv("SHOUTRRR_MATRIX_URL") ginkgo.BeforeEach(func() { service = &Service{} }) ginkgo.When("running integration tests", func() { ginkgo.It("should not error out", func() { // Tests matrix_client.go lines: // - 36-52: newClient (full initialization with logger and scheme) // - 63-65: login (via Initialize when User is set) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (via Send with real server) // - 156-173: sendMessageToRoom (sending to joined rooms) if envMatrixURL == "" { return } serviceURL, err := url.Parse(envMatrixURL) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("This is an integration test message", nil) gomega.Expect(err).NotTo(gomega.HaveOccurred()) }) }) ginkgo.Describe("creating configurations", func() { ginkgo.When("given an url with title prop", func() { ginkgo.It("should not throw an error", func() { // Tests matrix_config.go, not matrix_client.go directly // Related to Config.SetURL, which feeds into client setup later serviceURL := testutils.URLMust( `matrix://user:pass@mockserver?rooms=room1&title=Better%20Off%20Alone`, ) gomega.Expect((&Config{}).SetURL(serviceURL)).To(gomega.Succeed()) }) }) ginkgo.When("given an url with the prop `room`", func() { ginkgo.It("should treat is as an alias for `rooms`", func() { // Tests matrix_config.go, not matrix_client.go directly // Configures Rooms for client.sendToExplicitRooms later serviceURL := testutils.URLMust(`matrix://user:pass@mockserver?room=room1`) config := Config{} gomega.Expect(config.SetURL(serviceURL)).To(gomega.Succeed()) gomega.Expect(config.Rooms).To(gomega.ContainElement("#room1")) }) }) ginkgo.When("given an url with invalid props", func() { ginkgo.It("should return an error", func() { // Tests matrix_config.go, not matrix_client.go directly // Ensures invalid params fail before reaching client serviceURL := testutils.URLMust( `matrix://user:pass@mockserver?channels=room1,room2`, ) gomega.Expect((&Config{}).SetURL(serviceURL)).To(gomega.HaveOccurred()) }) }) ginkgo.When("parsing the configuration URL", func() { ginkgo.It("should be identical after de-/serialization", func() { // Tests matrix_config.go, not matrix_client.go directly // Verifies Config.GetURL/SetURL round-trip for client init testURL := "matrix://user:pass@mockserver?rooms=%23room1%2C%23room2" url, err := url.Parse(testURL) gomega.Expect(err).NotTo(gomega.HaveOccurred(), "parsing") config := &Config{} err = config.SetURL(url) gomega.Expect(err).NotTo(gomega.HaveOccurred(), "verifying") outputURL := config.GetURL() gomega.Expect(outputURL.String()).To(gomega.Equal(testURL)) }) }) }) ginkgo.Describe("the matrix client", func() { ginkgo.BeforeEach(func() { httpmock.Activate() }) ginkgo.When("not providing a logger", func() { ginkgo.It("should not crash", func() { // Tests matrix_client.go lines: // - 36-52: newClient (sets DiscardLogger when logger is nil) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) setupMockResponders() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") gomega.Expect(service.Initialize(serviceURL, nil)).To(gomega.Succeed()) }) }) ginkgo.When("sending a message", func() { ginkgo.It("should not report any errors", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (routes to sendToJoinedRooms) // - 134-153: sendToJoinedRooms (sends to joined rooms) // - 156-173: sendMessageToRoom (successful send) // - 225-242: getJoinedRooms (fetches room list) setupMockResponders() serviceURL, _ := url.Parse("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).NotTo(gomega.HaveOccurred()) }) }) ginkgo.When("sending a message to explicit rooms", func() { ginkgo.It("should not report any errors", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (routes to sendToExplicitRooms) // - 112-133: sendToExplicitRooms (sends to explicit rooms) // - 177-192: joinRoom (joins rooms successfully) // - 156-173: sendMessageToRoom (successful send) setupMockResponders() serviceURL, _ := url.Parse("matrix://user:pass@mockserver?rooms=room1,room2") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).NotTo(gomega.HaveOccurred()) }) ginkgo.When("sending to one room fails", func() { ginkgo.It("should report one error", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (routes to sendToExplicitRooms) // - 112-133: sendToExplicitRooms (handles join failure) // - 177-192: joinRoom (fails for "secret" room) // - 156-173: sendMessageToRoom (succeeds for "room2") setupMockResponders() serviceURL, _ := url.Parse("matrix://user:pass@mockserver?rooms=secret,room2") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).To(gomega.HaveOccurred()) }) }) }) ginkgo.When("disabling TLS", func() { ginkgo.It("should use HTTP instead of HTTPS", func() { // Tests matrix_client.go lines: // - 36-52: newClient (specifically line 50: c.apiURL.Scheme = c.apiURL.Scheme[:schemeHTTPPrefixLength]) // - 63-65: login (successful initialization over HTTP) // - 76-87: loginPassword (successful login flow) setupMockRespondersHTTP() serviceURL := testutils.URLMust("matrix://user:pass@mockserver?disableTLS=yes") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) gomega.Expect(service.client.apiURL.Scheme).To(gomega.Equal("http")) }) }) ginkgo.When("failing to get login flows", func() { ginkgo.It("should return an error", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-69: login (specifically line 69: return fmt.Errorf("failed to get login flows: %w", err)) // - 175-223: apiGet (returns error due to 500 response) setupMockRespondersLoginFail() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()).To(gomega.ContainSubstring("failed to get login flows")) }) }) ginkgo.When("no supported login flows are available", func() { ginkgo.It("should return an error with unsupported flows", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-87: login (specifically line 84: return fmt.Errorf("none of the server login flows are supported: %v", strings.Join(flows, ", "))) // - 175-223: apiGet (successful GET with unsupported flows) setupMockRespondersUnsupportedFlows() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()). To(gomega.Equal("none of the server login flows are supported: m.login.dummy")) }) }) ginkgo.When("using a token instead of login", func() { ginkgo.It("should initialize without errors", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 59-60: useToken (sets token and calls updateAccessToken) // - 244-248: updateAccessToken (updates URL query with token) setupMockResponders() // Minimal mocks for initialization serviceURL := testutils.URLMust("matrix://:token@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) gomega.Expect(service.client.accessToken).To(gomega.Equal("token")) gomega.Expect(service.client.apiURL.RawQuery).To(gomega.Equal("access_token=token")) }) }) ginkgo.When("failing to get joined rooms", func() { ginkgo.It("should return an error", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (routes to sendToJoinedRooms) // - 134-154: sendToJoinedRooms (specifically lines 137 and 154: error handling for getJoinedRooms failure) // - 225-267: getJoinedRooms (specifically line 267: return []string{}, err) setupMockRespondersJoinedRoomsFail() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()).To(gomega.ContainSubstring("failed to get joined rooms")) }) }) ginkgo.When("failing to join a room", func() { ginkgo.It("should skip to the next room and continue", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (routes to sendToExplicitRooms) // - 112-133: sendToExplicitRooms (specifically line 147: continue on join failure) // - 177-192: joinRoom (specifically line 188: return "", err on failure) // - 156-173: sendMessageToRoom (succeeds for second room) setupMockRespondersJoinFail() serviceURL := testutils.URLMust("matrix://user:pass@mockserver?rooms=secret,room2") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()).To(gomega.ContainSubstring("error joining room")) }) }) ginkgo.When("failing to marshal request in apiPost", func() { ginkgo.It("should return an error", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 195-252: apiPost (specifically line 208: body, err = json.Marshal(request) fails) setupMockResponders() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.client.apiPost("/test/path", make(chan int), nil) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()). To(gomega.ContainSubstring("json: unsupported type: chan int")) }) }) ginkgo.When("failing to read response body in apiPost", func() { ginkgo.It("should return an error", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (routes to sendToJoinedRooms) // - 134-153: sendToJoinedRooms (calls sendMessageToRoom) // - 156-173: sendMessageToRoom (calls apiPost) // - 195-252: apiPost (specifically lines 204, 223, 230: res handling and body read failure) setupMockRespondersBodyFail() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()). To(gomega.ContainSubstring("failed to read response body")) }) }) ginkgo.When("routing to explicit rooms at line 94", func() { ginkgo.It("should use sendToExplicitRooms", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (specifically line 94: if len(rooms) >= minSliceLength { true branch) // - 112-133: sendToExplicitRooms (sends to explicit rooms) // - 177-192: joinRoom (joins rooms successfully) // - 156-173: sendMessageToRoom (successful send) setupMockResponders() serviceURL := testutils.URLMust("matrix://user:pass@mockserver?rooms=room1") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).NotTo(gomega.HaveOccurred()) }) }) ginkgo.When("routing to joined rooms at line 94", func() { ginkgo.It("should use sendToJoinedRooms", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (specifically line 94: if len(rooms) >= minSliceLength { false branch) // - 134-153: sendToJoinedRooms (sends to joined rooms) // - 156-173: sendMessageToRoom (successful send) // - 225-242: getJoinedRooms (fetches room list) setupMockResponders() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).NotTo(gomega.HaveOccurred()) }) }) ginkgo.When("appending joined rooms error at line 137", func() { ginkgo.It("should append the error", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (routes to sendToJoinedRooms) // - 134-154: sendToJoinedRooms (specifically line 137: errors = append(errors, fmt.Errorf("failed to get joined rooms: %w", err))) // - 225-267: getJoinedRooms (returns error) setupMockRespondersJoinedRoomsFail() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()).To(gomega.ContainSubstring("failed to get joined rooms")) }) }) ginkgo.When("failing to join room at line 188", func() { ginkgo.It("should return join error", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (routes to sendToExplicitRooms) // - 112-133: sendToExplicitRooms (calls joinRoom) // - 177-192: joinRoom (specifically line 188: return "", err) setupMockRespondersJoinFail() serviceURL := testutils.URLMust("matrix://user:pass@mockserver?rooms=secret") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()).To(gomega.ContainSubstring("error joining room")) }) }) ginkgo.When("declaring response variable at line 204", func() { ginkgo.It("should handle HTTP failure", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 195-252: apiPost (specifically line 204: var res *http.Response and error handling) setupMockRespondersPostFail() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.client.apiPost( "/test/path", apiReqSend{MsgType: msgTypeText, Body: "test"}, nil, ) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()).To(gomega.ContainSubstring("simulated HTTP failure")) }) }) ginkgo.When("marshaling request fails at line 208", func() { ginkgo.It("should return marshal error", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 195-252: apiPost (specifically line 208: body, err = json.Marshal(request)) setupMockResponders() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.client.apiPost("/test/path", make(chan int), nil) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()). To(gomega.ContainSubstring("json: unsupported type: chan int")) }) }) ginkgo.When("getting query at line 244", func() { ginkgo.It("should update token in URL", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 59-60: useToken (calls updateAccessToken) // - 244-248: updateAccessToken (specifically line 244: query := c.apiURL.Query()) setupMockResponders() serviceURL := testutils.URLMust("matrix://:token@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) gomega.Expect(service.client.apiURL.RawQuery).To(gomega.Equal("access_token=token")) service.client.useToken("newtoken") gomega.Expect(service.client.apiURL.RawQuery). To(gomega.Equal("access_token=newtoken")) }) }) ginkgo.When("checking body read error at line 251", func() { ginkgo.It("should return read error", func() { // Tests matrix_client.go lines: // - 36-52: newClient (successful setup) // - 63-65: login (successful initialization) // - 76-87: loginPassword (successful login flow) // - 91-108: sendMessage (routes to sendToJoinedRooms) // - 134-153: sendToJoinedRooms (calls sendMessageToRoom) // - 156-173: sendMessageToRoom (calls apiPost) // - 195-252: apiPost (specifically line 251: if err != nil { after io.ReadAll) setupMockRespondersBodyFail() serviceURL := testutils.URLMust("matrix://user:pass@mockserver") err := service.Initialize(serviceURL, logger) gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = service.Send("Test message", nil) gomega.Expect(err).To(gomega.HaveOccurred()) gomega.Expect(err.Error()). To(gomega.ContainSubstring("failed to read response body")) }) }) ginkgo.AfterEach(func() { httpmock.DeactivateAndReset() }) }) ginkgo.It("should implement basic service API methods correctly", func() { // Tests matrix_config.go, not matrix_client.go directly // Exercises Config methods used indirectly by client initialization testutils.TestConfigGetInvalidQueryValue(&Config{}) testutils.TestConfigSetInvalidQueryValue(&Config{}, "matrix://user:pass@host/?foo=bar") testutils.TestConfigGetEnumsCount(&Config{}, 0) testutils.TestConfigGetFieldsCount(&Config{}, 4) }) ginkgo.It("should return the correct service ID", func() { service := &Service{} gomega.Expect(service.GetID()).To(gomega.Equal("matrix")) }) }) // setupMockResponders for HTTPS. func setupMockResponders() { const mockServer = "https://mockserver" httpmock.RegisterResponder( "GET", mockServer+apiLogin, httpmock.NewStringResponder(200, `{"flows": [ { "type": "m.login.password" } ] }`)) httpmock.RegisterResponder( "POST", mockServer+apiLogin, httpmock.NewStringResponder( 200, `{ "access_token": "TOKEN", "home_server": "mockserver", "user_id": "test:mockerserver" }`, ), ) httpmock.RegisterResponder( "GET", mockServer+apiJoinedRooms, httpmock.NewStringResponder(200, `{ "joined_rooms": [ "!room:mockserver" ] }`)) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiSendMessage, "%21room:mockserver"), httpmock.NewJsonResponderOrPanic(200, apiResEvent{EventID: "7"})) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiSendMessage, "1"), httpmock.NewJsonResponderOrPanic(200, apiResEvent{EventID: "8"})) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiSendMessage, "2"), httpmock.NewJsonResponderOrPanic(200, apiResEvent{EventID: "9"})) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiRoomJoin, "%23room1"), httpmock.NewJsonResponderOrPanic(200, apiResRoom{RoomID: "1"})) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiRoomJoin, "%23room2"), httpmock.NewJsonResponderOrPanic(200, apiResRoom{RoomID: "2"})) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiRoomJoin, "%23secret"), httpmock.NewJsonResponderOrPanic(403, apiResError{ Code: "M_FORBIDDEN", Message: "You are not invited to this room.", })) } // setupMockRespondersHTTP for HTTP. func setupMockRespondersHTTP() { const mockServer = "http://mockserver" httpmock.RegisterResponder( "GET", mockServer+apiLogin, httpmock.NewStringResponder(200, `{"flows": [ { "type": "m.login.password" } ] }`)) httpmock.RegisterResponder( "POST", mockServer+apiLogin, httpmock.NewStringResponder( 200, `{ "access_token": "TOKEN", "home_server": "mockserver", "user_id": "test:mockerserver" }`, ), ) httpmock.RegisterResponder( "GET", mockServer+apiJoinedRooms, httpmock.NewStringResponder(200, `{ "joined_rooms": [ "!room:mockserver" ] }`)) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiSendMessage, "%21room:mockserver"), httpmock.NewJsonResponderOrPanic(200, apiResEvent{EventID: "7"})) } // setupMockRespondersLoginFail for testing line 69. func setupMockRespondersLoginFail() { const mockServer = "https://mockserver" httpmock.RegisterResponder( "GET", mockServer+apiLogin, httpmock.NewStringResponder(500, `{"error": "Internal Server Error"}`)) } // setupMockRespondersUnsupportedFlows for testing line 84. func setupMockRespondersUnsupportedFlows() { const mockServer = "https://mockserver" httpmock.RegisterResponder( "GET", mockServer+apiLogin, httpmock.NewStringResponder(200, `{"flows": [ { "type": "m.login.dummy" } ] }`)) } // setupMockRespondersJoinedRoomsFail for testing lines 137, 154, and 267. func setupMockRespondersJoinedRoomsFail() { const mockServer = "https://mockserver" httpmock.RegisterResponder( "GET", mockServer+apiLogin, httpmock.NewStringResponder(200, `{"flows": [ { "type": "m.login.password" } ] }`)) httpmock.RegisterResponder( "POST", mockServer+apiLogin, httpmock.NewStringResponder( 200, `{ "access_token": "TOKEN", "home_server": "mockserver", "user_id": "test:mockerserver" }`, ), ) httpmock.RegisterResponder( "GET", mockServer+apiJoinedRooms, httpmock.NewStringResponder(500, `{"error": "Internal Server Error"}`)) } // setupMockRespondersJoinFail for testing lines 147 and 188. func setupMockRespondersJoinFail() { const mockServer = "https://mockserver" httpmock.RegisterResponder( "GET", mockServer+apiLogin, httpmock.NewStringResponder(200, `{"flows": [ { "type": "m.login.password" } ] }`)) httpmock.RegisterResponder( "POST", mockServer+apiLogin, httpmock.NewStringResponder( 200, `{ "access_token": "TOKEN", "home_server": "mockserver", "user_id": "test:mockerserver" }`, ), ) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiRoomJoin, "%23secret"), httpmock.NewJsonResponderOrPanic(403, apiResError{ Code: "M_FORBIDDEN", Message: "You are not invited to this room.", })) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiRoomJoin, "%23room2"), httpmock.NewJsonResponderOrPanic(200, apiResRoom{RoomID: "2"})) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiSendMessage, "2"), httpmock.NewJsonResponderOrPanic(200, apiResEvent{EventID: "9"})) } // setupMockRespondersBodyFail for testing lines 204, 223, and 230. func setupMockRespondersBodyFail() { const mockServer = "https://mockserver" httpmock.RegisterResponder( "GET", mockServer+apiLogin, httpmock.NewStringResponder(200, `{"flows": [ { "type": "m.login.password" } ] }`)) httpmock.RegisterResponder( "POST", mockServer+apiLogin, httpmock.NewStringResponder( 200, `{ "access_token": "TOKEN", "home_server": "mockserver", "user_id": "test:mockerserver" }`, ), ) httpmock.RegisterResponder( "GET", mockServer+apiJoinedRooms, httpmock.NewStringResponder(200, `{ "joined_rooms": [ "!room:mockserver" ] }`)) httpmock.RegisterResponder("POST", mockServer+fmt.Sprintf(apiSendMessage, "%21room:mockserver"), httpmock.NewErrorResponder(errors.New("failed to read response body"))) } // setupMockRespondersPostFail for testing line 204 and HTTP failure. func setupMockRespondersPostFail() { const mockServer = "https://mockserver" httpmock.RegisterResponder( "GET", mockServer+apiLogin, httpmock.NewStringResponder(200, `{"flows": [ { "type": "m.login.password" } ] }`)) httpmock.RegisterResponder( "POST", mockServer+apiLogin, httpmock.NewStringResponder( 200, `{ "access_token": "TOKEN", "home_server": "mockserver", "user_id": "test:mockerserver" }`, ), ) httpmock.RegisterResponder("POST", mockServer+"/test/path", httpmock.NewErrorResponder(errors.New("simulated HTTP failure"))) }