package evidence_test import ( "bytes" "context" "io" "net/http" "net/http/httptest" "os" "testing" "github.com/digitorus/timestamp" "github.com/netcell-it/deklarix/internal/evidence" ) func TestTimestampRejectsWrongHashSize(t *testing.T) { called := false server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called = true })) defer server.Close() ts := evidence.NewHTTPTimestamper(server.URL) _, err := ts.Timestamp(context.Background(), []byte("zu kurz")) if err == nil { t.Fatal("expected error for a non-SHA-256-sized hash, got nil") } if called { t.Fatal("expected no HTTP call for an invalid hash size") } } // TestTimestampSendsValidRequest prüft serverseitig, dass Timestamp() // einen tatsächlich gültigen, RFC-3161-konformen Request schickt (parsbar, // korrekter Hash, korrekter Algorithmus, Nonce gesetzt) — ohne dafür eine // echte signierte Antwort fälschen zu müssen. func TestTimestampSendsValidRequest(t *testing.T) { hash := evidence.HashBytes([]byte("test-content")) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if ct := r.Header.Get("Content-Type"); ct != "application/timestamp-query" { t.Errorf("Content-Type = %q, want application/timestamp-query", ct) } body, err := io.ReadAll(r.Body) if err != nil { t.Errorf("read request body: %v", err) } req, err := timestamp.ParseRequest(body) if err != nil { t.Errorf("ParseRequest: %v", err) w.WriteHeader(http.StatusBadRequest) return } if !bytes.Equal(req.HashedMessage, hash) { t.Errorf("HashedMessage = %x, want %x", req.HashedMessage, hash) } if req.Nonce == nil { t.Error("expected a nonce to be set on the request") } // Keine echte TSA hier — nur die Request-Validierung interessiert // dieser Test. Ein absichtlich ungültiger Response-Body lässt // Timestamp() mit einem Parse-Fehler zurückkommen, was für diesen // Test in Ordnung ist. w.WriteHeader(http.StatusOK) w.Write([]byte("not a valid timestamp response")) })) defer server.Close() ts := evidence.NewHTTPTimestamper(server.URL) _, err := ts.Timestamp(context.Background(), hash) if err == nil { t.Fatal("expected a parse error for the fake response, got nil") } } func TestTimestampRejectsNon200(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("tsa down")) })) defer server.Close() ts := evidence.NewHTTPTimestamper(server.URL) hash := evidence.HashBytes([]byte("x")) if _, err := ts.Timestamp(context.Background(), hash); err == nil { t.Fatal("expected error for non-200 TSA status, got nil") } } func TestTimestampRejectsMalformedResponse(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("garbage, not ASN.1 DER")) })) defer server.Close() ts := evidence.NewHTTPTimestamper(server.URL) hash := evidence.HashBytes([]byte("x")) if _, err := ts.Timestamp(context.Background(), hash); err == nil { t.Fatal("expected error for malformed TSA response, got nil") } } // TestTimestampIntegration ruft die echte Standard-TSA (FreeTSA.org) auf. // Läuft nur, wenn DEKLARIX_TSA_INTEGRATION gesetzt ist — kein Netzwerkzugriff // in normalen Testläufen (siehe internal/store für dasselbe Muster mit // DATABASE_URL). func TestTimestampIntegration(t *testing.T) { if os.Getenv("DEKLARIX_TSA_INTEGRATION") == "" { t.Skip("DEKLARIX_TSA_INTEGRATION nicht gesetzt, überspringe echten TSA-Aufruf") } hash := evidence.HashBytes([]byte(t.Name())) ts := evidence.NewHTTPTimestamper("") token, err := ts.Timestamp(context.Background(), hash) if err != nil { t.Fatalf("Timestamp: %v", err) } parsed, err := timestamp.Parse(token) if err != nil { t.Fatalf("Parse(token): %v", err) } if !bytes.Equal(parsed.HashedMessage, hash) { t.Fatalf("token hash = %x, want %x", parsed.HashedMessage, hash) } if parsed.Time.IsZero() { t.Fatal("expected a non-zero timestamp time") } tm, err := evidence.TimestampTime(token) if err != nil { t.Fatalf("TimestampTime: %v", err) } if !tm.Equal(parsed.Time) { t.Fatalf("TimestampTime() = %v, want %v", tm, parsed.Time) } }