|
| 1 | +package identity_test |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "testing" |
| 6 | + "time" |
| 7 | + |
| 8 | + "github.com/stretchr/testify/mock" |
| 9 | + "github.com/stretchr/testify/require" |
| 10 | + |
| 11 | + "github.com/BEDOLAGA-DEV/RemnaCore/internal/domain/identity" |
| 12 | + "github.com/BEDOLAGA-DEV/RemnaCore/pkg/tenantctx" |
| 13 | +) |
| 14 | + |
| 15 | +// These tests guard against the regression class that broke the deploy: the |
| 16 | +// pre-auth identity paths read/write identity.platform_users, which is FORCE |
| 17 | +// RLS with NO tenant_id-IS-NULL read branch. Under the non-superuser runtime |
| 18 | +// role they see ZERO rows unless the platform sentinel GUC is set. The bug was |
| 19 | +// forgetting RunInTx(WithPlatformScope(ctx)); a plain ctx silently ran unscoped. |
| 20 | +// |
| 21 | +// Each test asserts the CONTEXT passed to the repository carries the platform |
| 22 | +// sentinel. If someone removes the WithPlatformScope wrap, the matcher stops |
| 23 | +// matching and the test fails at merge — without needing a real database. |
| 24 | + |
| 25 | +// platformScoped matches a context whose app.tenant_id resolves to the platform |
| 26 | +// sentinel ("*"). NoopTxRunner passes the RunInTx context straight through, so |
| 27 | +// the repository sees exactly what the service scoped it with. |
| 28 | +var platformScoped = mock.MatchedBy(func(ctx context.Context) bool { |
| 29 | + return tenantctx.TenantIDFromContext(ctx) == tenantctx.PlatformScopeSentinel |
| 30 | +}) |
| 31 | + |
| 32 | +func TestPlatformScope_SetupNeeded(t *testing.T) { |
| 33 | + svc, repo, _ := newTestService(t) |
| 34 | + repo.On("CountAdmins", platformScoped).Return(int64(1), nil) |
| 35 | + |
| 36 | + needed, err := svc.SetupNeeded(context.Background()) |
| 37 | + require.NoError(t, err) |
| 38 | + require.False(t, needed) |
| 39 | + repo.AssertExpectations(t) // fails if CountAdmins wasn't called platform-scoped |
| 40 | +} |
| 41 | + |
| 42 | +func TestPlatformScope_Login(t *testing.T) { |
| 43 | + svc, repo, pub := newTestService(t) |
| 44 | + hash := hashedPassword(t) |
| 45 | + user := &identity.PlatformUser{ID: "u1", Email: "a@b.com", PasswordHash: hash, Role: identity.RoleCustomer} |
| 46 | + |
| 47 | + repo.On("GetUserByEmail", platformScoped, "a@b.com").Return(user, nil) |
| 48 | + repo.On("CreateSession", mock.Anything, mock.AnythingOfType("*aggregate.Session")).Return(nil) |
| 49 | + pub.On("Publish", mock.Anything, mock.AnythingOfType("domainevent.Event")).Return(nil) |
| 50 | + |
| 51 | + _, err := svc.Login(context.Background(), identity.LoginInput{Email: "a@b.com", Password: "StrongP4ss"}) |
| 52 | + require.NoError(t, err) |
| 53 | + repo.AssertExpectations(t) |
| 54 | +} |
| 55 | + |
| 56 | +func TestPlatformScope_RefreshToken(t *testing.T) { |
| 57 | + svc, repo, pub := newTestService(t) |
| 58 | + user := &identity.PlatformUser{ID: "u1", Email: "a@b.com", Role: identity.RoleCustomer} |
| 59 | + session := &identity.Session{ID: "s1", UserID: "u1", RefreshToken: "rt", ExpiresAt: time.Now().Add(time.Hour)} |
| 60 | + |
| 61 | + repo.On("GetSessionByRefreshToken", mock.Anything, "rt").Return(session, nil) |
| 62 | + repo.On("GetUserByID", platformScoped, "u1").Return(user, nil) |
| 63 | + repo.On("DeleteSession", mock.Anything, "s1").Return(nil) |
| 64 | + repo.On("CreateSession", mock.Anything, mock.AnythingOfType("*aggregate.Session")).Return(nil) |
| 65 | + pub.On("Publish", mock.Anything, mock.AnythingOfType("domainevent.Event")).Return(nil) |
| 66 | + |
| 67 | + _, err := svc.RefreshToken(context.Background(), "rt") |
| 68 | + require.NoError(t, err) |
| 69 | + repo.AssertExpectations(t) |
| 70 | +} |
| 71 | + |
| 72 | +func TestPlatformScope_Register(t *testing.T) { |
| 73 | + svc, repo, pub := newTestService(t) |
| 74 | + repo.On("GetUserByEmail", platformScoped, "new@b.com").Return(nil, identity.ErrNotFound) |
| 75 | + repo.On("CreateUser", platformScoped, mock.AnythingOfType("*aggregate.PlatformUser")).Return(nil) |
| 76 | + repo.On("CreateEmailVerification", platformScoped, mock.AnythingOfType("*aggregate.EmailVerification")).Return(nil) |
| 77 | + pub.On("Publish", mock.Anything, mock.AnythingOfType("domainevent.Event")).Return(nil) |
| 78 | + |
| 79 | + _, err := svc.Register(context.Background(), identity.RegisterInput{Email: "new@b.com", Password: "StrongP4ss"}) |
| 80 | + require.NoError(t, err) |
| 81 | + repo.AssertExpectations(t) |
| 82 | +} |
0 commit comments