package app import ( "context" "testing" "google.golang.org/grpc/metadata" ) func TestClientInfoFromContext(t *testing.T) { tests := []struct { name string md metadata.MD wantIP string wantUserAgent string }{ { name: "нет метаданных", wantIP: "", wantUserAgent: "", }, { name: "x-forwarded-for через gateway: берём последний элемент", md: metadata.MD{ "x-forwarded-for": []string{"203.0.113.1, 198.51.100.7"}, "grpcgateway-user-agent": []string{"Mozilla/5.0 (test)"}, }, wantIP: "198.51.100.7", wantUserAgent: "Mozilla/5.0 (test)", }, { name: "x-real-ip от доверенного прокси приоритетнее x-forwarded-for", md: metadata.MD{ "x-real-ip": []string{"192.0.2.10"}, "x-forwarded-for": []string{"203.0.113.1, 10.0.0.1"}, "grpcgateway-user-agent": []string{"curl/8.0"}, }, wantIP: "192.0.2.10", wantUserAgent: "curl/8.0", }, { name: "x-real-ip пустой — фолбэк на x-forwarded-for", md: metadata.MD{ "x-real-ip": []string{" "}, "x-forwarded-for": []string{"203.0.113.5"}, }, wantIP: "203.0.113.5", }, { name: "user-agent для прямого gRPC-клиента", md: metadata.MD{ "user-agent": []string{"grpc-go/1.60"}, }, wantUserAgent: "grpc-go/1.60", }, { name: "x-forwarded-for с пробелами после запятых", md: metadata.MD{ "x-forwarded-for": []string{"203.0.113.1 , 198.51.100.7 "}, }, wantIP: "198.51.100.7", }, { name: "невалидный x-real-ip — фолбэк на валидный x-forwarded-for", md: metadata.MD{ "x-real-ip": []string{"not-an-ip; drop me"}, "x-forwarded-for": []string{"203.0.113.1, 198.51.100.7"}, }, wantIP: "198.51.100.7", }, { name: "все источники невалидны — IP пустой (спуфинг)", md: metadata.MD{ "x-real-ip": []string{"not-an-ip; drop me"}, "x-forwarded-for": []string{"256.256.256.256"}, }, wantIP: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var ctx context.Context = context.Background() if tt.md != nil { ctx = metadata.NewIncomingContext(ctx, tt.md) } ip, userAgent := clientInfoFromContext(ctx) if ip != tt.wantIP { t.Errorf("ip = %q, want %q", ip, tt.wantIP) } if userAgent != tt.wantUserAgent { t.Errorf("userAgent = %q, want %q", userAgent, tt.wantUserAgent) } }) } }