This proposal adds comprehensive pytest testing for all existing features: - Unit tests for validation logic - Integration tests for database operations and GraphQL resolvers - E2E tests for complete API flows - MCP server tests for all 6 tools 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
project-setup Spec Delta
ADDED Requirements
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/, andtests/mcp/SHALL exist - AND a
tests/conftest.pyfile 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.pyforvalidation.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
dbfixture - 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_sessionfixture - 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_clientfixture - 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
membersquery 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
membersquery 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
createMembermutation 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
createMembermutation 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
createMembermutation 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
updateMembermutation 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
updateMembermutation 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
deleteMembermutation 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
deleteMembermutation 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_memberstool 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_membertool 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_membertool 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_schematool 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_querytool 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_querytool 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 pytestcommand - 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