//go:build integration package steme import ( "context" "fmt" "testing" "time" ) // Integration tests for Trace/audit operations. // TestIntegration_Trace tests the trace endpoint for audit trail querying. func TestIntegration_Trace(t *testing.T) { client := getTestClient(t) ctx := context.Background() // Create a unique subject for this test subject := uniqueSubject("IntegrationTest_Trace") predicate := "has_trace_value" // Create an assertion to generate query activity assertion := NewAssertion(subject, predicate). WithText("test_trace_value"). WithConfidence(0.95). WithLifecycle(LifecycleApproved). WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000"). Build() hash, err := client.Assert(ctx, assertion) if err != nil { t.Fatalf("Assert() failed: %v", err) } t.Logf("Created assertion with hash: %s", hash) // Small delay for indexing time.Sleep(100 * time.Millisecond) // Perform a query to create audit trail params := NewQuery(). WithSubject(subject). WithPredicate(predicate). Build() queryResult, err := client.Query(ctx, params) if err != nil { t.Fatalf("Query() failed: %v", err) } if queryResult.TotalCount == 0 { t.Fatal("Query() returned no results") } t.Logf("Query returned %d results", queryResult.TotalCount) // Small delay for audit to be written time.Sleep(100 * time.Millisecond) // Get the agent ID from the client's signer agentID := client.signer.PublicKey() // Calculate time range (last 1 hour to now) now := time.Now().Unix() from := now - 3600 // Trace the agent's queries traceParams := TraceParams{ AgentID: agentID, From: fmt.Sprintf("%d", from), To: fmt.Sprintf("%d", now), Subject: subject, // Filter to our test subject Limit: 100, } traceResult, err := client.Trace(ctx, traceParams) if err != nil { t.Fatalf("Trace() failed: %v", err) } t.Logf("Trace returned %d audit records", traceResult.TotalCount) // Verify we got at least one audit record if traceResult.TotalCount == 0 { t.Error("Trace() returned no audit records") } // Verify the trace result has the correct agent ID if traceResult.AgentID != agentID { t.Errorf("TraceResult.AgentID = %s, want %s", traceResult.AgentID, agentID) } // Verify the trace result has the correct time range if traceResult.FromTimestamp != uint64(from) { t.Errorf("TraceResult.FromTimestamp = %d, want %d", traceResult.FromTimestamp, from) } // Verify audit records contain our query foundOurQuery := false for _, audit := range traceResult.Audits { t.Logf("Audit record: query_id=%s, timestamp=%d, subject=%v, predicate=%v", audit.QueryID, audit.Timestamp, audit.Params.Subject, audit.Params.Predicate) // Check if this audit is for our query if audit.Params.Subject != nil && *audit.Params.Subject == subject && audit.Params.Predicate != nil && *audit.Params.Predicate == predicate { foundOurQuery = true // Verify agent ID if audit.AgentID == nil || *audit.AgentID != agentID { t.Errorf("Audit.AgentID = %v, want %s", audit.AgentID, agentID) } // Verify result hash is present if audit.ResultHash == nil { t.Error("Audit.ResultHash is nil, expected a result") } // Verify contributing assertions if len(audit.ContributingAssertions) == 0 { t.Error("Audit.ContributingAssertions is empty, expected at least one") } } } if !foundOurQuery { t.Errorf("Did not find audit record for our query (subject=%s, predicate=%s)", subject, predicate) } } // TestIntegration_Trace_WithPattern tests trace with subject pattern filtering. func TestIntegration_Trace_WithPattern(t *testing.T) { client := getTestClient(t) ctx := context.Background() // Create unique subjects with a common prefix prefix := uniqueSubject("IntegrationTest_TracePattern") subject1 := prefix + "_A" subject2 := prefix + "_B" predicate := "has_value" // Create assertions and query them for _, subj := range []string{subject1, subject2} { assertion := NewAssertion(subj, predicate). WithText("test_value"). WithConfidence(0.9). WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000"). Build() _, err := client.Assert(ctx, assertion) if err != nil { t.Fatalf("Assert() for %s failed: %v", subj, err) } time.Sleep(50 * time.Millisecond) // Query to create audit trail params := NewQuery().WithSubject(subj).WithPredicate(predicate).Build() _, err = client.Query(ctx, params) if err != nil { t.Fatalf("Query() for %s failed: %v", subj, err) } } time.Sleep(100 * time.Millisecond) // Trace with wildcard pattern agentID := client.signer.PublicKey() now := time.Now().Unix() from := now - 3600 traceParams := TraceParams{ AgentID: agentID, From: fmt.Sprintf("%d", from), To: fmt.Sprintf("%d", now), Subject: prefix + "*", // Match both subjects Limit: 100, } traceResult, err := client.Trace(ctx, traceParams) if err != nil { t.Fatalf("Trace() failed: %v", err) } t.Logf("Trace with pattern returned %d audit records", traceResult.TotalCount) // Should find both queries foundSubjects := make(map[string]bool) for _, audit := range traceResult.Audits { if audit.Params.Subject != nil { foundSubjects[*audit.Params.Subject] = true } } if !foundSubjects[subject1] { t.Errorf("Did not find audit for subject %s", subject1) } if !foundSubjects[subject2] { t.Errorf("Did not find audit for subject %s", subject2) } }