feat: add durable Kuknos settlement coordination
This commit is contained in:
@@ -1,23 +1,41 @@
|
||||
// Package health provides the service readiness use case.
|
||||
package health
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gl/domain/ledger"
|
||||
)
|
||||
|
||||
type Database interface {
|
||||
Ping(context.Context) error
|
||||
}
|
||||
|
||||
type SettlementMonitor interface {
|
||||
Stats(context.Context) (ledger.SettlementStats, error)
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
Serving bool
|
||||
DatabaseReady bool
|
||||
Serving bool
|
||||
DatabaseReady bool
|
||||
SettlementEnabled bool
|
||||
SettlementReady bool
|
||||
SettlementStats ledger.SettlementStats
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
database Database
|
||||
database Database
|
||||
settlement SettlementMonitor
|
||||
settlementEnabled bool
|
||||
}
|
||||
|
||||
func NewService(database Database) *Service {
|
||||
return &Service{database: database}
|
||||
func NewService(database Database, settlement ...SettlementMonitor) *Service {
|
||||
service := &Service{database: database}
|
||||
if len(settlement) > 0 && settlement[0] != nil {
|
||||
service.settlement = settlement[0]
|
||||
service.settlementEnabled = true
|
||||
}
|
||||
return service
|
||||
}
|
||||
|
||||
func (s *Service) Check(ctx context.Context) Status {
|
||||
@@ -26,5 +44,13 @@ func (s *Service) Check(ctx context.Context) Status {
|
||||
status.DatabaseReady = s.database.Ping(ctx) == nil
|
||||
}
|
||||
status.Serving = status.DatabaseReady
|
||||
status.SettlementEnabled = s.settlementEnabled
|
||||
if status.DatabaseReady && s.settlementEnabled {
|
||||
stats, err := s.settlement.Stats(ctx)
|
||||
if err == nil {
|
||||
status.SettlementStats = stats
|
||||
status.SettlementReady = true
|
||||
}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
@@ -4,12 +4,23 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gl/domain/ledger"
|
||||
)
|
||||
|
||||
type databaseStub struct{ err error }
|
||||
|
||||
func (s databaseStub) Ping(context.Context) error { return s.err }
|
||||
|
||||
type settlementStub struct {
|
||||
stats ledger.SettlementStats
|
||||
err error
|
||||
}
|
||||
|
||||
func (s settlementStub) Stats(context.Context) (ledger.SettlementStats, error) {
|
||||
return s.stats, s.err
|
||||
}
|
||||
|
||||
func TestCheckReportsDatabaseReadiness(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
@@ -28,3 +39,25 @@ func TestCheckReportsDatabaseReadiness(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckReportsSettlementBacklog(t *testing.T) {
|
||||
monitor := settlementStub{stats: ledger.SettlementStats{Pending: 2, Retryable: 3, OldestPendingSeconds: 60}}
|
||||
got := NewService(databaseStub{}, monitor).Check(context.Background())
|
||||
if !got.Serving || !got.DatabaseReady || !got.SettlementEnabled || !got.SettlementReady || got.SettlementStats != monitor.stats {
|
||||
t.Fatalf("unexpected status: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckExposesManualReviewWithoutStoppingSettlementQueue(t *testing.T) {
|
||||
got := NewService(databaseStub{}, settlementStub{stats: ledger.SettlementStats{ManualReview: 1}}).Check(context.Background())
|
||||
if !got.Serving || !got.SettlementReady || got.SettlementStats.ManualReview != 1 {
|
||||
t.Fatalf("unexpected status: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckReportsSettlementQueryFailure(t *testing.T) {
|
||||
got := NewService(databaseStub{}, settlementStub{err: errors.New("query failed")}).Check(context.Background())
|
||||
if !got.Serving || got.SettlementReady {
|
||||
t.Fatalf("unexpected status: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user