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>
This commit is contained in:
2025-12-04 16:28:05 +01:00
co-authored by Claude
parent d32dc99c66
commit da9286be73
5 changed files with 119 additions and 213 deletions
-40
View File
@@ -1,40 +0,0 @@
#!/bin/bash
# Test script to verify MCP server organization member support
set -e
echo "Testing MCP Server Organization Member Support"
echo "=============================================="
echo ""
# Test 1: Create individual member
echo "Test 1: Creating individual member..."
INDIVIDUAL_RESULT=$(echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "create_member", "arguments": {"firstName": "Bob", "lastName": "Jones", "email": "bob@example.com"}}}' | node dist/mcp_server.js 2>/dev/null | tail -n 1)
echo "✓ Individual member created"
echo ""
# Test 2: Create organization member
echo "Test 2: Creating organization member..."
ORG_RESULT=$(echo '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "create_member", "arguments": {"memberType": "ORGANIZATION", "companyName": "Acme Corp", "email": "info@acme.com"}}}' | node dist/mcp_server.js 2>/dev/null | tail -n 1)
echo "✓ Organization member created"
echo ""
# Test 3: List members
echo "Test 3: Listing all members..."
LIST_RESULT=$(echo '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "list_members", "arguments": {}}}' | node dist/mcp_server.js 2>/dev/null | tail -n 1)
echo "✓ Members listed"
echo ""
# Test 4: Get GraphQL schema
echo "Test 4: Checking GraphQL schema for MemberType enum..."
SCHEMA_RESULT=$(echo '{"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "get_graphql_schema", "arguments": {}}}' | node dist/mcp_server.js 2>/dev/null | tail -n 1)
if echo "$SCHEMA_RESULT" | grep -q "MemberType"; then
echo "✓ MemberType enum found in schema"
else
echo "✗ MemberType enum NOT found in schema"
exit 1
fi
echo ""
echo "All tests passed!"
-170
View File
@@ -1,170 +0,0 @@
#!/usr/bin/env python3
"""Test script for organization member support via GraphQL API."""
import httpx
import json
API_URL = "http://127.0.0.1:8000/graphql"
def execute_query(query: str, variables: dict = None):
"""Execute a GraphQL query."""
payload = {"query": query}
if variables:
payload["variables"] = variables
response = httpx.post(API_URL, json=payload)
response.raise_for_status()
result = response.json()
if "errors" in result:
print(f"GraphQL Errors: {result['errors']}")
raise Exception(f"GraphQL query failed: {result['errors']}")
return result["data"]
# Test 1: Create individual member
print("Test 1: Creating individual member...")
create_individual_query = """
mutation CreateMember($input: CreateMemberInput!) {
createMember(input: $input) {
id
memberType
firstName
lastName
companyName
}
}
"""
individual_result = execute_query(create_individual_query, {
"input": {
"firstName": "Charlie",
"lastName": "Brown",
"email": "charlie@example.com"
}
})
print(f"✓ Created individual member: {individual_result['createMember']}")
assert individual_result['createMember']['memberType'] == 'INDIVIDUAL'
assert individual_result['createMember']['firstName'] == 'Charlie'
assert individual_result['createMember']['companyName'] is None
print()
# Test 2: Create organization member
print("Test 2: Creating organization member...")
create_org_query = """
mutation CreateMember($input: CreateMemberInput!) {
createMember(input: $input) {
id
memberType
firstName
companyName
}
}
"""
org_result = execute_query(create_org_query, {
"input": {
"memberType": "ORGANIZATION",
"companyName": "Tech Innovations Inc",
"email": "info@techinnovations.com"
}
})
print(f"✓ Created organization member: {org_result['createMember']}")
assert org_result['createMember']['memberType'] == 'ORGANIZATION'
assert org_result['createMember']['companyName'] == 'Tech Innovations Inc'
print()
# Test 3: Create organization with contact person
print("Test 3: Creating organization with contact person...")
org_with_contact_result = execute_query(create_org_query, {
"input": {
"memberType": "ORGANIZATION",
"companyName": "Global Solutions Ltd",
"firstName": "Jane",
"lastName": "Doe",
"email": "contact@globalsolutions.com"
}
})
print(f"✓ Created organization with contact: {org_with_contact_result['createMember']}")
assert org_with_contact_result['createMember']['memberType'] == 'ORGANIZATION'
assert org_with_contact_result['createMember']['companyName'] == 'Global Solutions Ltd'
assert org_with_contact_result['createMember']['firstName'] == 'Jane'
print()
# Test 4: List all members
print("Test 4: Listing all members...")
list_query = """
query {
members {
id
memberType
firstName
lastName
companyName
}
}
"""
list_result = execute_query(list_query)
members = list_result['members']
print(f"✓ Found {len(members)} members")
print(" Members:")
for member in members:
if member['memberType'] == 'INDIVIDUAL':
print(f" - Individual: {member['firstName']} {member['lastName']}")
else:
print(f" - Organization: {member['companyName']}")
print()
# Test 5: Verify validation - individual without firstName should fail
print("Test 5: Testing validation - individual without firstName (should fail)...")
try:
execute_query(create_individual_query, {
"input": {
"memberType": "INDIVIDUAL",
"email": "nofirstname@example.com"
}
})
print("✗ Should have failed but didn't!")
exit(1)
except Exception as e:
print(f"✓ Correctly rejected: {str(e)[:100]}")
print()
# Test 6: Verify validation - organization without companyName should fail
print("Test 6: Testing validation - organization without companyName (should fail)...")
try:
execute_query(create_org_query, {
"input": {
"memberType": "ORGANIZATION",
"email": "nocompany@example.com"
}
})
print("✗ Should have failed but didn't!")
exit(1)
except Exception as e:
print(f"✓ Correctly rejected: {str(e)[:100]}")
print()
# Test 7: Check introspection for MemberType enum
print("Test 7: Verifying MemberType enum in schema...")
introspection_query = """
query {
__type(name: "MemberType") {
name
kind
enumValues {
name
}
}
}
"""
introspection_result = execute_query(introspection_query)
member_type_enum = introspection_result['__type']
print(f"✓ Found MemberType enum: {member_type_enum['name']}")
enum_values = [ev['name'] for ev in member_type_enum['enumValues']]
print(f" Enum values: {enum_values}")
assert 'INDIVIDUAL' in enum_values
assert 'ORGANIZATION' in enum_values
print()
print("=" * 50)
print("All tests passed! ✓")
print("=" * 50)
+28
View File
@@ -153,3 +153,31 @@ class TestGraphQLAPI:
# The error should mention validation
error_message = str(data["errors"])
assert "Invalid email format" in error_message or "email" in error_message.lower()
async def test_introspect_member_type_enum(self, graphql_client):
"""Test GraphQL introspection returns MemberType enum."""
query = """
query {
__type(name: "MemberType") {
name
kind
enumValues {
name
}
}
}
"""
response = await graphql_client.post("/graphql", json={"query": query})
assert response.status_code == 200
data = response.json()
assert "data" in data
member_type = data["data"]["__type"]
assert member_type["name"] == "MemberType"
assert member_type["kind"] == "ENUM"
enum_values = {ev["name"] for ev in member_type["enumValues"]}
assert "INDIVIDUAL" in enum_values
assert "ORGANIZATION" in enum_values
+53 -2
View File
@@ -3,9 +3,9 @@
import pytest
from sqlalchemy import select
from src.models.member import Member
from src.models.member import Member, MemberType
from src.resolvers.member import MemberNotFoundError, Mutation
from src.schemas.member import CreateMemberInput, UpdateMemberInput
from src.schemas.member import CreateMemberInput, MemberType as SchemaMemberType, UpdateMemberInput
from src.validation import ValidationError
@@ -89,6 +89,57 @@ class TestCreateMemberMutation:
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."""
+38 -1
View File
@@ -3,7 +3,7 @@
import pytest
from sqlalchemy import select
from src.models.member import Member
from src.models.member import Member, MemberType
from src.resolvers.member import Query
@@ -92,3 +92,40 @@ class TestMemberQueries:
assert results[2].last_name is None
assert results[3].first_name == "Bob"
assert results[3].last_name is None
async def test_query_mixed_member_types(self, async_session, patched_session_maker):
"""Test querying returns both individual and organization members."""
# Create individual member
individual = Member(
member_type=MemberType.INDIVIDUAL,
first_name="Alice",
last_name="Smith"
)
async_session.add(individual)
# Create organization member
organization = Member(
member_type=MemberType.ORGANIZATION,
company_name="Acme Corp",
email="info@acme.com"
)
async_session.add(organization)
await async_session.commit()
# Query all members
query = Query()
result = await query.members()
assert len(result) == 2
types = {m.member_type for m in result}
assert MemberType.INDIVIDUAL in types
assert MemberType.ORGANIZATION in types
# Verify individual member data
individual_result = next(m for m in result if m.member_type == MemberType.INDIVIDUAL)
assert individual_result.first_name == "Alice"
assert individual_result.last_name == "Smith"
# Verify organization member data
org_result = next(m for m in result if m.member_type == MemberType.ORGANIZATION)
assert org_result.company_name == "Acme Corp"