1
0
forked from baron/baron-sso
Files
baron-sso/backend/internal/service/rp_usage_event_emitter_test.go

133 lines
3.6 KiB
Go

package service
import (
"baron-sso-backend/internal/domain"
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/require"
)
type fakeRPUsageOutboxRepo struct {
created []domain.RPUsageEvent
ready []domain.RPUsageEvent
processing []string
processed []string
failed []string
createErr error
projectErr error
}
func (f *fakeRPUsageOutboxRepo) Create(ctx context.Context, event *domain.RPUsageEvent) error {
if f.createErr != nil {
return f.createErr
}
f.created = append(f.created, *event)
return nil
}
func (f *fakeRPUsageOutboxRepo) ListReady(ctx context.Context, limit int) ([]domain.RPUsageEvent, error) {
return f.ready, nil
}
func (f *fakeRPUsageOutboxRepo) MarkProcessing(ctx context.Context, id string) error {
f.processing = append(f.processing, id)
return nil
}
func (f *fakeRPUsageOutboxRepo) MarkProcessed(ctx context.Context, id string) error {
f.processed = append(f.processed, id)
return nil
}
func (f *fakeRPUsageOutboxRepo) MarkFailed(ctx context.Context, id string, message string, nextAttemptAt time.Time) error {
f.failed = append(f.failed, id)
return nil
}
type fakeRPUsageProjectionRepo struct {
created []domain.RPUsageEvent
err error
}
func (f *fakeRPUsageProjectionRepo) CreateRPUsageEvent(ctx context.Context, event domain.RPUsageEvent) error {
if f.err != nil {
return f.err
}
f.created = append(f.created, event)
return nil
}
func TestRPUsageEventEmitterRequiresCanonicalFields(t *testing.T) {
repo := &fakeRPUsageOutboxRepo{}
emitter := NewRPUsageEventEmitter(repo)
err := emitter.EmitRPUsageEvent(context.Background(), domain.RPUsageEvent{
EventType: domain.RPUsageEventTypeAuthorizationGranted,
ClientID: "client-app",
})
require.Error(t, err)
require.Empty(t, repo.created)
}
func TestRPUsageEventEmitterCreatesPendingOutboxEvent(t *testing.T) {
repo := &fakeRPUsageOutboxRepo{}
emitter := NewRPUsageEventEmitter(repo)
err := emitter.EmitRPUsageEvent(context.Background(), domain.RPUsageEvent{
EventType: domain.RPUsageEventTypeAuthorizationGranted,
Subject: "user-123",
ClientID: "client-app",
Source: "hydra_consent",
CorrelationID: "challenge-1",
})
require.NoError(t, err)
require.Len(t, repo.created, 1)
require.NotEmpty(t, repo.created[0].DedupeKey)
require.Equal(t, domain.RPUsageEventTypeAuthorizationGranted, repo.created[0].EventType)
require.Equal(t, "hydra_consent", repo.created[0].Source)
}
func TestRPUsageProjectorWorkerMarksProcessedAfterProjection(t *testing.T) {
outbox := &fakeRPUsageOutboxRepo{
ready: []domain.RPUsageEvent{{
ID: "event-1",
EventType: domain.RPUsageEventTypeAuthorizationGranted,
Subject: "user-123",
ClientID: "client-app",
}},
}
projection := &fakeRPUsageProjectionRepo{}
worker := NewRPUsageProjectorWorker(outbox, projection)
worker.processOnce(context.Background())
require.Equal(t, []string{"event-1"}, outbox.processing)
require.Equal(t, []string{"event-1"}, outbox.processed)
require.Empty(t, outbox.failed)
require.Len(t, projection.created, 1)
}
func TestRPUsageProjectorWorkerMarksFailedWhenProjectionFails(t *testing.T) {
outbox := &fakeRPUsageOutboxRepo{
ready: []domain.RPUsageEvent{{
ID: "event-1",
EventType: domain.RPUsageEventTypeAuthorizationGranted,
Subject: "user-123",
ClientID: "client-app",
}},
}
projection := &fakeRPUsageProjectionRepo{err: errors.New("clickhouse unavailable")}
worker := NewRPUsageProjectorWorker(outbox, projection)
worker.processOnce(context.Background())
require.Equal(t, []string{"event-1"}, outbox.processing)
require.Empty(t, outbox.processed)
require.Equal(t, []string{"event-1"}, outbox.failed)
}