package monitoring import ( "context" "net/http" "net/http/httptest" "testing" "time" ) func TestLokiClient_QueryRange(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/loki/api/v1/query_range" { t.Errorf("unexpected path: %s", r.URL.Path) } if r.URL.Query().Get("query") != `{job="varlogs"}` { t.Errorf("unexpected query param: %s", r.URL.Query().Get("query")) } if r.URL.Query().Get("direction") != "backward" { t.Errorf("unexpected direction: %s", r.URL.Query().Get("direction")) } if r.URL.Query().Get("limit") != "10" { t.Errorf("unexpected limit: %s", r.URL.Query().Get("limit")) } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{ "status": "success", "data": { "resultType": "streams", "result": [ { "stream": {"job": "varlogs", "filename": "/var/log/syslog"}, "values": [ ["1234567890000000000", "line 1"], ["1234567891000000000", "line 2"] ] } ] } }`)) })) defer srv.Close() client := NewLokiClient(srv.URL) start := time.Unix(0, 1234567890000000000) end := time.Unix(0, 1234567899000000000) data, err := client.QueryRange(context.Background(), `{job="varlogs"}`, start, end, 10, "backward") if err != nil { t.Fatalf("unexpected error: %v", err) } if data.ResultType != "streams" { t.Errorf("expected resultType=streams, got %s", data.ResultType) } if len(data.Result) != 1 { t.Fatalf("expected 1 stream, got %d", len(data.Result)) } if data.Result[0].Stream["job"] != "varlogs" { t.Errorf("expected job=varlogs, got %s", data.Result[0].Stream["job"]) } if len(data.Result[0].Values) != 2 { t.Fatalf("expected 2 entries, got %d", len(data.Result[0].Values)) } if data.Result[0].Values[0][1] != "line 1" { t.Errorf("expected first line='line 1', got %s", data.Result[0].Values[0][1]) } } func TestLokiClient_QueryRangeError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{ "status": "error", "errorType": "bad_data", "error": "invalid LogQL query" }`)) })) defer srv.Close() client := NewLokiClient(srv.URL) _, err := client.QueryRange(context.Background(), "invalid{", time.Now().Add(-time.Hour), time.Now(), 100, "backward") if err == nil { t.Fatal("expected error, got nil") } if !contains(err.Error(), "invalid LogQL query") { t.Errorf("expected error to contain 'invalid LogQL query', got: %s", err.Error()) } } func TestLokiClient_Labels(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/loki/api/v1/labels" { t.Errorf("unexpected path: %s", r.URL.Path) } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{ "status": "success", "data": ["job", "instance", "filename"] }`)) })) defer srv.Close() client := NewLokiClient(srv.URL) labels, err := client.Labels(context.Background()) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(labels) != 3 { t.Fatalf("expected 3 labels, got %d", len(labels)) } if labels[0] != "job" { t.Errorf("expected first label=job, got %s", labels[0]) } } func TestLokiClient_LabelValues(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/loki/api/v1/label/job/values" { t.Errorf("unexpected path: %s, expected /loki/api/v1/label/job/values", r.URL.Path) } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{ "status": "success", "data": ["varlogs", "nginx", "systemd"] }`)) })) defer srv.Close() client := NewLokiClient(srv.URL) values, err := client.LabelValues(context.Background(), "job") if err != nil { t.Fatalf("unexpected error: %v", err) } if len(values) != 3 { t.Fatalf("expected 3 values, got %d", len(values)) } if values[0] != "varlogs" { t.Errorf("expected first value=varlogs, got %s", values[0]) } } func TestLokiClient_HTTPError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) _, _ = w.Write([]byte("internal error")) })) defer srv.Close() client := NewLokiClient(srv.URL) _, err := client.QueryRange(context.Background(), `{job="test"}`, time.Now().Add(-time.Hour), time.Now(), 100, "backward") if err == nil { t.Fatal("expected error, got nil") } if !contains(err.Error(), "500") { t.Errorf("expected error to contain status code, got: %s", err.Error()) } }