Files
clubber/tests/integration/test_member_mutations.py
gurixandClaude da9286be73 test: Reorganize organization member tests into proper pytest structure
Fix test organization by moving ad-hoc test scripts into proper pytest tests
following project conventions. Added 4 new tests to integration and e2e layers
to provide comprehensive coverage for organization members.

Changes:
- tests/integration/test_member_mutations.py:
  * Add test_create_organization_member - Tests creating organizations with companyName
  * Add test_create_organization_with_contact_person - Tests organizations with contact info
  * Import MemberType from models and schemas

- tests/integration/test_member_queries.py:
  * Add test_query_mixed_member_types - Tests querying both individual and organization members
  * Import MemberType from models

- tests/e2e/test_graphql_api.py:
  * Add test_introspect_member_type_enum - Tests GraphQL introspection for MemberType enum
  * Verifies INDIVIDUAL and ORGANIZATION enum values

Deleted improper test files:
- test_organization_members.py (root) - Ad-hoc script using httpx directly
- test_mcp_changes.sh (root) - Shell script for MCP server testing

Test results:
- All 57 tests pass (up from 53)
- Organization members now tested at all layers: unit, integration, and e2e
- Tests follow pytest conventions with async patterns and shared fixtures
- Integrated with CI/CD pipeline (proper test/ directory structure)

The proper test structure ensures:
1. Unit tests validate business logic (validation layer)
2. Integration tests verify GraphQL resolvers and database persistence
3. E2E tests confirm full HTTP request/response flows
4. All tests use shared fixtures from conftest.py

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 16:28:05 +01:00

254 lines
9.6 KiB
Python

