package rootparticipation import ( "context" "errors" "testing" "github.com/carlosframework/platform/internal/rootstate" ) type transactionTestStore struct { rootstate.Store held bool locks int } func (s *transactionTestStore) Lock(context.Context, string) (func(), error) { if s.held { return nil, ErrUnavailable } s.held = true s.locks++ return func() { s.held = false }, nil } func TestPlan14ParticipationTransactionScope(t *testing.T) { s := &transactionTestStore{} l := Ledger{State: s} var stale context.Context if err := l.WithTransaction(t.Context(), func(ctx context.Context) error { stale = ctx held, ok, err := TransactionLedger(ctx) if err != nil || !ok || held.State != s || !s.held { t.Fatal("missing held ledger", err) } foreign := Ledger{State: &transactionTestStore{}} if err := foreign.WithTransaction(ctx, func(context.Context) error { t.Fatal("foreign token accepted"); return nil }); !errors.Is(err, ErrUnavailable) { t.Fatal(err) } return l.WithTransaction(ctx, func(context.Context) error { return nil }) }); err != nil { t.Fatal(err) } if s.held || s.locks != 1 { t.Fatal("nested lock or leak", s) } if _, _, err := TransactionLedger(stale); !errors.Is(err, ErrUnavailable) { t.Fatal("stale token accepted", err) } if err := l.WithTransaction(stale, func(context.Context) error { t.Fatal("stale token reused"); return nil }); !errors.Is(err, ErrUnavailable) { t.Fatal(err) } sentinel := errors.New("effect") if err := l.WithTransaction(t.Context(), func(context.Context) error { return sentinel }); !errors.Is(err, sentinel) || s.held { t.Fatal("error lost or lock leaked", err) } }