chore: archive add-pytest-tests change

Archive the completed pytest test suite implementation change. The change has been moved to the archive and the project-setup spec has been updated with all test requirements and scenarios.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-21 20:32:09 +01:00
co-authored by Claude
parent 4c9f2eb462
commit 48d84bdc0a
4 changed files with 292 additions and 0 deletions
+292
View File
@@ -190,3 +190,295 @@ The project MUST provide executable scripts for database seeding and common deve
**And** script is idempotent (safe to run multiple times)
**And** success message is displayed
### Requirement: Automated Test Suite with Pytest
The project SHALL provide a comprehensive test suite using pytest to verify all features and prevent regressions.
#### Scenario: Run test suite successfully
- **GIVEN** the project is set up with dependencies installed
- **WHEN** the developer runs `uv run pytest`
- **THEN** all tests SHALL execute
- **AND** test results SHALL be displayed with pass/fail status
- **AND** the command SHALL exit with code 0 if all tests pass
#### Scenario: Run tests with verbose output
- **GIVEN** the project has tests implemented
- **WHEN** the developer runs `uv run pytest --verbose`
- **THEN** detailed test output SHALL be displayed
- **AND** each test name and result SHALL be shown
#### Scenario: Run specific test categories
- **GIVEN** tests are organized in subdirectories
- **WHEN** the developer runs `uv run pytest tests/unit`
- **THEN** only unit tests SHALL execute
- **WHEN** the developer runs `uv run pytest tests/integration`
- **THEN** only integration tests SHALL execute
### Requirement: Test Organization Structure
Tests SHALL be organized in a clear directory structure separating different test types.
#### Scenario: Test directory structure exists
- **GIVEN** the project repository
- **WHEN** inspecting the directory structure
- **THEN** a `tests/` directory SHALL exist at the project root
- **AND** subdirectories `tests/unit/`, `tests/integration/`, `tests/e2e/`, and `tests/mcp/` SHALL exist
- **AND** a `tests/conftest.py` file SHALL exist for shared fixtures
#### Scenario: Test files follow naming convention
- **GIVEN** test files in the `tests/` directory
- **WHEN** examining file names
- **THEN** all test files SHALL be named `test_*.py`
- **AND** test file names SHALL correspond to the modules they test (e.g., `test_validation.py` for `validation.py`)
### Requirement: Shared Test Fixtures
The test suite SHALL provide reusable fixtures for common test setup via conftest.py.
#### Scenario: Database fixture provides in-memory SQLite
- **GIVEN** tests need database access
- **WHEN** a test function requests the `db` fixture
- **THEN** an in-memory SQLite database SHALL be created
- **AND** the database SHALL have all tables created via Alembic migrations or direct schema creation
- **AND** the database SHALL be cleaned up after the test completes
#### Scenario: Async session fixture provides database sessions
- **GIVEN** tests need to interact with the database
- **WHEN** a test function requests the `async_session` fixture
- **THEN** an async SQLAlchemy session SHALL be provided
- **AND** the session SHALL be connected to the test database
- **AND** the session SHALL be closed after the test completes
#### Scenario: GraphQL client fixture provides test client
- **GIVEN** tests need to make GraphQL requests
- **WHEN** a test function requests the `graphql_client` fixture
- **THEN** a FastAPI test client SHALL be provided
- **AND** the client SHALL be configured to use the test database
- **AND** the client SHALL support async GraphQL queries and mutations
### Requirement: Unit Tests for Validation Logic
Unit tests SHALL verify validation functions work correctly with valid and invalid inputs.
#### Scenario: Test firstName validation with valid input
- **GIVEN** a valid firstName "Alice"
- **WHEN** `validate_first_name("Alice")` is called
- **THEN** no exception SHALL be raised
#### Scenario: Test firstName validation with empty string
- **GIVEN** an empty firstName ""
- **WHEN** `validate_first_name("")` is called
- **THEN** a ValidationError SHALL be raised
- **AND** the error message SHALL indicate firstName cannot be empty
#### Scenario: Test email validation with valid email
- **GIVEN** a valid email "test@example.com"
- **WHEN** `validate_email("test@example.com")` is called
- **THEN** no exception SHALL be raised
#### Scenario: Test email validation with invalid email
- **GIVEN** an invalid email "not-an-email"
- **WHEN** `validate_email("not-an-email")` is called
- **THEN** a ValidationError SHALL be raised
- **AND** the error message SHALL indicate invalid email format
#### Scenario: Test email validation with None
- **GIVEN** email is None
- **WHEN** `validate_email(None)` is called
- **THEN** no exception SHALL be raised (None is allowed)
#### Scenario: Test phone validation with valid E.164 number
- **GIVEN** a valid phone "+41791234567"
- **WHEN** `validate_phone("+41791234567")` is called
- **THEN** no exception SHALL be raised
#### Scenario: Test phone validation with invalid number
- **GIVEN** an invalid phone "123"
- **WHEN** `validate_phone("123")` is called
- **THEN** a ValidationError SHALL be raised
#### Scenario: Test phone validation with None
- **GIVEN** phone is None
- **WHEN** `validate_phone(None)` is called
- **THEN** no exception SHALL be raised (None is allowed)
### Requirement: Integration Tests for Member Model
Integration tests SHALL verify database operations with the Member model.
#### Scenario: Create member with minimal data
- **GIVEN** a test database session
- **WHEN** a Member is created with only firstName="Alice"
- **THEN** the member SHALL be persisted to the database
- **AND** the member SHALL have an auto-generated ID
- **AND** created_at and updated_at timestamps SHALL be populated
- **AND** all optional fields SHALL be None
#### Scenario: Create member with complete data
- **GIVEN** a test database session
- **WHEN** a Member is created with all fields populated
- **THEN** the member SHALL be persisted with all data
- **AND** all fields SHALL match the provided values
#### Scenario: Member timestamps are set correctly
- **GIVEN** a newly created member
- **THEN** created_at SHALL be set to current time
- **AND** updated_at SHALL be set to current time
- **AND** created_at SHALL equal updated_at for new members
### Requirement: Integration Tests for GraphQL Queries
Integration tests SHALL verify GraphQL query resolvers return correct data.
#### Scenario: Query single member by ID
- **GIVEN** a member exists with ID 1
- **WHEN** the `member(id: 1)` query is executed
- **THEN** the member data SHALL be returned
- **AND** all requested fields SHALL match the database record
#### Scenario: Query member with non-existent ID
- **GIVEN** no member exists with ID 999
- **WHEN** the `member(id: 999)` query is executed
- **THEN** None SHALL be returned
- **AND** no error SHALL be raised
#### Scenario: List all members
- **GIVEN** multiple members exist in the database
- **WHEN** the `members` query is executed
- **THEN** an array of all members SHALL be returned
- **AND** members SHALL be sorted by last_name (nulls last), then first_name
#### Scenario: List members when database is empty
- **GIVEN** no members exist in the database
- **WHEN** the `members` query is executed
- **THEN** an empty array SHALL be returned
### Requirement: Integration Tests for GraphQL Mutations
Integration tests SHALL verify GraphQL mutation resolvers create, update, and delete members correctly.
#### Scenario: Create member with minimal data
- **GIVEN** CreateMemberInput with only firstName="Bob"
- **WHEN** the `createMember` mutation is executed
- **THEN** a new member SHALL be created in the database
- **AND** the member SHALL have firstName="Bob"
- **AND** all optional fields SHALL be None
- **AND** the mutation SHALL return the created member with ID
#### Scenario: Create member with complete data
- **GIVEN** CreateMemberInput with all fields populated
- **WHEN** the `createMember` mutation is executed
- **THEN** a new member SHALL be created with all data
- **AND** all fields SHALL match the input
#### Scenario: Create member with invalid email
- **GIVEN** CreateMemberInput with email="not-an-email"
- **WHEN** the `createMember` mutation is executed
- **THEN** a ValidationError SHALL be raised
- **AND** no database record SHALL be created
#### Scenario: Update member successfully
- **GIVEN** a member exists with ID 1
- **WHEN** the `updateMember` mutation is executed with id=1 and email="new@example.com"
- **THEN** the member's email SHALL be updated to "new@example.com"
- **AND** the updated_at timestamp SHALL be refreshed
- **AND** all other fields SHALL remain unchanged
#### Scenario: Update member with non-existent ID
- **GIVEN** no member exists with ID 999
- **WHEN** the `updateMember` mutation is executed with id=999
- **THEN** a MemberNotFoundError SHALL be raised
- **AND** the error message SHALL indicate member not found
#### Scenario: Delete member successfully
- **GIVEN** a member exists with ID 1
- **WHEN** the `deleteMember` mutation is executed with id=1
- **THEN** the member SHALL be removed from the database
- **AND** the mutation SHALL return true
- **AND** subsequent queries for that member SHALL return None
#### Scenario: Delete member with non-existent ID
- **GIVEN** no member exists with ID 999
- **WHEN** the `deleteMember` mutation is executed with id=999
- **THEN** a MemberNotFoundError SHALL be raised
### Requirement: E2E Tests for GraphQL API
End-to-end tests SHALL verify complete GraphQL request/response flows over HTTP.
#### Scenario: Execute GraphQL query via HTTP POST
- **GIVEN** the FastAPI application with GraphQL endpoint
- **WHEN** a POST request is sent to /graphql with a query
- **THEN** the query SHALL be executed
- **AND** a JSON response SHALL be returned with data or errors
#### Scenario: GraphQL introspection query
- **GIVEN** the GraphQL API is running
- **WHEN** an introspection query for __schema is executed
- **THEN** the complete schema SHALL be returned
- **AND** all types, queries, and mutations SHALL be included
#### Scenario: GraphQL type introspection
- **GIVEN** the GraphQL API is running
- **WHEN** an introspection query for __type(name: "Member") is executed
- **THEN** the Member type definition SHALL be returned
- **AND** all fields with their types SHALL be included
### Requirement: MCP Server Tests
Tests SHALL verify MCP server tool definitions and execution.
#### Scenario: MCP server initializes successfully
- **GIVEN** a valid CLUBBER_API_URL
- **WHEN** the MCP server is instantiated
- **THEN** the server SHALL initialize without errors
- **AND** the GraphQL client SHALL be configured
#### Scenario: List tools returns all 6 tools
- **GIVEN** an initialized MCP server
- **WHEN** `list_tools()` is called
- **THEN** 6 tools SHALL be returned
- **AND** tools SHALL include: list_members, get_member, create_member, update_member, get_graphql_schema, execute_graphql_query
#### Scenario: Execute list_members tool
- **GIVEN** members exist in the database
- **WHEN** the `list_members` tool is invoked
- **THEN** a formatted list of all members SHALL be returned
#### Scenario: Execute get_member tool
- **GIVEN** a member exists with ID 1
- **WHEN** the `get_member` tool is invoked with id=1
- **THEN** the member's data SHALL be returned as formatted text
#### Scenario: Execute create_member tool
- **GIVEN** valid member input data
- **WHEN** the `create_member` tool is invoked
- **THEN** a new member SHALL be created via the GraphQL API
- **AND** the created member data SHALL be returned
#### Scenario: Execute get_graphql_schema tool
- **GIVEN** the GraphQL API is accessible
- **WHEN** the `get_graphql_schema` tool is invoked
- **THEN** the complete schema SHALL be retrieved via introspection
- **AND** formatted schema information SHALL be returned
#### Scenario: Execute execute_graphql_query tool with simple query
- **GIVEN** a valid GraphQL query string
- **WHEN** the `execute_graphql_query` tool is invoked
- **THEN** the query SHALL be executed against the API
- **AND** formatted JSON results SHALL be returned
#### Scenario: Execute execute_graphql_query tool with variables
- **GIVEN** a GraphQL query with variables
- **WHEN** the `execute_graphql_query` tool is invoked with query and variables
- **THEN** the query SHALL be executed with variable substitution
- **AND** results SHALL be returned
### Requirement: Test Documentation in README
The README SHALL document how to run tests and understand test organization.
#### Scenario: README contains test running instructions
- **GIVEN** the README.md file
- **WHEN** reading the documentation
- **THEN** a "Running Tests" section SHALL exist
- **AND** the section SHALL document the `uv run pytest` command
- **AND** examples of running specific test categories SHALL be provided
#### Scenario: README documents test organization
- **GIVEN** the README.md file
- **WHEN** reading the documentation
- **THEN** a "Test Organization" section SHALL exist
- **AND** the section SHALL explain the tests/ directory structure
- **AND** the section SHALL describe unit, integration, e2e, and mcp test categories