feat: user service
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
package entitlementsvc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"galaxy/user/internal/domain/common"
|
||||
"galaxy/user/internal/domain/entitlement"
|
||||
"galaxy/user/internal/ports"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestReaderGetByUserIDPublishesExpiredRepairEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
userID := common.UserID("user-123")
|
||||
startsAt := time.Unix(1_775_240_000, 0).UTC()
|
||||
endsAt := startsAt.Add(24 * time.Hour)
|
||||
now := endsAt.Add(time.Hour)
|
||||
snapshotStore := &fakeSnapshotStore{
|
||||
byUserID: map[common.UserID]entitlement.CurrentSnapshot{
|
||||
userID: paidSnapshot(
|
||||
userID,
|
||||
entitlement.PlanCodePaidMonthly,
|
||||
startsAt,
|
||||
endsAt,
|
||||
common.Source("admin"),
|
||||
common.ReasonCode("manual_grant"),
|
||||
),
|
||||
},
|
||||
}
|
||||
historyStore := &fakeHistoryStore{
|
||||
byUserID: map[common.UserID][]entitlement.PeriodRecord{
|
||||
userID: {
|
||||
paidRecord(
|
||||
entitlement.EntitlementRecordID("entitlement-paid"),
|
||||
userID,
|
||||
entitlement.PlanCodePaidMonthly,
|
||||
startsAt,
|
||||
endsAt,
|
||||
common.Source("admin"),
|
||||
common.ReasonCode("manual_grant"),
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
lifecycleStore := &fakeLifecycleStore{
|
||||
historyStore: historyStore,
|
||||
snapshotStore: snapshotStore,
|
||||
}
|
||||
publisher := &recordingEntitlementPublisher{}
|
||||
|
||||
reader, err := NewReaderWithObservability(snapshotStore, lifecycleStore, fixedClock{now: now}, fixedIDGenerator{
|
||||
recordID: entitlement.EntitlementRecordID("entitlement-free"),
|
||||
}, nil, nil, publisher)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := reader.GetByUserID(context.Background(), userID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, entitlement.PlanCodeFree, got.PlanCode)
|
||||
require.Len(t, publisher.events, 1)
|
||||
require.Equal(t, ports.EntitlementChangedOperationExpiredRepaired, publisher.events[0].Operation)
|
||||
require.Equal(t, common.Source("entitlement_expiry_repair"), publisher.events[0].Source)
|
||||
}
|
||||
|
||||
func TestGrantServiceExecutePublisherFailureDoesNotRollbackResult(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
now := time.Unix(1_775_240_000, 0).UTC()
|
||||
userID := common.UserID("user-123")
|
||||
currentFreeStartsAt := now.Add(-24 * time.Hour)
|
||||
currentSnapshot := freeSnapshot(userID, currentFreeStartsAt, common.Source("auth_registration"), common.ReasonCode("initial_free_entitlement"))
|
||||
currentRecord := freeRecord(entitlement.EntitlementRecordID("entitlement-free"), userID, currentFreeStartsAt, common.Source("auth_registration"), common.ReasonCode("initial_free_entitlement"))
|
||||
lifecycleStore := &fakeLifecycleStore{}
|
||||
publisher := &recordingEntitlementPublisher{err: errors.New("publisher unavailable")}
|
||||
|
||||
service, err := NewGrantServiceWithObservability(
|
||||
fakeAccountStore{existsByUserID: map[common.UserID]bool{userID: true}},
|
||||
&fakeHistoryStore{byUserID: map[common.UserID][]entitlement.PeriodRecord{userID: {currentRecord}}},
|
||||
fakeEffectiveReader{byUserID: map[common.UserID]entitlement.CurrentSnapshot{userID: currentSnapshot}},
|
||||
lifecycleStore,
|
||||
fixedClock{now: now},
|
||||
fixedIDGenerator{recordID: entitlement.EntitlementRecordID("entitlement-paid")},
|
||||
nil,
|
||||
nil,
|
||||
publisher,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := service.Execute(context.Background(), GrantInput{
|
||||
UserID: userID.String(),
|
||||
PlanCode: string(entitlement.PlanCodePaidMonthly),
|
||||
Source: "admin",
|
||||
ReasonCode: "manual_grant",
|
||||
Actor: ActorInput{Type: "admin", ID: "admin-1"},
|
||||
StartsAt: now.Format(time.RFC3339Nano),
|
||||
EndsAt: now.Add(30 * 24 * time.Hour).Format(time.RFC3339Nano),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, entitlement.PlanCodePaidMonthly, result.Entitlement.PlanCode)
|
||||
require.Len(t, publisher.events, 1)
|
||||
require.Equal(t, ports.EntitlementChangedOperationGranted, publisher.events[0].Operation)
|
||||
}
|
||||
|
||||
type recordingEntitlementPublisher struct {
|
||||
err error
|
||||
events []ports.EntitlementChangedEvent
|
||||
}
|
||||
|
||||
func (publisher *recordingEntitlementPublisher) PublishEntitlementChanged(_ context.Context, event ports.EntitlementChangedEvent) error {
|
||||
if err := event.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
publisher.events = append(publisher.events, event)
|
||||
return publisher.err
|
||||
}
|
||||
|
||||
var _ ports.EntitlementChangedPublisher = (*recordingEntitlementPublisher)(nil)
|
||||
Reference in New Issue
Block a user