package uptimerobot import ( "encoding/json" "fmt" "io" "io/ioutil" "net/http" "net/http/httptest" "os" "testing" "github.com/google/go-cmp/cmp" ) func TestMarshalMonitor(t *testing.T) { t.Parallel() m := Monitor{ ID: 777749809, FriendlyName: "Google", URL: "http://www.google.com", Type: TypeHTTP, Port: 80, AlertContacts: []string{"3", "5", "7"}, } got, err := json.MarshalIndent(m, "", " ") if err != nil { t.Error(err) } want, err := ioutil.ReadFile("testdata/marshal.json") if err != nil { t.Fatal(err) } // Convert the actual data and the expected data to maps, for ease of // comparison wantMap := map[string]interface{}{} err = json.Unmarshal(want, &wantMap) if err != nil { t.Fatal(err) } gotMap := map[string]interface{}{} err = json.Unmarshal(got, &gotMap) if err != nil { t.Fatal(err) } if !cmp.Equal(wantMap, gotMap) { t.Error(cmp.Diff(wantMap, gotMap)) } } func TestUnmarshalMonitor(t *testing.T) { t.Parallel() want := Monitor{ ID: 777749809, FriendlyName: "Google", URL: "http://www.google.com", Type: TypeHTTP, Port: 80, Status: StatusUnknown, } data, err := ioutil.ReadFile("testdata/unmarshal.json") if err != nil { t.Fatal(err) } got := Monitor{} err = got.UnmarshalJSON(data) if err != nil { t.Error(err) } if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestCreate(t *testing.T) { t.Parallel() client := New("dummy") // force test coverage of the client's dump functionality client.Debug = ioutil.Discard ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { t.Errorf("want POST request, got %q", r.Method) } wantURL := "/v2/newMonitor" if r.URL.EscapedPath() != wantURL { t.Errorf("want %q, got %q", wantURL, r.URL.EscapedPath()) } body, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatal(err) } r.Body.Close() want, err := ioutil.ReadFile("testdata/requestNewMonitor.json") if err != nil { t.Fatal(err) } // Convert the received body and the expected body to maps, for // ease of comparison wantMap := map[string]interface{}{} err = json.Unmarshal(want, &wantMap) if err != nil { t.Fatal(err) } bodyMap := map[string]interface{}{} err = json.Unmarshal(body, &bodyMap) if err != nil { t.Fatal(err) } if !cmp.Equal(wantMap, bodyMap) { t.Error(cmp.Diff(wantMap, bodyMap)) } w.WriteHeader(http.StatusOK) data, err := os.Open("testdata/newMonitor.json") if err != nil { t.Fatal(err) } defer data.Close() io.Copy(w, data) })) defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL create := Monitor{ FriendlyName: "My test monitor", URL: "http://example.com", Type: TypeHTTP, Port: 80, AlertContacts: []string{"3", "5", "7"}, } got, err := client.CreateMonitor(create) if err != nil { t.Error(err) } var want int64 = 777810874 if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestGetAccountDetails(t *testing.T) { t.Parallel() client := New("dummy") ts := cannedResponseServer(t, "testdata/getAccountDetails.json") defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL got, err := client.GetAccountDetails() if err != nil { t.Error(err) } want := Account{ Email: "test@domain.com", MonitorLimit: 50, MonitorInterval: 1, UpMonitors: 1, DownMonitors: 0, PausedMonitors: 2, } if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestAllAlertContacts(t *testing.T) { t.Parallel() client := New("dummy") ts := cannedResponseServer(t, "testdata/getAlertContacts.json") defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL want := []AlertContact{ { ID: "0993765", FriendlyName: "John Doe", Type: 2, Status: 1, Value: "johndoe@gmail.com", }, { ID: "2403924", FriendlyName: "My Twitter", Type: 3, Status: 0, Value: "sampleTwitterAccount", }, } got, err := client.AllAlertContacts() if err != nil { t.Error(err) } if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestGetMonitorByID(t *testing.T) { t.Parallel() client := New("dummy") ts := cannedResponseServer(t, "testdata/getMonitorByID.json") defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL want := Monitor{ ID: 777749809, FriendlyName: "Google", URL: "http://www.google.com", Type: TypeHTTP, Port: 80, Status: StatusUnknown, } got, err := client.GetMonitor(want.ID) if err != nil { t.Error(err) } if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestGetMonitorsPages(t *testing.T) { t.Parallel() client := New("dummy") ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { bodyMap := map[string]interface{}{} if err := json.NewDecoder(r.Body).Decode(&bodyMap); err != nil { t.Fatal(err) } var datafile string switch bodyMap["offset"] { case "0": datafile = "testdata/getMonitorsPage1.json" case "50": datafile = "testdata/getMonitorsPage2.json" default: t.Fatalf("unexpected offset %s", bodyMap["offset"]) } data, err := os.Open(datafile) if err != nil { t.Fatal(err) } w.WriteHeader(http.StatusOK) defer data.Close() io.Copy(w, data) })) defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL monitors, err := client.AllMonitors() if err != nil { t.Error(err) } if len(monitors) != 100 { t.Fatalf("Wanted 100 monitors, but got %d", len(monitors)) } for i, m := range monitors { want := fmt.Sprintf("monitor-%d", i+1) got := m.FriendlyName if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } } func TestGetMonitors(t *testing.T) { t.Parallel() client := New("dummy") ts := cannedResponseServer(t, "testdata/getMonitors.json") defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL want := []Monitor{ { ID: 777749809, FriendlyName: "Google", URL: "http://www.google.com", Type: TypeHTTP, Port: 80, Status: StatusUnknown, }, { ID: 777712827, FriendlyName: "My Web Page", URL: "http://mywebpage.com/", Type: TypeHTTP, Status: StatusUp, }, { ID: 777559666, FriendlyName: "My FTP Server", URL: "ftp.mywebpage.com", Type: TypePort, SubType: SubTypeFTP, Port: 21, Status: StatusUp, }, { ID: 781397847, FriendlyName: "PortTest", URL: "mywebpage.com", Type: TypePort, SubType: SubTypeCustomPort, Port: 8000, Status: StatusUnknown, }, } got, err := client.AllMonitors() if err != nil { t.Error(err) } if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestGetMonitorsBySearch(t *testing.T) { t.Parallel() client := New("dummy") ts := cannedResponseServer(t, "testdata/getMonitorsBySearch.json") defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL want := []Monitor{ { ID: 777712827, FriendlyName: "My Web Page", URL: "http://mywebpage.com/", Type: TypeHTTP, Status: StatusUp, }, } got, err := client.SearchMonitors("My Web Page") if err != nil { t.Error(err) } if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestPauseMonitor(t *testing.T) { t.Parallel() client := New("dummy") ts := cannedResponseServer(t, "testdata/pauseMonitor.json") defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL want := Monitor{ ID: 677810870, } got, err := client.PauseMonitor(want) if err != nil { t.Error(err) } if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestStartMonitor(t *testing.T) { t.Parallel() client := New("dummy") ts := cannedResponseServer(t, "testdata/startMonitor.json") defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL want := Monitor{ ID: 677810870, } got, err := client.StartMonitor(want) if err != nil { t.Error(err) } if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestEnsure(t *testing.T) { t.Parallel() client := New("dummy") ts := cannedResponseServer(t, "testdata/ensure.json") defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL mon := Monitor{ ID: 777712827, FriendlyName: "My Web Page", URL: "http://mywebpage.com/", Type: TypeHTTP, } // The client will do a SearchMonitors and get a canned response from // the test server containing no matches. It will now try to create the // monitor, and the test server will just respond with an empty body and // OK. The resulting monitor will have an ID of 0. want := int64(0) got, err := client.EnsureMonitor(mon) if err != nil { t.Error(err) } if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestDeleteMonitor(t *testing.T) { t.Parallel() client := New("dummy") ts := cannedResponseServer(t, "testdata/deleteMonitor.json") defer ts.Close() client.HTTPClient = ts.Client() client.URL = ts.URL var want int64 = 777810874 if err := client.DeleteMonitor(want); err != nil { t.Error(err) } } func TestRenderMonitor(t *testing.T) { t.Parallel() tcs := []struct { name string input Monitor wantFile string }{ { name: "Simple HTTP", input: Monitor{ ID: 777749809, FriendlyName: "Google", URL: "http://www.google.com", Type: TypeHTTP, Port: 0, AlertContacts: []string{"3", "5", "7"}, Status: StatusUp, }, wantFile: "testdata/monitor_http.txt", }, { name: "Keyword exists", input: Monitor{ ID: 777749810, FriendlyName: "Google", URL: "http://www.google.com", Type: TypeKeyword, KeywordType: KeywordExists, KeywordValue: "bogus", Port: 80, Status: StatusMaybeDown, }, wantFile: "testdata/monitor_keyword.txt", }, { name: "Keyword not exists", input: Monitor{ ID: 777749811, FriendlyName: "Google", URL: "http://www.google.com", Type: TypeKeyword, KeywordType: KeywordNotExists, KeywordValue: "bogus", Port: 80, Status: StatusUnknown, }, wantFile: "testdata/monitor_keyword_notexists.txt", }, { name: "Subtype", input: Monitor{ ID: 777749812, FriendlyName: "Google", URL: "http://www.google.com", Type: TypePort, SubType: SubTypeFTP, Port: 80, Status: StatusPaused, }, wantFile: "testdata/monitor_subtype.txt", }, } for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { t.Parallel() wantBytes, err := ioutil.ReadFile(tc.wantFile) if err != nil { t.Fatal(err) } want := string(wantBytes) got := render(monitorTemplate, tc.input) if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } }) } } func TestRenderAccount(t *testing.T) { t.Parallel() input := Account{ Email: "j.random@example.com", MonitorLimit: 300, MonitorInterval: 1, UpMonitors: 208, DownMonitors: 2, PausedMonitors: 0, } wantBytes, err := ioutil.ReadFile("testdata/account_template.txt") if err != nil { t.Fatal(err) } want := string(wantBytes) got := render(accountTemplate, input) if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestFriendlyType(t *testing.T) { t.Parallel() m := Monitor{ Type: TypeHTTP, } want := "HTTP" got := m.FriendlyType() if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } func TestFriendlySubType(t *testing.T) { t.Parallel() tcs := []struct { name string mon Monitor want string }{ { name: "HTTPS", mon: Monitor{ Type: TypePort, SubType: SubTypeHTTPS, }, want: "HTTPS (443)", }, { name: "Custom port", mon: Monitor{ Type: TypePort, SubType: SubTypeCustomPort, Port: 8080, }, want: "Custom port (8080)", }, } for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { t.Parallel() got := tc.mon.FriendlySubType() if !cmp.Equal(tc.want, got) { t.Error(cmp.Diff(tc.want, got)) } }) } } func TestFriendlyKeywordType(t *testing.T) { t.Parallel() m := Monitor{ Type: TypeKeyword, KeywordType: KeywordExists, } want := "Exists" got := m.FriendlyKeywordType() if !cmp.Equal(want, got) { t.Error(cmp.Diff(want, got)) } } // cannedResponseServer returns a test TLS server which responds to any request // with a specified file of canned JSON data. func cannedResponseServer(t *testing.T, path string) *httptest.Server { return httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) data, err := os.Open(path) if err != nil { t.Fatal(err) } defer data.Close() io.Copy(w, data) })) }