// Copyright (c) 2020 Proton Technologies AG // // This file is part of ProtonMail Bridge. // // ProtonMail Bridge is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ProtonMail Bridge is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with ProtonMail Bridge. If not, see . package pmapi import ( "fmt" "net/http" "net/mail" "regexp" "strconv" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestClient_GetEvent(t *testing.T) { s, c := newTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.NoError(t, checkMethodAndPath(r, "GET", "/events/latest")) fmt.Fprint(w, testEventBody) })) defer s.Close() event, err := c.GetEvent("") require.NoError(t, err) require.Equal(t, testEvent, event) } func TestClient_GetEvent_withID(t *testing.T) { s, c := newTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.NoError(t, checkMethodAndPath(r, "GET", "/events/"+testEvent.EventID)) fmt.Fprint(w, testEventBody) })) defer s.Close() event, err := c.GetEvent(testEvent.EventID) require.NoError(t, err) require.Equal(t, testEvent, event) } // We first call GetEvent with id of eventID1, which returns More=1 so we fetch with id eventID2. func TestClient_GetEvent_mergeEvents(t *testing.T) { s, c := newTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.RequestURI() { case "/events/eventID1": assert.NoError(t, checkMethodAndPath(r, "GET", "/events/eventID1")) fmt.Fprint(w, testEventBodyMore1) case "/events/eventID2": assert.NoError(t, checkMethodAndPath(r, "GET", "/events/eventID2")) fmt.Fprint(w, testEventBodyMore2) default: t.Fail() } })) defer s.Close() event, err := c.GetEvent("eventID1") require.NoError(t, err) require.Equal(t, testEventMerged, event) } func TestClient_GetEvent_mergeMaxNumberOfEvents(t *testing.T) { numberOfCalls := 0 s, c := newTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { numberOfCalls++ re := regexp.MustCompile(`/eventID([0-9]+)`) eventIDString := re.FindStringSubmatch(r.URL.RequestURI())[1] eventID, err := strconv.Atoi(eventIDString) require.NoError(t, err) if numberOfCalls > maxNumberOfMergedEvents*2 { require.Fail(t, "Too many calls!") } fmt.Println("") body := strings.ReplaceAll(testEventBodyMore1, "eventID2", "eventID"+strconv.Itoa(eventID+1)) fmt.Fprint(w, body) })) defer s.Close() event, err := c.GetEvent("eventID1") require.NoError(t, err) require.Equal(t, maxNumberOfMergedEvents, numberOfCalls) require.Equal(t, 1, event.More) } var ( testEventMessageUpdateUnread = 0 testEvent = &Event{ EventID: "eventID1", Refresh: 0, Messages: []*EventMessage{ { EventItem: EventItem{ID: "hdI7aIgUO1hFplCIcJHB0jShRVsAzS0AB75wGCaiNVeIHXLmaUnt4eJ8l7c7L6uk4g0ZdXhGWG5gfh6HHgAZnw==", Action: EventCreate}, Created: &Message{ ID: "hdI7aIgUO1hFplCIcJHB0jShRVsAzS0AB75wGCaiNVeIHXLmaUnt4eJ8l7c7L6uk4g0ZdXhGWG5gfh6HHgAZnw==", Header: make(mail.Header), Subject: "Hey there", }, }, { EventItem: EventItem{ID: "bSFLAimPSfGz2Kj0aV3l3AyXsof_Vf7sfrrMJ8ifgGJe-f2NG2eLaEGXLytjMhq9wnLMtkoZpO2uBXM4nOVa5g==", Action: EventUpdateFlags}, Updated: &EventMessageUpdated{ ID: "bSFLAimPSfGz2Kj0aV3l3AyXsof_Vf7sfrrMJ8ifgGJe-f2NG2eLaEGXLytjMhq9wnLMtkoZpO2uBXM4nOVa5g==", Unread: &testEventMessageUpdateUnread, Time: 1472391377, LabelIDsAdded: []string{ArchiveLabel}, LabelIDsRemoved: []string{InboxLabel}, }, }, { EventItem: EventItem{ID: "XRBMBYnSkaEJWtqFACp2kjlNc-7GjzX3SnPcOtWK4PyLG11Nhsg0uxPYjTXoClQfB-EHVDl9gE3w2PVuj93jBg==", Action: EventDelete}, }, }, MessageCounts: []*MessagesCount{ { LabelID: "0", Total: 19, Unread: 2, }, { LabelID: "6", Total: 1, Unread: 0, }, }, Notices: []string{"Server will be down in 2min because of a NSA attack"}, } testEventMerged = &Event{ EventID: "eventID3", Refresh: 1, Messages: []*EventMessage{ { EventItem: EventItem{ID: "msgID1", Action: EventCreate}, Created: &Message{ ID: "id", Header: make(mail.Header), Subject: "Hey there", }, }, { EventItem: EventItem{ID: "msgID2", Action: EventCreate}, Created: &Message{ ID: "id", Header: make(mail.Header), Subject: "Hey there again", }, }, }, MessageCounts: []*MessagesCount{ { LabelID: "label1", Total: 19, Unread: 2, }, { LabelID: "label2", Total: 1, Unread: 0, }, { LabelID: "label2", Total: 2, Unread: 1, }, { LabelID: "label3", Total: 1, Unread: 0, }, }, Notices: []string{"Server will be down in 2min because of a NSA attack", "Just kidding lol"}, Labels: []*EventLabel{ { EventItem: EventItem{ ID: "labelID1", Action: 1, }, Label: &Label{ ID: "id", Name: "Event Label 1", }, }, { EventItem: EventItem{ ID: "labelID2", Action: 1, }, Label: &Label{ ID: "id", Name: "Event Label 2", }, }, }, User: User{ ID: "userID1", Name: "user", UsedSpace: 23456, }, Addresses: []*EventAddress{ { EventItem: EventItem{ ID: "addressID1", Action: 2, }, Address: &Address{ ID: "id", DisplayName: "address 1", }, }, { EventItem: EventItem{ ID: "addressID2", Action: 2, }, Address: &Address{ ID: "id", DisplayName: "address 2", }, }, }, } ) const ( testEventBody = `{ "EventID": "eventID1", "Refresh": 0, "Messages": [ { "ID": "hdI7aIgUO1hFplCIcJHB0jShRVsAzS0AB75wGCaiNVeIHXLmaUnt4eJ8l7c7L6uk4g0ZdXhGWG5gfh6HHgAZnw==", "Action": 1, "Message": { "ID": "hdI7aIgUO1hFplCIcJHB0jShRVsAzS0AB75wGCaiNVeIHXLmaUnt4eJ8l7c7L6uk4g0ZdXhGWG5gfh6HHgAZnw==", "Subject": "Hey there" } }, { "ID": "bSFLAimPSfGz2Kj0aV3l3AyXsof_Vf7sfrrMJ8ifgGJe-f2NG2eLaEGXLytjMhq9wnLMtkoZpO2uBXM4nOVa5g==", "Action": 3, "Message": { "ConversationID": "2oX3EILYRuZ0IRBVlzMg1oV5eazQL67sFIHlcR8bjickPn7K4id4sJZuAB6n0pdtI3hRIVsjCpgWfRm8c_x3IQ==", "Unread": 0, "Time": 1472391377, "Location": 6, "LabelIDsAdded": [ "6" ], "LabelIDsRemoved": [ "0" ] } }, { "ID": "XRBMBYnSkaEJWtqFACp2kjlNc-7GjzX3SnPcOtWK4PyLG11Nhsg0uxPYjTXoClQfB-EHVDl9gE3w2PVuj93jBg==", "Action": 0 } ], "Conversations": [ { "ID": "2oX3EILYRuZ0IRBVlzMg1oV5eazQL67sFIHlcR8bjickPn7K4id4sJZuAB6n0pdtI3hRIVsjCpgWfRm8c_x3IQ==", "Action": 1, "Conversation": { "ID": "2oX3EILYRuZ0IRBVlzMg1oV5eazQL67sFIHlcR8bjickPn7K4id4sJZuAB6n0pdtI3hRIVsjCpgWfRm8c_x3IQ==", "Order": 1616, "Subject": "Hey there", "Senders": [ { "Address": "apple@protonmail.com", "Name": "apple@protonmail.com" } ], "Recipients": [ { "Address": "apple@protonmail.com", "Name": "apple@protonmail.com" } ], "NumMessages": 1, "NumUnread": 1, "NumAttachments": 0, "ExpirationTime": 0, "TotalSize": 636, "AddressID": "QMJs2dzTx7uqpH5PNgIzjULywU4gO9uMBhEMVFOAVJOoUml54gC0CCHtW9qYwzH-zYbZwMv3MFYncPjW1Usq7Q==", "LabelIDs": [ "0" ], "Labels": [ { "Count": 1, "NumMessages": 1, "NumUnread": 1, "ID": "0" } ] } } ], "Total": { "Locations": [ { "Location": 0, "Count": 19 }, { "Location": 1, "Count": 16 }, { "Location": 2, "Count": 16 }, { "Location": 3, "Count": 17 }, { "Location": 6, "Count": 1 } ], "Labels": [ { "LabelID": "LLz8ysmVxwr4dF6mWpClePT0SpSWOEvzTdq17RydSl4ndMckvY1K63HeXDzn03BJQwKYvgf-eWT8Qfd9WVuIEQ==", "Count": 2 }, { "LabelID": "BvbqbySUPo9uWW_eR8tLA13NUsQMz3P4Zhw4UnpvrKqURnrHlE6L2Au0nplHfHlVXFgGz4L4hJ9-BYllOL-L5g==", "Count": 2 } ], "Starred": 3 }, "Unread": { "Locations": [ { "Location": 0, "Count": 2 }, { "Location": 1, "Count": 0 }, { "Location": 2, "Count": 0 }, { "Location": 3, "Count": 0 }, { "Location": 6, "Count": 0 } ], "Labels": [ { "LabelID": "LLz8ysmVxwr4dF6mWpClePT0SpSWOEvzTdq17RydSl4ndMckvY1K63HeXDzn03BJQwKYvgf-eWT8Qfd9WVuIEQ==", "Count": 0 }, { "LabelID": "BvbqbySUPo9uWW_eR8tLA13NUsQMz3P4Zhw4UnpvrKqURnrHlE6L2Au0nplHfHlVXFgGz4L4hJ9-BYllOL-L5g==", "Count": 0 } ], "Starred": 0 }, "MessageCounts": [ { "LabelID": "0", "Total": 19, "Unread": 2 }, { "LabelID": "6", "Total": 1, "Unread": 0 } ], "ConversationCounts": [ { "LabelID": "0", "Total": 19, "Unread": 2 }, { "LabelID": "6", "Total": 1, "Unread": 0 } ], "UsedSpace": 7552905, "Notices": ["Server will be down in 2min because of a NSA attack"], "Code": 1000 } ` testEventBodyMore1 = `{ "EventID": "eventID2", "More": 1, "Refresh": 1, "Messages": [ { "ID": "msgID1", "Action": 1, "Message": { "ID": "id", "Subject": "Hey there" } } ], "MessageCounts": [ { "LabelID": "label1", "Total": 19, "Unread": 2 }, { "LabelID": "label2", "Total": 1, "Unread": 0 } ], "Labels": [ { "ID":"labelID1", "Action":1, "Label":{ "ID":"id", "Name":"Event Label 1" } } ], "User": { "ID": "userID1", "Name": "user", "UsedSpace": 12345 }, "Addresses": [ { "ID": "addressID1", "Action": 2, "Address": { "ID": "id", "DisplayName": "address 1" } } ], "Notices": ["Server will be down in 2min because of a NSA attack"] } ` testEventBodyMore2 = `{ "EventID": "eventID3", "Refresh": 0, "Messages": [ { "ID": "msgID2", "Action": 1, "Message": { "ID": "id", "Subject": "Hey there again" } } ], "MessageCounts": [ { "LabelID": "label2", "Total": 2, "Unread": 1 }, { "LabelID": "label3", "Total": 1, "Unread": 0 } ], "Labels": [ { "ID":"labelID2", "Action":1, "Label":{ "ID":"id", "Name":"Event Label 2" } } ], "User": { "ID": "userID1", "Name": "user", "UsedSpace": 23456 }, "Addresses": [ { "ID": "addressID2", "Action": 2, "Address": { "ID": "id", "DisplayName": "address 2" } } ], "Notices": ["Just kidding lol"] } ` )