mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Failing after 5m15s
Hanzo CI/CD / test-amd64 (push) Failing after 17m40s
Hanzo CI/CD / cicd (push) Skipped
CI/CD / cicd (push) Failing after 17m53s
The event plane folded every occurrence into event.fact, and the Sentry module still read and wrote event.error. Its reads now select the error signal of event.fact, mapping issue, env, origin and the severity band. Errors enter through /v1/event only. The Sentry envelope and store routes, DSN keys and their rotation leave the module; the consumer of event.error files each fact through Ingest(org, product), which keeps the issue lifecycle and returns the issues a batch created.
92 lines
3.6 KiB
Go
92 lines
3.6 KiB
Go
package implsentry
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
dsmock "github.com/hanzo-ds/mock"
|
|
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
|
"github.com/hanzoai/o11y/pkg/telemetrystore/telemetrystoretest"
|
|
"github.com/hanzoai/o11y/pkg/types/sentrytypes"
|
|
"github.com/hanzoai/o11y/pkg/valuer"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// anyMatcher lets the mock match on expectation type + args, not exact SQL text —
|
|
// the SQL text itself is asserted by the pure builder tests (eventsql_test.go).
|
|
type anyMatcher struct{}
|
|
|
|
func (anyMatcher) Match(string, string) error { return nil }
|
|
|
|
// A read must execute exactly one query and NOTHING else. The store used to run
|
|
// CREATE DATABASE + CREATE TABLE before every operation, which recreated the database
|
|
// it names on the next process start — the way a dropped database comes back from the
|
|
// dead. Only the query below is expected here, so any DDL the store issued would be an
|
|
// unmatched call and fail this test.
|
|
func TestEventStore_ReadsWithoutIssuingDDL(t *testing.T) {
|
|
provider := telemetrystoretest.New(telemetrystore.Config{}, anyMatcher{})
|
|
mock := provider.Mock()
|
|
mock.MatchExpectationsInOrder(false)
|
|
|
|
// DistinctFingerprints → one String column. The 4 bound args are the (org,
|
|
// project, from, to) tenant+window scope every read carries.
|
|
mock.ExpectQuery("SELECT DISTINCT").
|
|
WithArgs(nil, nil, nil, nil). // nil = match-any (dsmock.matchArg); 4 = the tenant+window scope
|
|
WillReturnRows(dsmock.NewRows(
|
|
[]dsmock.ColumnType{{Name: "issue", Type: "String"}},
|
|
[][]any{{"fp-1"}, {"fp-2"}},
|
|
))
|
|
|
|
store := NewEventStore(telemetrystore.TelemetryStore(provider), namedScope("acme", "docs"))
|
|
fps, err := store.DistinctFingerprints(context.Background(), valuer.GenerateUUID(), valuer.GenerateUUID(), testWindow())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []string{"fp-1", "fp-2"}, fps)
|
|
|
|
require.NoError(t, mock.ExpectationsWereMet())
|
|
}
|
|
|
|
// TestEventStoreTargetsEventFact pins WHICH table the plane reads: the one fact
|
|
// table, whose error slice every read selects by signal.
|
|
func TestEventStoreTargetsEventFact(t *testing.T) {
|
|
s := NewEventStore(nil, namedScope("acme", "docs")).(*eventStore)
|
|
assert.Equal(t, "event", s.db)
|
|
assert.Equal(t, "fact", s.table)
|
|
}
|
|
|
|
// TestProjectionMatchesScan pins the read invariant: queryEvents scans exactly as many
|
|
// targets as selectColumns selects.
|
|
func TestProjectionMatchesScan(t *testing.T) {
|
|
assert.Len(t, selectList, 28,
|
|
"the read projection selects the 5 frame arrays plus the attribute-backed fields")
|
|
assert.Equal(t, strings.Join(selectList, ", "), selectColumns)
|
|
}
|
|
|
|
// Frames read back from the five parallel arrays event.fact stores.
|
|
func TestFramesRead(t *testing.T) {
|
|
want := []sentrytypes.Frame{
|
|
{Function: "handle", File: "app/svc.py", Line: 42, Column: 7, Own: true},
|
|
{Function: "connect", File: "inpage.js", Line: 1, Column: 84179, Own: false},
|
|
}
|
|
got := zipFrames([]string{"handle", "connect"}, []string{"app/svc.py", "inpage.js"},
|
|
[]uint32{42, 1}, []uint32{7, 84179}, []bool{true, false})
|
|
assert.Equal(t, want, got)
|
|
}
|
|
|
|
// A short parallel array must read as a zero value, never panic — the shape a row
|
|
// written before a frame column existed has.
|
|
func TestFramesTolerateShortArrays(t *testing.T) {
|
|
got := zipFrames([]string{"handle", "connect"}, []string{"only-one.py"}, nil, nil, nil)
|
|
require.Len(t, got, 2)
|
|
assert.Equal(t, "only-one.py", got[0].File)
|
|
assert.Equal(t, sentrytypes.Frame{Function: "connect"}, got[1])
|
|
}
|
|
|
|
// No frames means no frames — not one empty frame.
|
|
func TestFramesEmpty(t *testing.T) {
|
|
assert.Nil(t, zipFrames(nil, nil, nil, nil, nil))
|
|
}
|
|
|
|
var _ sentrytypes.EventStore = (*eventStore)(nil)
|