package main import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "sync" "testing" ) func TestRequestWeighting(t *testing.T) { var mu sync.Mutex counts := make(map[string]int) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mu.Lock() counts[r.Method+" "+r.URL.RequestURI()]++ mu.Unlock() w.WriteHeader(http.StatusOK) })) defer server.Close() g := &generator{client: server.Client(), target: server.URL, buoys: []string{"buoy-1"}, counts: make(map[string]int)} for roll := range 100 { g.sendRoll(context.Background(), roll) } want := map[string]int{ "GET /api/v1/readings": 60, "POST /api/v1/readings": 25, "GET /api/v1/readings/aggregate?window=1h": 15, } if fmt.Sprint(counts) != fmt.Sprint(want) { t.Fatalf("request weighting = %v, want %v", counts, want) } } func TestSeedEmptyAPI(t *testing.T) { var mu sync.Mutex created := 0 var posted []buoy server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { case r.Method == http.MethodGet && r.URL.Path == "/api/v1/buoys": _ = json.NewEncoder(w).Encode([]buoy{}) case r.Method == http.MethodPost && r.URL.Path == "/api/v1/buoys": var input buoy if err := json.NewDecoder(r.Body).Decode(&input); err != nil { http.Error(w, "invalid buoy", http.StatusBadRequest) return } mu.Lock() created++ posted = append(posted, input) id := fmt.Sprintf("buoy-%d", created) mu.Unlock() _ = json.NewEncoder(w).Encode(buoy{ID: id}) default: http.NotFound(w, r) } })) defer server.Close() g := &generator{client: server.Client(), target: server.URL, counts: make(map[string]int)} if err := g.seed(context.Background()); err != nil { t.Fatalf("seed: %v", err) } if created != 42 || len(g.buoys) != 42 { t.Fatalf("created %d buoys and retained %d IDs, want 42 and 42", created, len(g.buoys)) } want := [][2]float64{{44.5, -9}, {44.2, -8}, {44.3, -7}, {44.4, -6}, {44.5, -5}, {44.6, -4}, {44.7, -3}, {44.5, -2}, {43.8, -2}, {43.5, -9.8}, {42.5, -10.2}, {41.5, -10.5}, {40.5, -10.8}, {39.5, -11}, {38.5, -11}, {37.5, -10.8}, {36.5, -10}, {35.9, -9}, {35.8, -8}, {35.7, -7}, {35.2, -7.5}, {35, -8.5}, {35.8, -4.5}, {36, -3.5}, {36.2, -2.5}, {36.5, -1.5}, {36.8, -.5}, {37.2, .5}, {37.7, 1.2}, {38.1, 2}, {38, 3}, {38.5, 4}, {39.5, 1}, {40, 1.5}, {40.5, 2}, {41, 2.7}, {41.5, 3.3}, {42, 3.8}, {42.5, 4.5}, {43, -9.8}, {41, -10.5}, {39, -10.8}} for i, position := range want { if posted[i].Latitude != position[0] || posted[i].Longitude != position[1] { t.Errorf("buoy %d position = (%v, %v), want (%v, %v)", i, posted[i].Latitude, posted[i].Longitude, position[0], position[1]) } } } func TestTrafficContinuesThroughHTTP500(t *testing.T) { const requests = 30 var mu sync.Mutex received := 0 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { mu.Lock() received++ mu.Unlock() http.Error(w, "unavailable", http.StatusInternalServerError) })) defer server.Close() g := &generator{client: server.Client(), target: server.URL, buoys: []string{"buoy-1"}, counts: make(map[string]int)} for roll := range requests { g.sendRoll(context.Background(), roll) } mu.Lock() got := received mu.Unlock() if got != requests { t.Fatalf("received %d requests, want %d", got, requests) } if g.counts["500"] != requests { t.Fatalf("recorded %d HTTP 500 responses, want %d", g.counts["500"], requests) } }