"""Integration tests for GraphQL mutation resolvers."""
import pytest
from sqlalchemy import select
from src.models.member import Member, MemberType
from src.resolvers.member import MemberNotFoundError, Mutation
from src.schemas.member import CreateMemberInput, MemberType as SchemaMemberType, UpdateMemberInput
from src.validation import ValidationError
class TestCreateMemberMutation:
"""Tests for createMember mutation."""
async def test_create_member_with_minimal_data(self, async_session, patched_session_maker):
"""Test creating a member with only firstName."""
mutation = Mutation()
input_data = CreateMemberInput(first_name="Bob")
result = await mutation.create_member(input=input_data)
assert result.id is not None
assert result.first_name == "Bob"
assert result.last_name is None
assert result.email is None
assert result.phone is None
# Verify in database
db_result = await async_session.execute(
select(Member).where(Member.id == result.id)
)
db_member = db_result.scalar_one()
assert db_member.first_name == "Bob"
async def test_create_member_with_complete_data(self, patched_session_maker):
"""Test creating a member with all fields populated."""
mutation = Mutation()
input_data = CreateMemberInput(
first_name="Alice",
last_name="Johnson",
street="456 Oak Ave",
apartment_number="2A",
zip="54321",
city="Portland",
country="USA",
email="alice@example.com",
phone="+14155559999",
)
result = await mutation.create_member(input=input_data)
assert result.first_name == "Alice"
assert result.last_name == "Johnson"
assert result.street == "456 Oak Ave"
assert result.apartment_number == "2A"
assert result.zip == "54321"
assert result.city == "Portland"
assert result.country == "USA"
assert result.email == "alice@example.com"
assert result.phone == "+14155559999"
async def test_create_member_with_invalid_email(self, patched_session_maker):
"""Test creating a member with invalid email raises ValidationError."""
mutation = Mutation()
input_data = CreateMemberInput(
first_name="Charlie",
email="not-an-email"
)
with pytest.raises(ValidationError, match="Invalid email format"):
await mutation.create_member(input=input_data)
async def test_create_member_with_invalid_phone(self, patched_session_maker):
"""Test creating a member with invalid phone raises ValidationError."""
mutation = Mutation()
input_data = CreateMemberInput(
first_name="David",
phone="abc123" # Invalid characters
)
with pytest.raises(ValidationError, match="Invalid phone format"):
await mutation.create_member(input=input_data)
async def test_create_member_with_empty_first_name(self, patched_session_maker):
"""Test creating a member with empty firstName raises ValidationError."""
mutation = Mutation()
input_data = CreateMemberInput(first_name="")
with pytest.raises(ValidationError, match="firstName is required for individual members"):
await mutation.create_member(input=input_data)
async def test_create_organization_member(self, async_session, patched_session_maker):
"""Test creating an organization member with companyName."""
mutation = Mutation()
input_data = CreateMemberInput(
member_type=SchemaMemberType.ORGANIZATION,
company_name="Tech Innovations Inc",
email="info@techinnovations.com"
)
result = await mutation.create_member(input=input_data)
assert result.id is not None
assert result.member_type == SchemaMemberType.ORGANIZATION
assert result.company_name == "Tech Innovations Inc"
assert result.first_name is None
# Verify in database
db_result = await async_session.execute(
select(Member).where(Member.id == result.id)
)
db_member = db_result.scalar_one()
assert db_member.member_type.value == "ORGANIZATION"
assert db_member.company_name == "Tech Innovations Inc"
async def test_create_organization_with_contact_person(self, async_session, patched_session_maker):
"""Test creating an organization with contact person."""
mutation = Mutation()
input_data = CreateMemberInput(
member_type=SchemaMemberType.ORGANIZATION,
company_name="Global Solutions Ltd",
first_name="Jane",
last_name="Doe",
email="contact@globalsolutions.com"
)
result = await mutation.create_member(input=input_data)
assert result.member_type == SchemaMemberType.ORGANIZATION
assert result.company_name == "Global Solutions Ltd"
assert result.first_name == "Jane"
assert result.last_name == "Doe"
# Verify in database
db_result = await async_session.execute(
select(Member).where(Member.id == result.id)
)
db_member = db_result.scalar_one()
assert db_member.member_type.value == "ORGANIZATION"
assert db_member.company_name == "Global Solutions Ltd"
assert db_member.first_name == "Jane"
class TestUpdateMemberMutation:
"""Tests for updateMember mutation."""
async def test_update_member_successfully(self, async_session, patched_session_maker):
"""Test updating a member's fields successfully."""
# Create a member
member = Member(first_name="Eve", email="old@example.com")
async_session.add(member)
await async_session.commit()
await async_session.refresh(member)
original_created_at = member.created_at
# Update the member
mutation = Mutation()
input_data = UpdateMemberInput(
id=member.id,
email="new@example.com",
phone="+14155551111"
)
result = await mutation.update_member(input=input_data)
assert result.id == member.id
assert result.first_name == "Eve" # Unchanged
assert result.email == "new@example.com" # Updated
assert result.phone == "+14155551111" # Updated
# Verify updated_at is set (can't reliably test it changed due to timing)
assert result.updated_at is not None
assert result.created_at == original_created_at
async def test_update_member_with_nonexistent_id(self, patched_session_maker):
"""Test updating a non-existent member raises MemberNotFoundError."""
mutation = Mutation()
input_data = UpdateMemberInput(id=999, email="test@example.com")
with pytest.raises(MemberNotFoundError, match="Member with ID 999 not found"):
await mutation.update_member(input=input_data)
async def test_update_member_partial_data(self, async_session, patched_session_maker):
"""Test updating only some fields leaves others unchanged."""
# Create a member with complete data
member = Member(
first_name="Frank",
last_name="Miller",
email="frank@example.com",
phone="+14155552222"
)
async_session.add(member)
await async_session.commit()
await async_session.refresh(member)
# Update only email
mutation = Mutation()
input_data = UpdateMemberInput(id=member.id, email="updated@example.com")
result = await mutation.update_member(input=input_data)
assert result.first_name == "Frank" # Unchanged
assert result.last_name == "Miller" # Unchanged
assert result.email == "updated@example.com" # Updated
assert result.phone == "+14155552222" # Unchanged
async def test_update_member_with_invalid_email(self, async_session, patched_session_maker):
"""Test updating with invalid email raises ValidationError."""
# Create a member
member = Member(first_name="Grace")
async_session.add(member)
await async_session.commit()
await async_session.refresh(member)
# Try to update with invalid email
mutation = Mutation()
input_data = UpdateMemberInput(id=member.id, email="invalid-email")
with pytest.raises(ValidationError, match="Invalid email format"):
await mutation.update_member(input=input_data)
class TestDeleteMemberMutation:
"""Tests for deleteMember mutation."""
async def test_delete_member_successfully(self, async_session, patched_session_maker):
"""Test deleting a member successfully."""
# Create a member
member = Member(first_name="Henry")
async_session.add(member)
await async_session.commit()
await async_session.refresh(member)
member_id = member.id
# Delete the member
mutation = Mutation()
result = await mutation.delete_member(id=member_id)
assert result is True
# Verify member is gone
db_result = await async_session.execute(
select(Member).where(Member.id == member_id)
)
db_member = db_result.scalar_one_or_none()
assert db_member is None
async def test_delete_member_with_nonexistent_id(self, patched_session_maker):
"""Test deleting a non-existent member raises MemberNotFoundError."""
mutation = Mutation()
with pytest.raises(MemberNotFoundError, match="Member with ID 999 not found"):
await mutation.delete_member(id=999)