package web_test import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "testing" "time" "code.severnaya.net/kordophone-mock/v2/model" "code.severnaya.net/kordophone-mock/v2/server" "code.severnaya.net/kordophone-mock/v2/web" ) func TestVersion(t *testing.T) { s := httptest.NewServer(web.NewMockHTTPServer(web.MockHTTPServerConfiguration{})) resp, err := http.Get(s.URL + "/version") if err != nil { t.Fatalf("TestVersion error: %s", err) } body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Error decoding body: %s", body) } if string(body) != server.VERSION { t.Fatalf("Unexpected return value: %s (expected %s)", body, "1.0") } } func TestStatus(t *testing.T) { s := httptest.NewServer(web.NewMockHTTPServer(web.MockHTTPServerConfiguration{})) resp, err := http.Get(s.URL + "/status") if err != nil { t.Fatalf("TestStatus error: %s", err) } body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Error decoding body: %s", body) } if string(body) != "OK" { t.Fatalf("Unexpected return value: %s (expected %s)", body, "OK") } } func TestConversations(t *testing.T) { server := web.NewMockHTTPServer(web.MockHTTPServerConfiguration{}) httpServer := httptest.NewServer(server) conversation := model.Conversation{ Date: time.Now(), Participants: []string{"Alice", "Bob"}, UnreadCount: 1, LastMessagePreview: "Hello world", Guid: "1234567890", } server.Server.AddConversation(conversation) resp, err := http.Get(httpServer.URL + "/conversations") if err != nil { t.Fatalf("TestConversations error: %s", err) } body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Error decoding body: %s", body) } var convos []model.Conversation err = json.Unmarshal(body, &convos) if err != nil { t.Fatalf("Error unmarshalling JSON: %s", err) } if len(convos) != 1 { t.Fatalf("Unexpected number of conversations: %d (expected %d)", len(convos), 1) } testConversation := &convos[0] if testConversation.Equal(&conversation) != true { t.Fatalf("Unexpected conversation: %v (expected %v)", convos[0], conversation) } } func TestMessages(t *testing.T) { server := web.NewMockHTTPServer(web.MockHTTPServerConfiguration{}) httpServer := httptest.NewServer(server) const sender = "Alice" const text = "This is a test." conversation := model.Conversation{ Date: time.Now(), Participants: []string{sender}, UnreadCount: 1, Guid: "1234567890", } server.Server.AddConversation(conversation) message := model.Message{ Text: text, Sender: &conversation.Participants[0], Date: time.Now(), } server.Server.AppendMessageToConversation(&conversation, message) resp, err := http.Get(httpServer.URL + "/messages?guid=" + *&conversation.Guid) if err != nil { t.Fatalf("TestMessages error: %s", err) } body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Error decoding body: %s", body) } var messages []model.Message err = json.Unmarshal(body, &messages) if err != nil { t.Fatalf("Error unmarshalling JSON: %s", err) } if len(messages) != 1 { t.Fatalf("Unexpected num messages: %d (expected %d)", len(messages), 1) } fetchedMessage := messages[0] if fetchedMessage.Text != message.Text { t.Fatalf("Unexpected message text: %s (expected %s)", fetchedMessage.Text, message.Text) } if *fetchedMessage.Sender != *message.Sender { t.Fatalf("Unexpected message sender: %s (expected %s)", *fetchedMessage.Sender, *message.Sender) } } func TestAuthentication(t *testing.T) { s := web.NewMockHTTPServer(web.MockHTTPServerConfiguration{AuthEnabled: true}) httpServer := httptest.NewServer(s) // First, try authenticated request and make sure it fails resp, err := http.Get(httpServer.URL + "/status") if err != nil { t.Fatalf("TestAuthentication status error: %s", err) } if resp.StatusCode != http.StatusUnauthorized { t.Fatalf("Unexpected status code: %d (expected %d)", resp.StatusCode, http.StatusUnauthorized) } tryAuthenticate := func(username string, password string) *http.Response { authRequest := web.AuthenticationRequest{ Username: username, Password: password, } authRequestJSON, err := json.Marshal(authRequest) if err != nil { t.Fatalf("Error marshalling JSON: %s", err) } resp, err := http.Post(httpServer.URL+"/authenticate", "application/json", io.NopCloser(bytes.NewReader(authRequestJSON))) if err != nil { t.Fatalf("TestAuthentication error: %s", err) } return resp } // Send authentication request with bad credentials resp = tryAuthenticate("bad", "credentials") if resp.StatusCode == http.StatusOK { t.Fatalf("Unexpected status code: %d (expected %d)", resp.StatusCode, http.StatusUnauthorized) } // Now try good credentials resp = tryAuthenticate(server.AUTH_USERNAME, server.AUTH_PASSWORD) if resp.StatusCode != http.StatusOK { t.Fatalf("Unexpected status code: %d (expected %d)", resp.StatusCode, http.StatusOK) } // Decode the token from the body. body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Error decoding body: %s", body) } var authToken model.AuthToken err = json.Unmarshal(body, &authToken) if err != nil { t.Fatalf("Error unmarshalling JSON: %s, body: %s", err, body) } if authToken.SignedToken == "" { t.Fatalf("Unexpected empty signed token") } // Send a request with the signed token req, err := http.NewRequest(http.MethodGet, httpServer.URL+"/status", nil) if err != nil { t.Fatalf("Error creating request: %s", err) } req.Header.Set("Authorization", "Bearer "+authToken.SignedToken) resp, err = http.DefaultClient.Do(req) if err != nil { t.Fatalf("Error sending request: %s", err) } if resp.StatusCode != http.StatusOK { t.Fatalf("Unexpected status code: %d (expected %d)", resp.StatusCode, http.StatusUnauthorized) } body, err = io.ReadAll(resp.Body) if err != nil { t.Fatalf("Error decoding body: %s", body) } if string(body) != "OK" { t.Fatalf("Unexpected body: %s (expected %s)", body, "OK") } } func TestUpdates(t *testing.T) { s := web.NewMockHTTPServer(web.MockHTTPServerConfiguration{AuthEnabled: true}) httpServer := httptest.NewServer(s) messageSeq := 0 // Mock conversation guid := "1234567890" conversation := model.Conversation{ Date: time.Now(), Participants: []string{"Alice"}, UnreadCount: 0, Guid: guid, } s.Server.AddConversation(conversation) // Receive a message message := model.Message{ Text: "This is a test.", Sender: &conversation.Participants[0], Date: time.Now(), } // This should enqueue an update item s.Server.ReceiveMessage(&conversation, message) resp, err := http.Get(httpServer.URL + fmt.Sprintf("/pollUpdates?seq=%d", messageSeq)) if err != nil { t.Fatalf("TestUpdates error: %s", err) } if resp.StatusCode != http.StatusOK { t.Fatalf("Unexpected status code: %d (expected %d)", resp.StatusCode, http.StatusOK) } body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Error decoding body: %s", body) } var updates []model.UpdateItem err = json.Unmarshal(body, &updates) if err != nil { t.Fatalf("Error unmarshalling JSON: %s", err) } if len(updates) != 1 { t.Fatalf("Unexpected num updates: %d (expected %d)", len(updates), 1) } update := updates[0] // Message seq should be >= messageSeq messageSeq = update.MessageSequenceNumber if messageSeq != 1 { t.Fatalf("Unexpected message seq: %d (expected >= 0)", messageSeq) } if update.Conversation.Guid != conversation.Guid { t.Fatalf("Unexpected conversation guid: %s (expected %s)", update.Conversation.Guid, conversation.Guid) } if update.Message.Text != message.Text { t.Fatalf("Unexpected message text: %s (expected %s)", update.Message.Text, message.Text) } } func TestMarkConversation(t *testing.T) { s := web.NewMockHTTPServer(web.MockHTTPServerConfiguration{AuthEnabled: true}) httpServer := httptest.NewServer(s) // Mock conversation guid := "1234567890" conversation := model.Conversation{ Date: time.Now(), Participants: []string{"Alice"}, UnreadCount: 0, Guid: guid, } s.Server.AddConversation(conversation) // Receive message to mark as unread message := model.Message{ Text: "This is a test.", Sender: &conversation.Participants[0], Date: time.Now(), } s.Server.ReceiveMessage(&conversation, message) if convo, _ := s.Server.ConversationForGUID(guid); convo.UnreadCount != 1 { t.Fatalf("Unexpected unread count: %d (expected %d)", convo.UnreadCount, 1) } // Mark conversation as read resp, err := http.Post(httpServer.URL+"/markConversation?guid="+guid, "", nil) if err != nil { t.Fatalf("TestMarkConversation error: %s", err) } if resp.StatusCode != http.StatusOK { t.Fatalf("Unexpected status code: %d (expected %d)", resp.StatusCode, http.StatusOK) } if convo, _ := s.Server.ConversationForGUID(guid); convo.UnreadCount != 0 { t.Fatalf("Unexpected unread count: %d (expected %d)", convo.UnreadCount, 0) } }