Files
freemo 08868e1e55 test: boost coverage for server lifecycle and team collaboration
Add Behave BDD scenarios covering:
- ServerLifecycle.start() with mocked uvicorn (full lifecycle)
- run_server() convenience function with Settings defaults and overrides
- Signal handler installation and non-main-thread edge case
- TeamCollaborationService validation edge cases (empty args)
- SessionRegistry validation edge cases and get_active_sessions
- VersionConflictError negative version validation
- Untracked resource version stamp creation path

These tests cover previously uncovered validation branches and the
server lifecycle start/shutdown paths to maintain >=97% coverage.
2026-05-29 05:08:08 -04:00

473 lines
22 KiB
Gherkin

Feature: Team collaboration — multi-user sessions, RBAC, and conflict resolution
As a CleverAgents team lead
I want team collaboration features with role-based access control
So that multiple users can work concurrently on shared projects
# TeamRole enum
Scenario: TeamRole enum contains expected members
Given the TeamRole enum
Then the TeamRole enum should contain "owner"
And the TeamRole enum should contain "admin"
And the TeamRole enum should contain "member"
And the TeamRole enum should contain "viewer"
# ── TeamPermission enum ──────────────────────────────────────────
Scenario: TeamPermission enum contains expected members
Given the TeamPermission enum
Then the TeamPermission enum should contain "read"
And the TeamPermission enum should contain "write"
And the TeamPermission enum should contain "admin"
And the TeamPermission enum should contain "manage_members"
# ── ConflictResolutionStrategy enum ──────────────────────────────
Scenario: ConflictResolutionStrategy enum contains expected members
Given the ConflictResolutionStrategy enum
Then the ConflictResolutionStrategy enum should contain "last_writer_wins"
And the ConflictResolutionStrategy enum should contain "reject"
And the ConflictResolutionStrategy enum should contain "merge"
# ── TEAM_ROLE_PERMISSIONS constant ───────────────────────────────
Scenario: OWNER role has all 4 permissions
Given the TEAM_ROLE_PERMISSIONS constant
Then the "owner" team role should have 4 allowed permissions
Scenario: ADMIN role has all 4 permissions
Given the TEAM_ROLE_PERMISSIONS constant
Then the "admin" team role should have 4 allowed permissions
Scenario: MEMBER role has read and write
Given the TEAM_ROLE_PERMISSIONS constant
Then the "member" team role should have 2 allowed permissions
Scenario: VIEWER role has only read
Given the TEAM_ROLE_PERMISSIONS constant
Then the "viewer" team role should have 1 allowed permissions
# ── TeamMember model ─────────────────────────────────────────────
Scenario: Create a valid TeamMember
Given a team member "alice" with role "owner" and email "alice@example.com"
Then the team member user_id should be "alice"
And the team member role should be "owner"
And the team member email should be "alice@example.com"
Scenario: TeamMember email validation rejects missing @
When I try to create a team member with email "invalid-email"
Then a team member validation error should be raised
Scenario: TeamMember has_permission returns True for allowed action
Given a team member "bob" with role "member" and email "bob@example.com"
Then the team member should have "read" permission
And the team member should have "write" permission
Scenario: TeamMember has_permission returns False for denied action
Given a team member "carol" with role "viewer" and email "carol@example.com"
Then the team member should not have "write" permission
And the team member should not have "admin" permission
# ── UserSession model ────────────────────────────────────────────
Scenario: Create a valid UserSession
Given a user session for user "alice" on project "proj-1"
Then the user session user_id should be "alice"
And the user session project_id should be "proj-1"
And the user session should be active
Scenario: Deactivate a UserSession
Given a user session for user "alice" on project "proj-1"
When the session is deactivated
Then the user session should not be active
Scenario: Touch updates last_active timestamp
Given a user session for user "alice" on project "proj-1"
When the session is touched
Then the last_active timestamp should be updated
# ── VersionStamp model ───────────────────────────────────────────
Scenario: Create a valid VersionStamp
Given a version stamp for resource "res-1" by user "alice"
Then the version stamp version should be 1
And the version stamp updated_by should be "alice"
Scenario: Increment a VersionStamp
Given a version stamp for resource "res-1" by user "alice"
When the version stamp is incremented by "bob"
Then the new version stamp version should be 2
And the new version stamp updated_by should be "bob"
Scenario: VersionStamp increment rejects empty user
Given a version stamp for resource "res-1" by user "alice"
Then incrementing with empty user should raise ValueError
# ── ConflictDetectionResult model ────────────────────────────────
Scenario: Create a no-conflict result
Given a conflict detection result with no conflict for "res-1"
Then the result detected should be False
And the result resource_id should be "res-1"
# ── VersionConflictError ─────────────────────────────────────────
Scenario: VersionConflictError carries version info
When a VersionConflictError is raised for "res-1" expected 1 actual 2
Then the error resource_id should be "res-1"
And the error expected_version should be 1
And the error actual_version should be 2
Scenario: VersionConflictError rejects empty resource_id
Then creating VersionConflictError with empty resource_id raises ValueError
# ── SessionRegistry ──────────────────────────────────────────────
Scenario: Register and retrieve a session
Given a session registry
And a user session "sess-1" for user "alice" on project "proj-1"
When the session is registered
Then the registry should contain the session "sess-1"
And the registry active count should be 1
Scenario: Unregister a session
Given a session registry
And a user session "sess-1" for user "alice" on project "proj-1"
When the session is registered
And the session "sess-1" is unregistered
Then the registry should not contain the session "sess-1"
And the registry active count should be 0
Scenario: Get sessions for a specific user
Given a session registry
And a user session "sess-1" for user "alice" on project "proj-1"
And a user session "sess-2" for user "alice" on project "proj-2"
And a user session "sess-3" for user "bob" on project "proj-1"
When all sessions are registered
Then the registry should have 2 sessions for user "alice"
And the registry should have 1 sessions for user "bob"
Scenario: Get sessions for a specific project
Given a session registry
And a user session "sess-1" for user "alice" on project "proj-1"
And a user session "sess-2" for user "bob" on project "proj-1"
And a user session "sess-3" for user "carol" on project "proj-2"
When all sessions are registered
Then the registry should have 2 sessions for project "proj-1"
And the registry should have 1 sessions for project "proj-2"
Scenario: Clear all sessions
Given a session registry
And a user session "sess-1" for user "alice" on project "proj-1"
And a user session "sess-2" for user "bob" on project "proj-2"
When all sessions are registered
And the registry is cleared
Then the registry active count should be 0
Scenario: Duplicate session registration raises ValueError
Given a session registry
And a user session "sess-1" for user "alice" on project "proj-1"
When the session is registered
Then registering the same session again raises ValueError
Scenario: Session registry generate_session_id returns valid ULID
Given a session registry
Then the generated session ID should be a valid ULID
# ── TeamCollaborationService — member management ─────────────────
Scenario: Add and retrieve a team member via service
Given a team collaboration service
When I add member "alice" with role "owner" and email "alice@example.com"
Then the service should have 1 members
And getting member "alice" should return the member
Scenario: Adding duplicate member raises TeamMemberExistsError
Given a team collaboration service
When I add member "alice" with role "owner" and email "alice@example.com"
Then adding member "alice" again raises TeamMemberExistsError
Scenario: Remove a team member via service
Given a team collaboration service
When I add member "alice" with role "owner" and email "alice@example.com"
And I remove member "alice"
Then the service should have 0 members
Scenario: Removing non-existent member raises TeamMemberNotFoundError
Given a team collaboration service
Then removing member "ghost" raises TeamMemberNotFoundError
Scenario: Update a member role
Given a team collaboration service
When I add member "alice" with role "member" and email "alice@example.com"
And I update member "alice" role to "admin"
Then member "alice" should have role "admin"
# ── TeamCollaborationService — permission checks ─────────────────
Scenario: Owner has all permissions
Given a team collaboration service
When I add member "alice" with role "owner" and email "alice@example.com"
Then member "alice" should have "read" team permission
And member "alice" should have "write" team permission
And member "alice" should have "admin" team permission
And member "alice" should have "manage_members" team permission
Scenario: Viewer has only read permission
Given a team collaboration service
When I add member "viewer-user" with role "viewer" and email "view@example.com"
Then member "viewer-user" should have "read" team permission
And member "viewer-user" should not have "write" team permission
Scenario: Enforce permission raises PermissionError for viewer writing
Given a team collaboration service
When I add member "viewer-user" with role "viewer" and email "view@example.com"
Then enforcing "write" on "viewer-user" raises PermissionError
# ── TeamCollaborationService — session management ────────────────
Scenario: Register a session for a team member
Given a team collaboration service
When I add member "alice" with role "owner" and email "alice@example.com"
And I register a session for "alice" on project "proj-1"
Then the project "proj-1" should have 1 active sessions
Scenario: Multiple users have concurrent sessions on same project
Given a team collaboration service
When I add member "alice" with role "owner" and email "alice@example.com"
And I add member "bob" with role "member" and email "bob@example.com"
And I register a session for "alice" on project "proj-1"
And I register a session for "bob" on project "proj-1"
Then the project "proj-1" should have 2 active sessions
Scenario: Unregister a session reduces active count
Given a team collaboration service
When I add member "alice" with role "owner" and email "alice@example.com"
And I register a session for "alice" on project "proj-1"
And I unregister the last session
Then the project "proj-1" should have 0 active sessions
Scenario: Removing a member cleans up their sessions
Given a team collaboration service
When I add member "alice" with role "owner" and email "alice@example.com"
And I register a session for "alice" on project "proj-1"
And I remove member "alice"
Then the project "proj-1" should have 0 active sessions
# ── TeamCollaborationService — conflict resolution ───────────────
Scenario: Create and retrieve a version stamp
Given a team collaboration service
When I create a version stamp for "res-1" by "alice"
Then the version stamp for "res-1" should have version 1
Scenario: Update version with matching expected version succeeds
Given a team collaboration service
When I create a version stamp for "res-1" by "alice"
And I update version for "res-1" expecting 1 by "bob"
Then the version stamp for "res-1" should have version 2
Scenario: Update version with mismatched version using reject strategy raises error
Given a team collaboration service
When I create a version stamp for "res-1" by "alice"
And I update version for "res-1" expecting 1 by "bob"
Then updating "res-1" expecting 1 by "carol" with reject raises VersionConflictError
Scenario: Update version with last-writer-wins resolves conflict
Given a team collaboration service
When I create a version stamp for "res-1" by "alice"
And I update version for "res-1" expecting 1 by "bob"
And I update "res-1" expecting 1 by "carol" with last_writer_wins
Then the conflict result should be detected and resolved
And the version stamp for "res-1" should have version 3
Scenario: Detect conflict without resolving
Given a team collaboration service
When I create a version stamp for "res-1" by "alice"
And I update version for "res-1" expecting 1 by "bob"
Then detecting conflict on "res-1" expecting 1 should return True
And detecting conflict on "res-1" expecting 2 should return False
Scenario: Update version with merge strategy resolves conflict
Given a team collaboration service
When I create a version stamp for "res-1" by "alice"
And I update version for "res-1" expecting 1 by "bob"
And I update "res-1" expecting 1 by "carol" with merge
Then the conflict result should be detected and resolved
# ── Error hierarchy ──────────────────────────────────────────────
Scenario: TeamMemberNotFoundError is a TeamCollaborationError
Then TeamMemberNotFoundError should be a subclass of TeamCollaborationError
Scenario: TeamMemberExistsError is a TeamCollaborationError
Then TeamMemberExistsError should be a subclass of TeamCollaborationError
Scenario: VersionConflictError is a TeamCollaborationError
Then VersionConflictError should be a subclass of TeamCollaborationError
# -----------------------------------------------------------------------
# Service edge case validations
# -----------------------------------------------------------------------
Scenario: Service session_registry property returns the registry
Given a fresh TeamCollaborationService
Then the service session_registry should be a SessionRegistry instance
Scenario: Service add_member rejects empty user_id
Given a fresh TeamCollaborationService
When I try to add a member with empty user_id via service
Then a collab ValueError should be raised mentioning "user_id"
Scenario: Service add_member rejects empty display_name
Given a fresh TeamCollaborationService
When I try to add a member with empty display_name via service
Then a collab ValueError should be raised mentioning "display_name"
Scenario: Service add_member rejects empty email
Given a fresh TeamCollaborationService
When I try to add a member with empty email via service
Then a collab ValueError should be raised mentioning "email"
Scenario: Service remove_member rejects empty user_id
Given a fresh TeamCollaborationService
When I try to remove a member with empty user_id via service
Then a collab ValueError should be raised mentioning "user_id"
Scenario: Service get_member rejects empty user_id
Given a fresh TeamCollaborationService
When I try to get a member with empty user_id via service
Then a collab ValueError should be raised mentioning "user_id"
Scenario: Service get_member for non-existent user raises error
Given a fresh TeamCollaborationService
When I try to get member "ghost" via service
Then a TeamMemberNotFoundError should be raised
Scenario: Service update_member_role rejects empty user_id
Given a fresh TeamCollaborationService
When I try to update role with empty user_id via service
Then a collab ValueError should be raised mentioning "user_id"
Scenario: Service register_session rejects empty user_id
Given a fresh TeamCollaborationService
When I try to register session with empty user_id via service
Then a collab ValueError should be raised mentioning "user_id"
Scenario: Service register_session rejects empty project_id
Given a fresh TeamCollaborationService
And a team member "u1" with role "member"
When I try to register session with empty project_id for user "u1"
Then a collab ValueError should be raised mentioning "project_id"
Scenario: Service unregister_session rejects empty session_id
Given a fresh TeamCollaborationService
When I try to unregister session with empty session_id
Then a collab ValueError should be raised mentioning "session_id"
Scenario: Service get_active_sessions_for_project rejects empty project_id
Given a fresh TeamCollaborationService
When I try to get sessions for empty project_id
Then a collab ValueError should be raised mentioning "project_id"
Scenario: Service create_version_stamp rejects empty resource_id
Given a fresh TeamCollaborationService
When I try to create version stamp with empty resource_id
Then a collab ValueError should be raised mentioning "resource_id"
Scenario: Service create_version_stamp rejects empty user_id
Given a fresh TeamCollaborationService
When I try to create version stamp with empty user_id
Then a collab ValueError should be raised mentioning "user_id"
Scenario: Service get_version_stamp rejects empty resource_id
Given a fresh TeamCollaborationService
When I try to get version stamp with empty resource_id
Then a collab ValueError should be raised mentioning "resource_id"
Scenario: Service update_version rejects empty resource_id
Given a fresh TeamCollaborationService
When I try to update version with empty resource_id
Then a collab ValueError should be raised mentioning "resource_id"
Scenario: Service update_version rejects empty user_id
Given a fresh TeamCollaborationService
When I try to update version with empty user_id
Then a collab ValueError should be raised mentioning "user_id"
Scenario: Service update_version rejects zero expected_version
Given a fresh TeamCollaborationService
When I try to update version with expected_version 0
Then a collab ValueError should be raised mentioning "expected_version"
Scenario: Service update_version creates stamp for untracked resource
Given a fresh TeamCollaborationService
When I update version for untracked resource "new-res" by user "u1" expecting version 1
Then the result should indicate no conflict detected
And the result details should contain "New resource created"
Scenario: Service detect_conflict rejects empty resource_id
Given a fresh TeamCollaborationService
When I try to detect conflict with empty resource_id
Then a collab ValueError should be raised mentioning "resource_id"
Scenario: Service detect_conflict rejects zero expected_version
Given a fresh TeamCollaborationService
When I try to detect conflict with expected_version 0
Then a collab ValueError should be raised mentioning "expected_version"
Scenario: Service detect_conflict returns False for untracked resource
Given a fresh TeamCollaborationService
When I detect conflict for untracked resource "phantom" expecting version 1
Then the conflict result should be False
# -----------------------------------------------------------------------
# Domain model edge case validations
# -----------------------------------------------------------------------
Scenario: VersionConflictError rejects negative expected_version
When I try to create VersionConflictError with expected_version -1
Then a collab ValueError should be raised mentioning "expected_version"
Scenario: VersionConflictError rejects negative actual_version
When I try to create VersionConflictError with actual_version -1
Then a collab ValueError should be raised mentioning "actual_version"
Scenario: SessionRegistry register rejects empty session_id
Given a fresh SessionRegistry
When I try to register a session with empty session_id
Then a collab ValueError should be raised mentioning "session_id"
Scenario: SessionRegistry unregister rejects empty session_id
Given a fresh SessionRegistry
When I try to unregister a session with empty session_id
Then a collab ValueError should be raised mentioning "session_id"
Scenario: SessionRegistry unregister returns False for unknown session
Given a fresh SessionRegistry
When I unregister session "nonexistent-id"
Then the unregister result should be False
Scenario: SessionRegistry get rejects empty session_id
Given a fresh SessionRegistry
When I try to get session with empty session_id
Then a collab ValueError should be raised mentioning "session_id"
Scenario: SessionRegistry list_active_sessions returns only active ones
Given a fresh SessionRegistry
And a registered session "s1" for user "u1" in project "p1"
And a registered session "s2" for user "u2" in project "p1"
When I deactivate session "s1" in the registry
Then get_active_sessions should return 1 session
Scenario: SessionRegistry get_sessions_for_user rejects empty user_id
Given a fresh SessionRegistry
When I try to get sessions for empty user_id
Then a collab ValueError should be raised mentioning "user_id"
Scenario: SessionRegistry get_sessions_for_project rejects empty project_id
Given a fresh SessionRegistry
When I try to get sessions for empty project_id via registry
Then a collab ValueError should be raised mentioning "project_id"