Fix Cursor driver inactive account detection
Cursor's Admin API exposes two independent removal signals: the isRemoved boolean and a role value of "removed". They are not always consistent — a member can carry role "removed" while isRemoved is still false, a known gap documented on the Cursor community forum. Previously Active was derived from isRemoved alone, so a member with role "removed" but isRemoved=false was incorrectly reported as active. Now either signal is sufficient to mark the account inactive. Add a cassette entry and test case covering the inconsistent state (role "removed", isRemoved false) to prevent regression. Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
@@ -86,10 +86,12 @@ func (d *CursorDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
||||
continue
|
||||
}
|
||||
|
||||
// isRemoved is Cursor's only account-status signal, so Active is
|
||||
// Cursor exposes two removal signals that are not always
|
||||
// consistent: the isRemoved soft-delete flag and a role of
|
||||
// "removed". Either one marks the member inactive, so Active is
|
||||
// always populated (never nil): a removed member is reported
|
||||
// inactive rather than dropped, per the AccountRecord contract.
|
||||
active := !m.IsRemoved
|
||||
active := !m.IsRemoved && m.Role != "removed"
|
||||
|
||||
records = append(records, AccountRecord{
|
||||
Email: m.Email,
|
||||
@@ -98,8 +100,8 @@ func (d *CursorDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
||||
Active: &active,
|
||||
IsAdmin: cursorIsAdmin(m.Role),
|
||||
MFAStatus: coredata.MFAStatusUnknown,
|
||||
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
||||
AccountType: coredata.AccessEntryAccountTypeUser,
|
||||
AuthMethod: coredata.AccessReviewEntryAuthMethodUnknown,
|
||||
AccountType: coredata.AccessReviewEntryAccountTypeUser,
|
||||
ExternalID: m.ID,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestCursorDriver(t *testing.T) {
|
||||
driver := NewCursorDriver(client)
|
||||
records, err := driver.ListAccounts(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, records, 3)
|
||||
require.Len(t, records, 4)
|
||||
|
||||
member := records[0]
|
||||
assert.Equal(t, "jane@example.com", member.Email)
|
||||
@@ -61,6 +61,15 @@ func TestCursorDriver(t *testing.T) {
|
||||
assert.False(t, removed.IsAdmin)
|
||||
require.NotNil(t, removed.Active)
|
||||
assert.False(t, *removed.Active)
|
||||
|
||||
// Cursor's two removal signals are not always consistent: a member can
|
||||
// carry role "removed" while isRemoved is still false. The role alone
|
||||
// must mark the account inactive.
|
||||
removedByRole := records[3]
|
||||
assert.Equal(t, "Removed", removedByRole.Role)
|
||||
assert.False(t, removedByRole.IsAdmin)
|
||||
require.NotNil(t, removedByRole.Active)
|
||||
assert.False(t, *removedByRole.Active)
|
||||
}
|
||||
|
||||
func TestCursorRole(t *testing.T) {
|
||||
|
||||
@@ -19,7 +19,7 @@ interactions:
|
||||
proto_minor: 0
|
||||
content_length: -1
|
||||
uncompressed: true
|
||||
body: '{"teamMembers":[{"id":"10000001","name":"Jane Doe","email":"jane@example.com","role":"member","isRemoved":false},{"id":"10000002","name":"Alex Martin","email":"alex@example.com","role":"owner","isRemoved":false},{"id":"10000003","name":"John Smith","email":"john@example.com","role":"removed","isRemoved":true}]}'
|
||||
body: '{"teamMembers":[{"id":"10000001","name":"Jane Doe","email":"jane@example.com","role":"member","isRemoved":false},{"id":"10000002","name":"Alex Martin","email":"alex@example.com","role":"owner","isRemoved":false},{"id":"10000003","name":"John Smith","email":"john@example.com","role":"removed","isRemoved":true},{"id":"10000004","name":"Mary Jones","email":"mary@example.com","role":"removed","isRemoved":false}]}'
|
||||
headers:
|
||||
Content-Type:
|
||||
- application/json
|
||||
|
||||
Reference in New Issue
Block a user