prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

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 }