// Package intentpubstub provides an in-process // ports.IntentPublisher implementation for service-level tests. The // stub records every Publish call and lets tests inject failures to // verify that publication errors do not roll back already-committed // business state. package intentpubstub import ( "context" "errors" "strconv" "sync" "galaxy/lobby/internal/ports" "galaxy/notificationintent" ) // Publisher is a concurrency-safe in-memory implementation of // ports.IntentPublisher. The zero value is not usable; call NewPublisher // to construct. type Publisher struct { mu sync.Mutex published []notificationintent.Intent nextID int err error } // NewPublisher constructs an empty Publisher ready for use. func NewPublisher() *Publisher { return &Publisher{} } // SetError preloads err to be returned by every Publish call. Pass nil // to reset. func (publisher *Publisher) SetError(err error) { if publisher == nil { return } publisher.mu.Lock() defer publisher.mu.Unlock() publisher.err = err } // Publish records intent and returns a synthetic stream entry id. func (publisher *Publisher) Publish(ctx context.Context, intent notificationintent.Intent) (string, error) { if publisher == nil { return "", errors.New("publish notification intent: nil publisher") } if ctx == nil { return "", errors.New("publish notification intent: nil context") } publisher.mu.Lock() defer publisher.mu.Unlock() if publisher.err != nil { return "", publisher.err } publisher.nextID++ publisher.published = append(publisher.published, intent) return strconv.Itoa(publisher.nextID), nil } // Published returns a snapshot of every Publish-accepted intent in the // order it was received. func (publisher *Publisher) Published() []notificationintent.Intent { if publisher == nil { return nil } publisher.mu.Lock() defer publisher.mu.Unlock() out := make([]notificationintent.Intent, len(publisher.published)) copy(out, publisher.published) return out } // Compile-time interface assertion. var _ ports.IntentPublisher = (*Publisher)(nil)