feat: user service

This commit is contained in:
Ilia Denisov
2026-04-10 19:05:02 +02:00
committed by GitHub
parent 710bad712e
commit 23ffcb7535
140 changed files with 33418 additions and 952 deletions
@@ -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)