package telescope import ( "net/http" "net/http/httptest" "strings" "testing" ) func TestRingOrderAndOverflow(t *testing.T) { tel := New(3, nil, nil) for i := 0; i < 5; i++ { tel.add(Entry{Path: string(rune('a' + i))}) } got := tel.Entries() if len(got) != 3 { t.Fatalf("want 3 entries, got %d", len(got)) } // جدید-به-قدیم: e, d, c (a و b بازنویسی شده‌اند). want := []string{"e", "d", "c"} for i, w := range want { if got[i].Path != w { t.Errorf("entry %d: want %q got %q", i, w, got[i].Path) } } // شناسه‌ها باید افزایشی و یکتا باشند. if got[0].ID != 5 || got[2].ID != 3 { t.Errorf("ids wrong: %d..%d", got[2].ID, got[0].ID) } } func TestMiddlewareCapturesPayloadHeadersResponseMasked(t *testing.T) { tel := New(10, nil, nil) h := tel.Middleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusBadRequest) _, _ = w.Write([]byte(`{"token":"xyz","message":"bad"}`)) })) req := httptest.NewRequest(http.MethodPost, "/api/x", strings.NewReader(`{"password":"secret","kind":"coin"}`)) req.Header.Set("Authorization", "Bearer supersecret") h.ServeHTTP(httptest.NewRecorder(), req) e := tel.Entries() if len(e) != 1 || e[0].Status != 400 { t.Fatalf("expected one 400 entry, got %+v", e) } // payload برای هر درخواست ثبت می‌شود و مقادیرِ حساس ماسک می‌شوند. if !strings.Contains(e[0].ReqBody, `"password":"***"`) { t.Errorf("password not masked: %s", e[0].ReqBody) } if !strings.Contains(e[0].ReqBody, `"kind":"coin"`) { t.Errorf("non-secret field lost: %s", e[0].ReqBody) } // پاسخ ثبت و توکنِ آن ماسک می‌شود. if !strings.Contains(e[0].RespBody, `"token":"***"`) { t.Errorf("response token not masked: %s", e[0].RespBody) } // هدرِ Authorization در نمایش پنهان می‌شود. if strings.Contains(e[0].Headers, "supersecret") { t.Errorf("authorization header leaked: %s", e[0].Headers) } if !strings.Contains(e[0].Headers, "Authorization: ***") { t.Errorf("authorization header not masked: %s", e[0].Headers) } } func TestMobileFromLoginBody(t *testing.T) { tel := New(10, nil, nil) h := tel.Middleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })) req := httptest.NewRequest(http.MethodPost, "/api/auth/login-otp", strings.NewReader(`{"mobile":"09120000000"}`)) h.ServeHTTP(httptest.NewRecorder(), req) e := tel.Entries() if len(e) != 1 || e[0].Mobile != "09120000000" { t.Errorf("mobile not extracted from login body, got %+v", e) } } func TestResolveMobileByUserID(t *testing.T) { tel := New(10, func(*http.Request) int64 { return 42 }, func(id int64) string { if id == 42 { return "09121112233" } return "" }) h := tel.Middleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })) h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/api/me", nil)) e := tel.Entries() if len(e) != 1 || e[0].Mobile != "09121112233" || e[0].UserID != 42 { t.Errorf("mobile not resolved by user id, got %+v", e) } } func TestMiddlewareSkipsWS(t *testing.T) { tel := New(10, nil, nil) h := tel.Middleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })) h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/ws", nil)) if len(tel.Entries()) != 0 { t.Error("ws request should be skipped") } }