package tide import ( "context" "net/http" "net/http/httptest" "strings" "testing" ) func TestHeadersAllowListIgnoresDate(t *testing.T) { want := Response{ Status: 401, Headers: map[string]string{ "Content-Type": "application/json", "WWW-Authenticate": `Bearer error="invalid_token"`, "Cache-Control": "no-store", "Date": "Wed, 01 Jan 2020 00:00:00 GMT", }, Body: Body(`{"ok":false}`), } got := Response{ Status: 401, Headers: map[string]string{ "Content-Type": "application/json", "WWW-Authenticate": `Bearer error="invalid_token"`, "Cache-Control": "no-store", "Date": "Thu, 02 Jan 2020 00:00:00 GMT", "Server": "php", }, Body: Body(`{"ok":false}`), } diffs := compareStep(Step{ID: "h", Response: want, Headers: map[string]string{"WWW-Authenticate": "", "Cache-Control": ""}}, got) if len(diffs) != 0 { t.Fatalf("Date/Server must be ignored: %+v", diffs) } got.Headers["WWW-Authenticate"] = `Bearer error="other"` diffs = compareStep(Step{ID: "h", Response: want}, got) if len(diffs) == 0 { t.Fatal("WWW-Authenticate mismatch must fail") } if !strings.Contains(strings.ToLower(diffs[0].Path), "www-authenticate") { t.Fatalf("path %s", diffs[0].Path) } } func TestHeadersCompareLocation(t *testing.T) { want := Response{ Status: 302, Headers: map[string]string{"Location": "http://127.0.0.1:8424/oauth/callback?code={{oauth:code}}"}, } got := Response{ Status: 302, Headers: map[string]string{"Location": "http://127.0.0.1:8424/oauth/callback?code={{oauth:code}}"}, } if diffs := compareHeaders(want.Headers, got.Headers, nil); len(diffs) != 0 { t.Fatalf("identical Location must pass: %+v", diffs) } got.Headers["Location"] = "http://evil.example/oauth/callback?code={{oauth:code}}" diffs := compareHeaders(want.Headers, got.Headers, nil) if len(diffs) == 0 { t.Fatal("Location host mismatch must fail") } if !strings.Contains(strings.ToLower(diffs[0].Path), "location") { t.Fatalf("path %s", diffs[0].Path) } } func TestHeadersReplayAgainstServer(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-store") w.Header().Set("Pragma", "no-cache") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"ok":true}`)) })) t.Cleanup(srv.Close) flow := Flow{ Version: 1, Name: "headers", Steps: []Step{{ ID: "h", Request: Request{Method: http.MethodGet, Path: "/"}, Response: Response{ Status: 200, Headers: map[string]string{ "Content-Type": "application/json", "Cache-Control": "no-store", "Pragma": "no-cache", }, Body: Body(`{"ok":true}`), }, }}, } if _, err := ReplayFlow(context.Background(), flow, ReplayConfig{Target: srv.URL}); err != nil { t.Fatal(err) } }