Cursor rule
.cursor/rules/20-hermes-api-tests.mdcCursor rules
Quality
97/100
Scores the file, not the repository.Length
570 words
23 headings · 14 code blocksRepository
45
— · pushed 2 days agoLast changed
3 days ago
First indexed 3 days ago.123456# Hermes API - Testing Rules78## Pytest Configuration910### Test Discovery11- Test files: `test_*.py`12- Test classes: `Test*`13- Test functions: `test_*`14- Location: `packages/hermes-api/tests/`1516### Running Tests17```bash18pytest # All tests19pytest -v # Verbose20pytest -m unit # Only unit tests21pytest -m "not slow" # Exclude slow tests22pytest tests/test_api/test_auth.py # Specific file23```2425## Test Organization2627```28tests/29├── conftest.py # Shared fixtures30├── test_api/ # API endpoint tests31├── test_integration/ # Integration tests32├── test_services/ # Service layer tests33└── test_tasks/ # Celery task tests34```3536### Test Markers37```python38@pytest.mark.unit39def test_simple_function():40 pass4142@pytest.mark.integration43async def test_full_workflow():44 pass4546@pytest.mark.slow47async def test_large_download():48 pass49```5051## Async Testing5253```python54import pytest55from httpx import AsyncClient5657@pytest.mark.asyncio58async def test_async_endpoint(client: AsyncClient):59 response = await client.get("/api/v1/health")60 assert response.status_code == 20061```6263## Fixtures6465### Common Fixtures (conftest.py)66```python67@pytest.fixture68async def client():69 async with AsyncClient(app=app, base_url="http://test") as ac:70 yield ac7172@pytest.fixture73async def auth_token(client: AsyncClient, test_user):74 response = await client.post(75 "/api/v1/auth/login",76 json={"username": test_user.username, "password": "password123"}77 )78 return response.json()["access_token"]7980@pytest.fixture81async def db_session():82 async for session in get_database_session():83 yield session84 await session.rollback()85```8687## API Testing8889### Testing Endpoints90```python91@pytest.mark.asyncio92async def test_get_endpoint(client: AsyncClient, auth_token: str):93 headers = {"Authorization": f"Bearer {auth_token}"}94 response = await client.get("/api/v1/downloads", headers=headers)9596 assert response.status_code == 20097 data = response.json()98 assert isinstance(data, list)99100@pytest.mark.asyncio101async def test_post_endpoint(client: AsyncClient, auth_token: str):102 headers = {"Authorization": f"Bearer {auth_token}"}103 payload = {"url": "https://example.com/video", "profile_id": "default"}104105 response = await client.post(106 "/api/v1/downloads",107 headers=headers,108 json=payload109 )110111 assert response.status_code == 201112 assert "id" in response.json()113```114115### Testing Auth116```python117@pytest.mark.asyncio118async def test_protected_without_auth(client: AsyncClient):119 response = await client.get("/api/v1/downloads")120 assert response.status_code == 401121122@pytest.mark.asyncio123async def test_protected_invalid_token(client: AsyncClient):124 headers = {"Authorization": "Bearer invalid"}125 response = await client.get("/api/v1/downloads", headers=headers)126 assert response.status_code == 401127```128129### Testing Errors130```python131@pytest.mark.asyncio132async def test_not_found(client: AsyncClient, auth_token: str):133 headers = {"Authorization": f"Bearer {auth_token}"}134 response = await client.get("/api/v1/downloads/nonexistent", headers=headers)135136 assert response.status_code == 404137 assert "not found" in response.json()["detail"].lower()138139@pytest.mark.asyncio140async def test_validation_error(client: AsyncClient, auth_token: str):141 headers = {"Authorization": f"Bearer {auth_token}"}142 response = await client.post("/api/v1/downloads", headers=headers, json={})143144 assert response.status_code == 422145```146147## Database Testing148149```python150@pytest.mark.asyncio151async def test_create_user(db_session: AsyncSession):152 repo = UserRepository(db_session)153154 user_data = {155 "username": "testuser",156 "email": "test@example.com",157 "password_hash": "hashed"158 }159160 user = await repo.create(user_data)161162 assert user.id is not None163 assert user.username == "testuser"164```165166## Mocking167168```python169@pytest.mark.asyncio170async def test_with_mock(mocker):171 mock_yt_dlp = mocker.patch("app.services.yt_dlp_service.YtDlpService.get_info")172 mock_yt_dlp.return_value = {"title": "Test Video", "duration": 120}173174 result = await some_function()175176 assert result["title"] == "Test Video"177 mock_yt_dlp.assert_called_once()178179@pytest.mark.asyncio180async def test_celery_task(client, auth_token, mocker):181 mock_task = mocker.patch("app.tasks.download_tasks.process_download.delay")182 mock_task.return_value.id = "task-123"183184 headers = {"Authorization": f"Bearer {auth_token}"}185 response = await client.post(186 "/api/v1/downloads",187 headers=headers,188 json={"url": "https://example.com"}189 )190191 assert response.status_code == 201192 mock_task.assert_called_once()193```194195## Test Best Practices196197### Arrange-Act-Assert198```python199def test_something():200 # Arrange201 user_data = {"username": "test"}202203 # Act204 result = create_user(user_data)205206 # Assert207 assert result.username == "test"208```209210### Test Names211```python212# ✅ Good213def test_create_download_success()214def test_get_download_not_found()215def test_delete_download_unauthorized()216217# ❌ Bad218def test_download()219def test_1()220```221222### Test Independence223- Each test should be independent224- Don't rely on execution order225- Clean up after tests226- Use fixtures for setup227228### Assertions229```python230assert response.status_code == 200, "Expected successful response"231assert "id" in data, "Response should include ID"232assert len(downloads) > 0, "Should return downloads"233```234235## Coverage236237```bash238pytest --cov=app --cov-report=html239open htmlcov/index.html240```241242- Aim for 80%+ coverage on critical code243- Focus on meaningful coverage244- Test edge cases and error conditions245
Also in TechSquidTV/Hermes
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| TechSquidTV/Hermes.cursor/rules/00-project.mdc · 45 | Cursor rules | setuplint-formatstylearch+4 | 89/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-app.mdc · 45 | Cursor rules | lint-formatstylearchtypes+5 | 88/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-api.mdc · 45 | Cursor rules | stylearchdependenciesapi+2 | 77/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-db.mdc · 45 | Cursor rules | teststylearchtesting-strategy+3 | 73/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-app-components.mdc · 45 | Cursor rules | archtypesuiperformance+1 | 65/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-app-hooks.mdc · 45 | Cursor rules | lint-formatstylearchtypes+3 | 73/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-app-routes.mdc · 45 | Cursor rules | archapiuido-not | 65/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/30-docker.mdc · 45 | Cursor rules | setupbuildstylesecurity+4 | 84/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/30-docs.mdc · 45 | Cursor rules | setuplint-formatstylearch+3 | 81/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/30-tests.mdc · 45 | Cursor rules | buildteststylearch+4 | 85/100 | 3 days ago |
Diff against .cursor/rules/00-project.mdc Diff against .cursor/rules/10-hermes-api.mdc Diff against .cursor/rules/10-hermes-app.mdc Diff against .cursor/rules/20-hermes-api-api.mdc Diff against .cursor/rules/20-hermes-api-db.mdc Diff against .cursor/rules/20-hermes-app-components.mdc Diff against .cursor/rules/20-hermes-app-hooks.mdc Diff against .cursor/rules/20-hermes-app-routes.mdc Diff against .cursor/rules/30-docker.mdc Diff against .cursor/rules/30-docs.mdc Diff against .cursor/rules/30-tests.mdc
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| skillrecordings/egghead-next.cursor/rules/gh-task-plan.mdc · 1.4k | Cursor rules | teststylearchtypes+2 | 96/100 | 3 days ago |
