package revokeallusersessions import ( "context" "errors" "testing" "time" "galaxy/authsession/internal/domain/common" "galaxy/authsession/internal/domain/devicesession" "galaxy/authsession/internal/domain/gatewayprojection" "galaxy/authsession/internal/service/shared" "galaxy/authsession/internal/testkit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestExecuteRevokesExistingUserSessionsAndPublishes(t *testing.T) { t.Parallel() store := &testkit.InMemorySessionStore{} userDirectory := &testkit.InMemoryUserDirectory{} publisher := &testkit.RecordingProjectionPublisher{} if err := userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1")); err != nil { require.Failf(t, "test failed", "SeedExisting() returned error: %v", err) } for _, record := range []devicesession.Session{ activeSessionFixture("device-session-1", "user-1", time.Unix(10, 0).UTC()), activeSessionFixture("device-session-2", "user-1", time.Unix(20, 0).UTC()), } { if err := store.Create(context.Background(), record); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } } service, err := New(store, userDirectory, publisher, testkit.FixedClock{Time: time.Unix(30, 0).UTC()}) require.NoError(t, err) result, err := service.Execute(context.Background(), Input{ UserID: "user-1", ReasonCode: "logout_all", ActorType: "system", }) require.NoError(t, err) assert.Equal(t, "revoked", result.Outcome) assert.EqualValues(t, 2, result.AffectedSessionCount) assert.Equal(t, []string{"device-session-2", "device-session-1"}, result.AffectedDeviceSessionIDs) for _, deviceSessionID := range result.AffectedDeviceSessionIDs { stored, getErr := store.Get(context.Background(), common.DeviceSessionID(deviceSessionID)) require.NoError(t, getErr) require.NotNil(t, stored.Revocation) assert.Equal(t, devicesession.StatusRevoked, stored.Status) assert.Equal(t, devicesession.RevokeReasonLogoutAll, stored.Revocation.ReasonCode) assert.Equal(t, common.RevokeActorType("system"), stored.Revocation.ActorType) assert.Empty(t, stored.Revocation.ActorID) assert.Equal(t, time.Unix(30, 0).UTC(), stored.Revocation.At) } published := publisher.PublishedSnapshots() require.Len(t, published, 2) assert.Equal(t, []common.DeviceSessionID{"device-session-2", "device-session-1"}, []common.DeviceSessionID{ published[0].DeviceSessionID, published[1].DeviceSessionID, }) for _, snapshot := range published { assert.Equal(t, gatewayprojection.StatusRevoked, snapshot.Status) assert.Equal(t, devicesession.RevokeReasonLogoutAll, snapshot.RevokeReasonCode) assert.Equal(t, common.RevokeActorType("system"), snapshot.RevokeActorType) require.NotNil(t, snapshot.RevokedAt) assert.Equal(t, time.Unix(30, 0).UTC(), *snapshot.RevokedAt) } } func TestExecuteReturnsNoActiveSessionsForExistingUserWithoutActiveSessions(t *testing.T) { t.Parallel() store := &testkit.InMemorySessionStore{} userDirectory := &testkit.InMemoryUserDirectory{} publisher := &testkit.RecordingProjectionPublisher{} if err := userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1")); err != nil { require.Failf(t, "test failed", "SeedExisting() returned error: %v", err) } service, err := New(store, userDirectory, publisher, testkit.FixedClock{Time: time.Unix(30, 0).UTC()}) require.NoError(t, err) result, err := service.Execute(context.Background(), Input{ UserID: "user-1", ReasonCode: "logout_all", ActorType: "system", }) require.NoError(t, err) assert.Equal(t, "no_active_sessions", result.Outcome) assert.EqualValues(t, 0, result.AffectedSessionCount) require.NotNil(t, result.AffectedDeviceSessionIDs) assert.Empty(t, result.AffectedDeviceSessionIDs) assert.Empty(t, publisher.PublishedSnapshots()) } func TestExecuteReturnsSubjectNotFoundForUnknownUser(t *testing.T) { t.Parallel() service, err := New(&testkit.InMemorySessionStore{}, &testkit.InMemoryUserDirectory{}, &testkit.RecordingProjectionPublisher{}, testkit.FixedClock{Time: time.Unix(30, 0).UTC()}) if err != nil { require.Failf(t, "test failed", "New() returned error: %v", err) } _, err = service.Execute(context.Background(), Input{ UserID: "missing", ReasonCode: "logout_all", ActorType: "system", }) assert.Equal(t, shared.ErrorCodeSubjectNotFound, shared.CodeOf(err)) } func TestExecuteReturnsServiceUnavailableWhenPublishFails(t *testing.T) { t.Parallel() store := &testkit.InMemorySessionStore{} userDirectory := &testkit.InMemoryUserDirectory{} publisher := &testkit.RecordingProjectionPublisher{Err: errors.New("publish failed")} if err := userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1")); err != nil { require.Failf(t, "test failed", "SeedExisting() returned error: %v", err) } if err := store.Create(context.Background(), activeSessionFixture("device-session-1", "user-1", time.Unix(10, 0).UTC())); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service, err := New(store, userDirectory, publisher, testkit.FixedClock{Time: time.Unix(30, 0).UTC()}) require.NoError(t, err) _, err = service.Execute(context.Background(), Input{ UserID: "user-1", ReasonCode: "logout_all", ActorType: "system", }) assert.Equal(t, shared.ErrorCodeServiceUnavailable, shared.CodeOf(err)) stored, getErr := store.Get(context.Background(), common.DeviceSessionID("device-session-1")) require.NoError(t, getErr) require.NotNil(t, stored.Revocation) assert.Equal(t, devicesession.StatusRevoked, stored.Status) assert.Equal(t, devicesession.RevokeReasonLogoutAll, stored.Revocation.ReasonCode) assert.Equal(t, common.RevokeActorType("system"), stored.Revocation.ActorType) } func activeSessionFixture(deviceSessionID string, userID string, createdAt time.Time) devicesession.Session { key, err := common.NewClientPublicKey(make([]byte, 32)) if err != nil { panic(err) } return devicesession.Session{ ID: common.DeviceSessionID(deviceSessionID), UserID: common.UserID(userID), ClientPublicKey: key, Status: devicesession.StatusActive, CreatedAt: createdAt, } }