563 lines
18 KiB
Go
563 lines
18 KiB
Go
package entitlementsvc
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"galaxy/user/internal/domain/account"
|
|
"galaxy/user/internal/domain/common"
|
|
"galaxy/user/internal/domain/entitlement"
|
|
"galaxy/user/internal/domain/policy"
|
|
"galaxy/user/internal/ports"
|
|
"galaxy/user/internal/service/shared"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestReaderGetByUserIDRepairsExpiredFinitePaidSnapshot(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(2 * 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,
|
|
}
|
|
|
|
reader, err := NewReader(snapshotStore, lifecycleStore, fixedClock{now: now}, fixedIDGenerator{
|
|
recordID: entitlement.EntitlementRecordID("entitlement-free"),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
got, err := reader.GetByUserID(context.Background(), userID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, entitlement.PlanCodeFree, got.PlanCode)
|
|
require.False(t, got.IsPaid)
|
|
require.Equal(t, endsAt, got.StartsAt)
|
|
require.Equal(t, expiryRepairSource, got.Source)
|
|
require.Equal(t, expiryRepairReasonCode, got.ReasonCode)
|
|
require.Equal(t, common.ActorRef{Type: expiryRepairActorType, ID: expiryRepairActorID}, got.Actor)
|
|
require.Len(t, historyStore.byUserID[userID], 2)
|
|
require.Equal(t, got, snapshotStore.byUserID[userID])
|
|
require.Equal(t, entitlement.EntitlementRecordID("entitlement-free"), lifecycleStore.repairInput.NewRecord.RecordID)
|
|
}
|
|
|
|
func TestGrantServiceExecuteRejectsInvalidPlanRules(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Unix(1_775_240_000, 0).UTC()
|
|
userID := common.UserID("user-123")
|
|
freeSnapshot := freeSnapshot(userID, now.Add(-24*time.Hour), common.Source("auth_registration"), common.ReasonCode("initial_free_entitlement"))
|
|
freeRecord := freeRecord(entitlement.EntitlementRecordID("entitlement-free"), userID, now.Add(-24*time.Hour), common.Source("auth_registration"), common.ReasonCode("initial_free_entitlement"))
|
|
|
|
tests := []struct {
|
|
name string
|
|
input GrantInput
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "free plan not allowed",
|
|
input: GrantInput{
|
|
UserID: userID.String(),
|
|
PlanCode: string(entitlement.PlanCodeFree),
|
|
Source: "admin",
|
|
ReasonCode: "manual_grant",
|
|
Actor: ActorInput{Type: "admin", ID: "admin-1"},
|
|
StartsAt: now.Format(time.RFC3339Nano),
|
|
},
|
|
wantErr: shared.ErrorCodeInvalidRequest,
|
|
},
|
|
{
|
|
name: "future starts at rejected",
|
|
input: GrantInput{
|
|
UserID: userID.String(),
|
|
PlanCode: string(entitlement.PlanCodePaidMonthly),
|
|
Source: "admin",
|
|
ReasonCode: "manual_grant",
|
|
Actor: ActorInput{Type: "admin", ID: "admin-1"},
|
|
StartsAt: now.Add(time.Hour).Format(time.RFC3339Nano),
|
|
EndsAt: now.Add(31 * 24 * time.Hour).Format(time.RFC3339Nano),
|
|
},
|
|
wantErr: shared.ErrorCodeInvalidRequest,
|
|
},
|
|
{
|
|
name: "finite plan requires ends at",
|
|
input: 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),
|
|
},
|
|
wantErr: shared.ErrorCodeInvalidRequest,
|
|
},
|
|
{
|
|
name: "lifetime plan forbids ends at",
|
|
input: GrantInput{
|
|
UserID: userID.String(),
|
|
PlanCode: string(entitlement.PlanCodePaidLifetime),
|
|
Source: "admin",
|
|
ReasonCode: "manual_grant",
|
|
Actor: ActorInput{Type: "admin", ID: "admin-1"},
|
|
StartsAt: now.Format(time.RFC3339Nano),
|
|
EndsAt: now.Add(24 * time.Hour).Format(time.RFC3339Nano),
|
|
},
|
|
wantErr: shared.ErrorCodeInvalidRequest,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
service, err := NewGrantService(
|
|
fakeAccountStore{existsByUserID: map[common.UserID]bool{userID: true}},
|
|
&fakeHistoryStore{byUserID: map[common.UserID][]entitlement.PeriodRecord{userID: {freeRecord}}},
|
|
fakeEffectiveReader{byUserID: map[common.UserID]entitlement.CurrentSnapshot{userID: freeSnapshot}},
|
|
&fakeLifecycleStore{},
|
|
fixedClock{now: now},
|
|
fixedIDGenerator{recordID: entitlement.EntitlementRecordID("entitlement-paid")},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = service.Execute(context.Background(), tt.input)
|
|
require.Error(t, err)
|
|
require.Equal(t, tt.wantErr, shared.CodeOf(err))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGrantServiceExecuteBuildsTransition(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{}
|
|
|
|
service, err := NewGrantService(
|
|
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")},
|
|
)
|
|
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, userID.String(), result.UserID)
|
|
require.Equal(t, entitlement.PlanCodePaidMonthly, result.Entitlement.PlanCode)
|
|
require.Equal(t, entitlement.EntitlementRecordID("entitlement-paid"), lifecycleStore.grantInput.NewRecord.RecordID)
|
|
require.Equal(t, currentSnapshot, lifecycleStore.grantInput.ExpectedCurrentSnapshot)
|
|
require.Equal(t, currentRecord.RecordID, lifecycleStore.grantInput.UpdatedCurrentRecord.RecordID)
|
|
require.NotNil(t, lifecycleStore.grantInput.UpdatedCurrentRecord.ClosedAt)
|
|
require.True(t, lifecycleStore.grantInput.UpdatedCurrentRecord.ClosedAt.Equal(now))
|
|
}
|
|
|
|
func TestExtendServiceExecuteBuildsExtensionSegment(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Unix(1_775_240_000, 0).UTC()
|
|
userID := common.UserID("user-123")
|
|
startsAt := now.Add(-24 * time.Hour)
|
|
currentEndsAt := now.Add(24 * time.Hour)
|
|
currentSnapshot := paidSnapshot(
|
|
userID,
|
|
entitlement.PlanCodePaidMonthly,
|
|
startsAt,
|
|
currentEndsAt,
|
|
common.Source("admin"),
|
|
common.ReasonCode("manual_grant"),
|
|
)
|
|
currentRecord := paidRecord(
|
|
entitlement.EntitlementRecordID("entitlement-paid-1"),
|
|
userID,
|
|
entitlement.PlanCodePaidMonthly,
|
|
startsAt,
|
|
currentEndsAt,
|
|
common.Source("admin"),
|
|
common.ReasonCode("manual_grant"),
|
|
)
|
|
lifecycleStore := &fakeLifecycleStore{}
|
|
|
|
service, err := NewExtendService(
|
|
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-2")},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
result, err := service.Execute(context.Background(), ExtendInput{
|
|
UserID: userID.String(),
|
|
Source: "admin",
|
|
ReasonCode: "manual_extend",
|
|
Actor: ActorInput{Type: "admin", ID: "admin-1"},
|
|
EndsAt: currentEndsAt.Add(30 * 24 * time.Hour).Format(time.RFC3339Nano),
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, currentEndsAt, lifecycleStore.extendInput.NewRecord.StartsAt)
|
|
require.Equal(t, startsAt, lifecycleStore.extendInput.NewSnapshot.StartsAt)
|
|
require.Equal(t, entitlement.PlanCodePaidMonthly, result.Entitlement.PlanCode)
|
|
}
|
|
|
|
func TestRevokeServiceExecuteBuildsFreeTransition(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Unix(1_775_240_000, 0).UTC()
|
|
userID := common.UserID("user-123")
|
|
startsAt := now.Add(-24 * time.Hour)
|
|
currentEndsAt := now.Add(24 * time.Hour)
|
|
currentSnapshot := paidSnapshot(
|
|
userID,
|
|
entitlement.PlanCodePaidMonthly,
|
|
startsAt,
|
|
currentEndsAt,
|
|
common.Source("admin"),
|
|
common.ReasonCode("manual_grant"),
|
|
)
|
|
currentRecord := paidRecord(
|
|
entitlement.EntitlementRecordID("entitlement-paid-1"),
|
|
userID,
|
|
entitlement.PlanCodePaidMonthly,
|
|
startsAt,
|
|
currentEndsAt,
|
|
common.Source("admin"),
|
|
common.ReasonCode("manual_grant"),
|
|
)
|
|
lifecycleStore := &fakeLifecycleStore{}
|
|
|
|
service, err := NewRevokeService(
|
|
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-free-2")},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
result, err := service.Execute(context.Background(), RevokeInput{
|
|
UserID: userID.String(),
|
|
Source: "admin",
|
|
ReasonCode: "manual_revoke",
|
|
Actor: ActorInput{Type: "admin", ID: "admin-1"},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, entitlement.PlanCodeFree, result.Entitlement.PlanCode)
|
|
require.NotNil(t, lifecycleStore.revokeInput.UpdatedCurrentRecord.ClosedAt)
|
|
require.True(t, lifecycleStore.revokeInput.UpdatedCurrentRecord.ClosedAt.Equal(now))
|
|
require.Equal(t, now, lifecycleStore.revokeInput.NewRecord.StartsAt)
|
|
}
|
|
|
|
type fakeAccountStore struct {
|
|
existsByUserID map[common.UserID]bool
|
|
}
|
|
|
|
func (store fakeAccountStore) Create(context.Context, ports.CreateAccountInput) error {
|
|
return nil
|
|
}
|
|
|
|
func (store fakeAccountStore) GetByUserID(context.Context, common.UserID) (account.UserAccount, error) {
|
|
return account.UserAccount{}, ports.ErrNotFound
|
|
}
|
|
|
|
func (store fakeAccountStore) GetByEmail(context.Context, common.Email) (account.UserAccount, error) {
|
|
return account.UserAccount{}, ports.ErrNotFound
|
|
}
|
|
|
|
func (store fakeAccountStore) GetByUserName(context.Context, common.UserName) (account.UserAccount, error) {
|
|
return account.UserAccount{}, ports.ErrNotFound
|
|
}
|
|
|
|
func (store fakeAccountStore) ExistsByUserID(_ context.Context, userID common.UserID) (bool, error) {
|
|
return store.existsByUserID[userID], nil
|
|
}
|
|
|
|
|
|
func (store fakeAccountStore) Update(context.Context, account.UserAccount) error {
|
|
return nil
|
|
}
|
|
|
|
type fakeSnapshotStore struct {
|
|
byUserID map[common.UserID]entitlement.CurrentSnapshot
|
|
}
|
|
|
|
func (store *fakeSnapshotStore) GetByUserID(_ context.Context, userID common.UserID) (entitlement.CurrentSnapshot, error) {
|
|
record, ok := store.byUserID[userID]
|
|
if !ok {
|
|
return entitlement.CurrentSnapshot{}, ports.ErrNotFound
|
|
}
|
|
|
|
return record, nil
|
|
}
|
|
|
|
func (store *fakeSnapshotStore) Put(_ context.Context, record entitlement.CurrentSnapshot) error {
|
|
store.byUserID[record.UserID] = record
|
|
return nil
|
|
}
|
|
|
|
type fakeHistoryStore struct {
|
|
byUserID map[common.UserID][]entitlement.PeriodRecord
|
|
}
|
|
|
|
func (store *fakeHistoryStore) Create(_ context.Context, record entitlement.PeriodRecord) error {
|
|
store.byUserID[record.UserID] = append(store.byUserID[record.UserID], record)
|
|
return nil
|
|
}
|
|
|
|
func (store *fakeHistoryStore) GetByRecordID(_ context.Context, recordID entitlement.EntitlementRecordID) (entitlement.PeriodRecord, error) {
|
|
for _, records := range store.byUserID {
|
|
for _, record := range records {
|
|
if record.RecordID == recordID {
|
|
return record, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return entitlement.PeriodRecord{}, ports.ErrNotFound
|
|
}
|
|
|
|
func (store *fakeHistoryStore) ListByUserID(_ context.Context, userID common.UserID) ([]entitlement.PeriodRecord, error) {
|
|
records := store.byUserID[userID]
|
|
cloned := make([]entitlement.PeriodRecord, len(records))
|
|
copy(cloned, records)
|
|
return cloned, nil
|
|
}
|
|
|
|
func (store *fakeHistoryStore) Update(_ context.Context, record entitlement.PeriodRecord) error {
|
|
records := store.byUserID[record.UserID]
|
|
for idx := range records {
|
|
if records[idx].RecordID == record.RecordID {
|
|
records[idx] = record
|
|
store.byUserID[record.UserID] = records
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return ports.ErrNotFound
|
|
}
|
|
|
|
type fakeEffectiveReader struct {
|
|
byUserID map[common.UserID]entitlement.CurrentSnapshot
|
|
}
|
|
|
|
func (reader fakeEffectiveReader) GetByUserID(_ context.Context, userID common.UserID) (entitlement.CurrentSnapshot, error) {
|
|
record, ok := reader.byUserID[userID]
|
|
if !ok {
|
|
return entitlement.CurrentSnapshot{}, ports.ErrNotFound
|
|
}
|
|
|
|
return record, nil
|
|
}
|
|
|
|
type fakeLifecycleStore struct {
|
|
historyStore *fakeHistoryStore
|
|
snapshotStore *fakeSnapshotStore
|
|
|
|
grantInput ports.GrantEntitlementInput
|
|
extendInput ports.ExtendEntitlementInput
|
|
revokeInput ports.RevokeEntitlementInput
|
|
repairInput ports.RepairExpiredEntitlementInput
|
|
}
|
|
|
|
func (store *fakeLifecycleStore) Grant(_ context.Context, input ports.GrantEntitlementInput) error {
|
|
store.grantInput = input
|
|
return nil
|
|
}
|
|
|
|
func (store *fakeLifecycleStore) Extend(_ context.Context, input ports.ExtendEntitlementInput) error {
|
|
store.extendInput = input
|
|
return nil
|
|
}
|
|
|
|
func (store *fakeLifecycleStore) Revoke(_ context.Context, input ports.RevokeEntitlementInput) error {
|
|
store.revokeInput = input
|
|
return nil
|
|
}
|
|
|
|
func (store *fakeLifecycleStore) RepairExpired(_ context.Context, input ports.RepairExpiredEntitlementInput) error {
|
|
store.repairInput = input
|
|
if store.historyStore != nil {
|
|
store.historyStore.byUserID[input.NewRecord.UserID] = append(store.historyStore.byUserID[input.NewRecord.UserID], input.NewRecord)
|
|
}
|
|
if store.snapshotStore != nil {
|
|
store.snapshotStore.byUserID[input.NewSnapshot.UserID] = input.NewSnapshot
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type fixedClock struct {
|
|
now time.Time
|
|
}
|
|
|
|
func (clock fixedClock) Now() time.Time {
|
|
return clock.now
|
|
}
|
|
|
|
type fixedIDGenerator struct {
|
|
recordID entitlement.EntitlementRecordID
|
|
sanctionRecordID policy.SanctionRecordID
|
|
limitRecordID policy.LimitRecordID
|
|
}
|
|
|
|
func (generator fixedIDGenerator) NewUserID() (common.UserID, error) {
|
|
return "", nil
|
|
}
|
|
|
|
func (generator fixedIDGenerator) NewUserName() (common.UserName, error) {
|
|
return "", nil
|
|
}
|
|
|
|
func (generator fixedIDGenerator) NewEntitlementRecordID() (entitlement.EntitlementRecordID, error) {
|
|
return generator.recordID, nil
|
|
}
|
|
|
|
func (generator fixedIDGenerator) NewSanctionRecordID() (policy.SanctionRecordID, error) {
|
|
return generator.sanctionRecordID, nil
|
|
}
|
|
|
|
func (generator fixedIDGenerator) NewLimitRecordID() (policy.LimitRecordID, error) {
|
|
return generator.limitRecordID, nil
|
|
}
|
|
|
|
func freeSnapshot(
|
|
userID common.UserID,
|
|
startsAt time.Time,
|
|
source common.Source,
|
|
reasonCode common.ReasonCode,
|
|
) entitlement.CurrentSnapshot {
|
|
return entitlement.CurrentSnapshot{
|
|
UserID: userID,
|
|
PlanCode: entitlement.PlanCodeFree,
|
|
IsPaid: false,
|
|
StartsAt: startsAt,
|
|
Source: source,
|
|
Actor: common.ActorRef{Type: common.ActorType("service"), ID: common.ActorID("user-service")},
|
|
ReasonCode: reasonCode,
|
|
UpdatedAt: startsAt,
|
|
}
|
|
}
|
|
|
|
func freeRecord(
|
|
recordID entitlement.EntitlementRecordID,
|
|
userID common.UserID,
|
|
startsAt time.Time,
|
|
source common.Source,
|
|
reasonCode common.ReasonCode,
|
|
) entitlement.PeriodRecord {
|
|
return entitlement.PeriodRecord{
|
|
RecordID: recordID,
|
|
UserID: userID,
|
|
PlanCode: entitlement.PlanCodeFree,
|
|
Source: source,
|
|
Actor: common.ActorRef{Type: common.ActorType("service"), ID: common.ActorID("user-service")},
|
|
ReasonCode: reasonCode,
|
|
StartsAt: startsAt,
|
|
CreatedAt: startsAt,
|
|
}
|
|
}
|
|
|
|
func paidSnapshot(
|
|
userID common.UserID,
|
|
planCode entitlement.PlanCode,
|
|
startsAt time.Time,
|
|
endsAt time.Time,
|
|
source common.Source,
|
|
reasonCode common.ReasonCode,
|
|
) entitlement.CurrentSnapshot {
|
|
return entitlement.CurrentSnapshot{
|
|
UserID: userID,
|
|
PlanCode: planCode,
|
|
IsPaid: true,
|
|
StartsAt: startsAt,
|
|
EndsAt: timePointer(endsAt),
|
|
Source: source,
|
|
Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-1")},
|
|
ReasonCode: reasonCode,
|
|
UpdatedAt: startsAt,
|
|
}
|
|
}
|
|
|
|
func paidRecord(
|
|
recordID entitlement.EntitlementRecordID,
|
|
userID common.UserID,
|
|
planCode entitlement.PlanCode,
|
|
startsAt time.Time,
|
|
endsAt time.Time,
|
|
source common.Source,
|
|
reasonCode common.ReasonCode,
|
|
) entitlement.PeriodRecord {
|
|
return entitlement.PeriodRecord{
|
|
RecordID: recordID,
|
|
UserID: userID,
|
|
PlanCode: planCode,
|
|
Source: source,
|
|
Actor: common.ActorRef{Type: common.ActorType("admin"), ID: common.ActorID("admin-1")},
|
|
ReasonCode: reasonCode,
|
|
StartsAt: startsAt,
|
|
EndsAt: timePointer(endsAt),
|
|
CreatedAt: startsAt,
|
|
}
|
|
}
|
|
|
|
func timePointer(value time.Time) *time.Time {
|
|
utcValue := value.UTC()
|
|
return &utcValue
|
|
}
|
|
|
|
var (
|
|
_ ports.UserAccountStore = fakeAccountStore{}
|
|
_ ports.EntitlementSnapshotStore = (*fakeSnapshotStore)(nil)
|
|
_ ports.EntitlementHistoryStore = (*fakeHistoryStore)(nil)
|
|
_ ports.EntitlementLifecycleStore = (*fakeLifecycleStore)(nil)
|
|
_ ports.Clock = fixedClock{}
|
|
_ ports.IDGenerator = fixedIDGenerator{}
|
|
)
|