Files
fn_registry/functions/infra/push_loki_stream_test.go
T
egutierrez 10bfb846a8 ahora si funciona
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 16:23:52 +02:00

140 lines
4.4 KiB
Go

package infra
import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// lokiPushBody refleja la estructura JSON que espera el push API de Loki.
type lokiPushBody struct {
Streams []struct {
Stream map[string]string `json:"stream"`
Values [][2]string `json:"values"`
} `json:"streams"`
}
func TestPushLokiStream(t *testing.T) {
t.Run("JSON enviado tiene estructura streams/stream/values correcta", func(t *testing.T) {
var captured lokiPushBody
var contentType string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
contentType = r.Header.Get("Content-Type")
raw, _ := io.ReadAll(r.Body)
if err := json.Unmarshal(raw, &captured); err != nil {
t.Errorf("body no es JSON valido: %v", err)
}
w.WriteHeader(http.StatusNoContent)
}))
defer srv.Close()
labels := map[string]string{"instance": "lucas", "job": "journald", "unit": "ssh.service"}
ts := []int64{1700000000000000001, 1700000000000000002}
lines := []string{"line one", "line two"}
err := PushLokiStream(srv.URL, "", "", labels, ts, lines)
if err != nil {
t.Fatalf("error inesperado: %v", err)
}
if contentType != "application/json" {
t.Errorf("Content-Type = %q, want application/json", contentType)
}
if len(captured.Streams) != 1 {
t.Fatalf("streams len = %d, want 1", len(captured.Streams))
}
s := captured.Streams[0]
if s.Stream["unit"] != "ssh.service" || s.Stream["job"] != "journald" || s.Stream["instance"] != "lucas" {
t.Errorf("stream labels = %v, want %v", s.Stream, labels)
}
if len(s.Values) != 2 {
t.Fatalf("values len = %d, want 2", len(s.Values))
}
if s.Values[0][0] != "1700000000000000001" || s.Values[0][1] != "line one" {
t.Errorf("values[0] = %v, want [1700000000000000001 line one]", s.Values[0])
}
if s.Values[1][0] != "1700000000000000002" || s.Values[1][1] != "line two" {
t.Errorf("values[1] = %v, want [1700000000000000002 line two]", s.Values[1])
}
})
t.Run("longitudes desiguales dan error antes del POST", func(t *testing.T) {
hit := false
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hit = true
w.WriteHeader(http.StatusNoContent)
}))
defer srv.Close()
ts := []int64{1, 2, 3}
lines := []string{"only one"}
err := PushLokiStream(srv.URL, "", "", map[string]string{"job": "x"}, ts, lines)
if err == nil {
t.Fatalf("se esperaba error por longitudes desiguales")
}
if hit {
t.Errorf("no debe haber peticion HTTP cuando las longitudes no coinciden")
}
})
t.Run("len lines cero es no-op sin peticion", func(t *testing.T) {
hit := false
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hit = true
w.WriteHeader(http.StatusNoContent)
}))
defer srv.Close()
err := PushLokiStream(srv.URL, "", "", map[string]string{"job": "x"}, []int64{}, []string{})
if err != nil {
t.Fatalf("no-op no debe retornar error: %v", err)
}
if hit {
t.Errorf("no-op no debe hacer ninguna peticion HTTP")
}
})
t.Run("Basic Auth presente cuando user no vacio", func(t *testing.T) {
var gotUser, gotPass string
var hadAuth bool
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotUser, gotPass, hadAuth = r.BasicAuth()
w.WriteHeader(http.StatusNoContent)
}))
defer srv.Close()
err := PushLokiStream(srv.URL, "tenant", "secret", map[string]string{"job": "x"}, []int64{1}, []string{"hi"})
if err != nil {
t.Fatalf("error inesperado: %v", err)
}
if !hadAuth {
t.Fatalf("se esperaba header Authorization Basic")
}
if gotUser != "tenant" || gotPass != "secret" {
t.Errorf("basic auth = %q/%q, want tenant/secret", gotUser, gotPass)
}
})
t.Run("status 500 produce error", func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("loki rejected the push"))
}))
defer srv.Close()
err := PushLokiStream(srv.URL, "", "", map[string]string{"job": "x"}, []int64{1}, []string{"hi"})
if err == nil {
t.Fatalf("se esperaba error con status 500")
}
if !strings.Contains(err.Error(), "500") {
t.Errorf("error no menciona el codigo 500: %v", err)
}
if !strings.Contains(err.Error(), "loki rejected the push") {
t.Errorf("error no incluye el cuerpo de respuesta: %v", err)
}
})
}