Skip to content

Commit ca13a85

Browse files
bokelleyclaude
andcommitted
feat: add pytest markers, coverage config, security scanning, and integration tests
High-priority code hygiene improvements: 1. **Pytest markers**: Added unit/integration/slow markers - Integration tests excluded by default for fast local feedback - Run with: pytest -m integration to include them 2. **Coverage configuration**: - Set 80% minimum threshold (current: 86%) - Exclude generated code and test files - Branch coverage enabled - HTML reports to htmlcov/ 3. **Bandit security scanning**: - Added to pre-commit hooks - Configured to skip tests and scripts - Allows assert statements (not using -O optimization) 4. **Integration tests converted to pytest**: - tests/integration/test_creative_agent.py now uses pytest - Tests creative.adcontextprotocol.org reference agent - Includes error handling test These changes ensure: - Code quality checks run automatically before commits - Test coverage doesn't drop below acceptable threshold - Security vulnerabilities are caught early - Integration tests are properly organized 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 734c16d commit ca13a85

File tree

3 files changed

+78
-39
lines changed

3 files changed

+78
-39
lines changed

.pre-commit-config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ repos:
3030
pass_filenames: false
3131
args: [src/adcp]
3232

33+
# Security scanning with bandit
34+
- repo: https://github.com/PyCQA/bandit
35+
rev: 1.7.10
36+
hooks:
37+
- id: bandit
38+
args: ["-c", "pyproject.toml"]
39+
additional_dependencies: ["bandit[toml]"]
40+
exclude: ^(tests/|scripts/)
41+
3342
# Basic file checks
3443
- repo: https://github.com/pre-commit/pre-commit-hooks
3544
rev: v5.0.0

pyproject.toml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,47 @@ ignore_errors = true
101101
[tool.pytest.ini_options]
102102
testpaths = ["tests"]
103103
asyncio_mode = "auto"
104+
markers = [
105+
"unit: Unit tests that don't require external services",
106+
"integration: Integration tests that hit real endpoints (may be slow/flaky)",
107+
"slow: Tests that take significant time to run",
108+
]
109+
# By default, skip integration tests for fast local development
110+
addopts = "-m 'not integration'"
111+
112+
[tool.coverage.run]
113+
source = ["src/adcp"]
114+
omit = [
115+
"*/tests/*",
116+
"*/test_*.py",
117+
"*/_generated.py",
118+
"*/generated_poc/*",
119+
]
120+
branch = true
121+
122+
[tool.coverage.report]
123+
precision = 2
124+
show_missing = true
125+
skip_covered = false
126+
exclude_lines = [
127+
"pragma: no cover",
128+
"def __repr__",
129+
"raise AssertionError",
130+
"raise NotImplementedError",
131+
"if __name__ == .__main__.:",
132+
"if TYPE_CHECKING:",
133+
"class .*\\bProtocol\\):",
134+
"@(abc\\.)?abstractmethod",
135+
]
136+
# Maintain current 86% coverage, fail if it drops below 80%
137+
fail_under = 80
138+
139+
[tool.coverage.html]
140+
directory = "htmlcov"
141+
142+
[tool.bandit]
143+
exclude_dirs = ["tests", "scripts"]
144+
skips = ["B101"] # Allow assert in code (we're not using -O optimization)
104145

105146
[dependency-groups]
106147
dev = [
Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,44 @@
11
"""Integration tests for the creative agent at creative.adcontextprotocol.org."""
22

3-
import asyncio
4-
import sys
5-
from pathlib import Path
6-
7-
# Add src to path for imports
8-
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
3+
import pytest
94

105
from adcp import ADCPClient
11-
from adcp.types import AgentConfig, Protocol
12-
6+
from adcp.types import AgentConfig, ListCreativeFormatsRequest, Protocol
137

14-
async def test_creative_agent():
15-
"""Test the reference creative agent."""
16-
print("\n" + "=" * 60)
17-
print("Testing Creative Agent (MCP)")
18-
print("=" * 60)
198

9+
@pytest.mark.integration
10+
@pytest.mark.asyncio
11+
async def test_creative_agent_list_formats():
12+
"""Test the reference creative agent can list formats."""
2013
config = AgentConfig(
2114
id="creative_agent",
2215
agent_uri="https://creative.adcontextprotocol.org",
2316
protocol=Protocol.MCP,
2417
)
2518

26-
client = ADCPClient(config)
27-
28-
# Test 1: List creative formats
29-
print("\n📋 Test 1: Listing creative formats...")
30-
try:
31-
result = await client.list_creative_formats()
32-
if result.success:
33-
print("✅ Success!")
34-
print(f"Status: {result.status}")
35-
print(f"Data: {result.data}")
36-
else:
37-
print(f"❌ Failed: {result.error}")
38-
except Exception as e:
39-
print(f"❌ Exception: {e}")
40-
41-
# Clean up
42-
if hasattr(client.adapter, "close"):
43-
await client.adapter.close()
19+
async with ADCPClient(config) as client:
20+
request = ListCreativeFormatsRequest()
21+
result = await client.list_creative_formats(request)
4422

23+
assert result.success, f"Failed to list formats: {result.error}"
24+
assert result.data is not None, "Expected data in response"
25+
assert hasattr(result.data, "formats"), "Expected formats in data"
26+
assert len(result.data.formats) > 0, "Expected at least one format"
4527

46-
async def main():
47-
"""Run all tests."""
48-
await test_creative_agent()
49-
print("\n" + "=" * 60)
50-
print("Integration tests completed")
51-
print("=" * 60 + "\n")
5228

29+
@pytest.mark.integration
30+
@pytest.mark.asyncio
31+
async def test_creative_agent_connection_error_handling():
32+
"""Test that connection errors are handled gracefully."""
33+
config = AgentConfig(
34+
id="invalid_agent",
35+
agent_uri="https://invalid.example.com/nonexistent",
36+
protocol=Protocol.MCP,
37+
timeout=2.0, # Short timeout for faster test
38+
)
5339

54-
if __name__ == "__main__":
55-
asyncio.run(main())
40+
# Should raise an exception for invalid endpoint
41+
with pytest.raises(Exception):
42+
async with ADCPClient(config) as client:
43+
request = ListCreativeFormatsRequest()
44+
await client.list_creative_formats(request)

0 commit comments

Comments
 (0)