wakatime_test.go (4255B)
1 package external 2 3 import ( 4 "net/http" 5 "net/http/httptest" 6 "sync/atomic" 7 "testing" 8 "time" 9 10 "github.com/patrickmn/go-cache" 11 ) 12 13 func newTestWakaTime(srv *httptest.Server, user string) *WakaTime { 14 w := &WakaTime{user: user, cache: cache.New(time.Minute, time.Minute)} 15 if srv != nil { 16 w.httpClient = srv.Client() 17 w.baseURL = srv.URL 18 } 19 return w 20 } 21 22 func TestFetchTotalParsesPublicStats(t *testing.T) { 23 var gotPath, gotAuth string 24 var hits atomic.Int32 25 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 26 hits.Add(1) 27 gotPath = r.URL.EscapedPath() 28 gotAuth = r.Header.Get("Authorization") 29 _, _ = w.Write([]byte(`{"data":{"status":"ok","total_seconds":6467373.8,"total_seconds_including_other_language":12283939.45}}`)) 30 })) 31 defer srv.Close() 32 33 w := newTestWakaTime(srv, "moncheeta") 34 stats, err := w.FetchTotal() 35 if err != nil { 36 t.Fatal(err) 37 } 38 if stats == nil || stats.AllTime.TotalText != "3.4k hrs logged" { 39 t.Fatalf("stats = %+v", stats) 40 } 41 if gotPath != "/users/@moncheeta/stats/all_time" { 42 t.Fatalf("path = %q", gotPath) 43 } 44 if gotAuth != "" { 45 t.Fatalf("unexpected Authorization header %q", gotAuth) 46 } 47 48 if _, err := w.FetchTotal(); err != nil { 49 t.Fatal(err) 50 } 51 if hits.Load() != 1 { 52 t.Fatalf("expected cached second call, got %d requests", hits.Load()) 53 } 54 } 55 56 func TestFetchTotalEscapesUser(t *testing.T) { 57 var gotPath string 58 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 59 gotPath = r.URL.EscapedPath() 60 _, _ = w.Write([]byte(`{"data":{"status":"ok","total_seconds_including_other_language":7200}}`)) 61 })) 62 defer srv.Close() 63 64 if _, err := newTestWakaTime(srv, "a b/c").FetchTotal(); err != nil { 65 t.Fatal(err) 66 } 67 if gotPath != "/users/@a%20b%2Fc/stats/all_time" { 68 t.Fatalf("path = %q", gotPath) 69 } 70 } 71 72 func TestFetchTotalPending(t *testing.T) { 73 cases := map[string]struct { 74 code int 75 body string 76 }{ 77 "202": {http.StatusAccepted, `{"data":{"status":"pending_update"}}`}, 78 "status": {http.StatusOK, `{"data":{"status":"pending_update","total_seconds_including_other_language":3600}}`}, 79 "null total": {http.StatusOK, `{"data":{"status":"ok","total_seconds_including_other_language":null}}`}, 80 "missing data": {http.StatusOK, `{}`}, 81 } 82 for name, tc := range cases { 83 t.Run(name, func(t *testing.T) { 84 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 85 w.WriteHeader(tc.code) 86 _, _ = w.Write([]byte(tc.body)) 87 })) 88 defer srv.Close() 89 90 stats, err := newTestWakaTime(srv, "moncheeta").FetchTotal() 91 if err != nil || stats != nil { 92 t.Fatalf("got %+v, %v; want nil, nil", stats, err) 93 } 94 }) 95 } 96 } 97 98 func TestFetchTotalServesStaleWhilePendingOrFailing(t *testing.T) { 99 var mode atomic.Value 100 mode.Store("ok") 101 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 102 switch mode.Load().(string) { 103 case "pending": 104 w.WriteHeader(http.StatusAccepted) 105 _, _ = w.Write([]byte(`{"data":{"status":"pending_update"}}`)) 106 case "error": 107 w.WriteHeader(http.StatusInternalServerError) 108 default: 109 _, _ = w.Write([]byte(`{"data":{"status":"ok","total_seconds_including_other_language":36000}}`)) 110 } 111 })) 112 defer srv.Close() 113 114 w := newTestWakaTime(srv, "moncheeta") 115 if _, err := w.FetchTotal(); err != nil { 116 t.Fatal(err) 117 } 118 119 for _, m := range []string{"pending", "error"} { 120 mode.Store(m) 121 w.cache.Delete(wakaTimeKey) 122 stats, err := w.FetchTotal() 123 if err != nil || stats == nil || stats.AllTime.TotalText != "10 hrs logged" { 124 t.Fatalf("%s: got %+v, %v; want stale value", m, stats, err) 125 } 126 } 127 } 128 129 func TestFetchTotalErrorWithoutCache(t *testing.T) { 130 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 131 w.WriteHeader(http.StatusNotFound) 132 })) 133 defer srv.Close() 134 135 stats, err := newTestWakaTime(srv, "moncheeta").FetchTotal() 136 if err == nil || stats != nil { 137 t.Fatalf("got %+v, %v; want error", stats, err) 138 } 139 } 140 141 func TestFetchTotalWithoutUser(t *testing.T) { 142 // No server: any network call would panic on the nil client. 143 w := newTestWakaTime(nil, "") 144 if stats, err := w.FetchTotal(); stats != nil || err != nil { 145 t.Fatalf("got %v, %v", stats, err) 146 } 147 }