@ranimontagna/agent-toolkit 0.1.7 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -14
- package/dist/src/args.js +10 -0
- package/dist/src/args.js.map +1 -1
- package/dist/src/menu.js +43 -2
- package/dist/src/menu.js.map +1 -1
- package/dist/src/skills.js +9 -2
- package/dist/src/skills.js.map +1 -1
- package/dist/src/state.d.ts +2 -0
- package/dist/src/state.js +7 -0
- package/dist/src/state.js.map +1 -1
- package/dist/src/status.js +3 -1
- package/dist/src/status.js.map +1 -1
- package/dist/src/ui.js +4 -1
- package/dist/src/ui.js.map +1 -1
- package/dist/src/usage.js +2 -0
- package/dist/src/usage.js.map +1 -1
- package/docs/assets/install-plan.svg +2 -2
- package/docs/assets/install-skill-packages.svg +14 -14
- package/package.json +1 -1
- package/skills/backend/database/postgres-patterns/LICENSE.ANTIGRAVITY +21 -0
- package/skills/backend/database/postgres-patterns/LICENSE.ECC +21 -0
- package/skills/backend/database/postgres-patterns/NOTICE.md +11 -0
- package/skills/backend/database/postgres-patterns/SKILL.md +180 -0
- package/skills/backend/kotlin/kotlin-patterns/LICENSE +21 -0
- package/skills/backend/kotlin/kotlin-patterns/NOTICE.md +7 -0
- package/skills/backend/kotlin/kotlin-patterns/SKILL.md +711 -0
- package/skills/backend/kotlin/kotlin-testing/LICENSE +21 -0
- package/skills/backend/kotlin/kotlin-testing/NOTICE.md +7 -0
- package/skills/backend/kotlin/kotlin-testing/SKILL.md +824 -0
- package/skills/backend/python/python-patterns/LICENSE +21 -0
- package/skills/backend/python/python-patterns/NOTICE.md +7 -0
- package/skills/backend/python/python-patterns/SKILL.md +424 -0
- package/skills/backend/python/python-testing/LICENSE +21 -0
- package/skills/backend/python/python-testing/NOTICE.md +7 -0
- package/skills/backend/python/python-testing/SKILL.md +494 -0
- package/skills/devops/docker-patterns/LICENSE.ANTIGRAVITY +21 -0
- package/skills/devops/docker-patterns/LICENSE.ECC +21 -0
- package/skills/devops/docker-patterns/NOTICE.md +11 -0
- package/skills/devops/docker-patterns/SKILL.md +397 -0
- package/skills/frontend/accessibility/LICENSE +21 -0
- package/skills/frontend/accessibility/NOTICE.md +10 -0
- package/skills/frontend/accessibility/SKILL.md +145 -0
- package/skills/frontend/design/ui-ux-pro-max/LICENSE +21 -0
- package/skills/frontend/design/ui-ux-pro-max/NOTICE.md +12 -0
- package/skills/frontend/design/ui-ux-pro-max/SKILL.md +672 -0
- package/skills/frontend/design/ui-ux-pro-max/data/_sync_all.py +414 -0
- package/skills/frontend/design/ui-ux-pro-max/data/app-interface.csv +31 -0
- package/skills/frontend/design/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/frontend/design/ui-ux-pro-max/data/colors.csv +162 -0
- package/skills/frontend/design/ui-ux-pro-max/data/design.csv +1776 -0
- package/skills/frontend/design/ui-ux-pro-max/data/draft.csv +1779 -0
- package/skills/frontend/design/ui-ux-pro-max/data/google-fonts.csv +1924 -0
- package/skills/frontend/design/ui-ux-pro-max/data/icons.csv +106 -0
- package/skills/frontend/design/ui-ux-pro-max/data/landing.csv +35 -0
- package/skills/frontend/design/ui-ux-pro-max/data/products.csv +162 -0
- package/skills/frontend/design/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/angular.csv +51 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/laravel.csv +51 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/threejs.csv +54 -0
- package/skills/frontend/design/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/frontend/design/ui-ux-pro-max/data/styles.csv +85 -0
- package/skills/frontend/design/ui-ux-pro-max/data/typography.csv +74 -0
- package/skills/frontend/design/ui-ux-pro-max/data/ui-reasoning.csv +162 -0
- package/skills/frontend/design/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/frontend/design/ui-ux-pro-max/scripts/core.py +262 -0
- package/skills/frontend/design/ui-ux-pro-max/scripts/design_system.py +1148 -0
- package/skills/frontend/design/ui-ux-pro-max/scripts/search.py +114 -0
- package/skills/general/code-reviewer/AGENTS.md +292 -0
- package/skills/general/code-reviewer/LICENSE +201 -0
- package/skills/general/code-reviewer/NOTICE.md +10 -0
- package/skills/general/code-reviewer/SKILL.md +132 -0
- package/skills/general/code-reviewer/rules/correctness-error-handling.md +279 -0
- package/skills/general/code-reviewer/rules/maintainability-naming.md +149 -0
- package/skills/general/code-reviewer/rules/maintainability-type-hints.md +93 -0
- package/skills/general/code-reviewer/rules/performance-n-plus-one.md +250 -0
- package/skills/general/code-reviewer/rules/security-sql-injection.md +142 -0
- package/skills/general/code-reviewer/rules/security-xss-prevention.md +225 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-testing
|
|
3
|
+
description: Use when writing or improving Python tests with pytest fixtures, parametrization, mocking, coverage, async tests, and maintainable test organization.
|
|
4
|
+
metadata:
|
|
5
|
+
origin: ECC
|
|
6
|
+
globs: ["**/*.py", "**/*.pyi"]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Python Testing
|
|
10
|
+
|
|
11
|
+
> This skill provides comprehensive Python testing patterns using pytest as the primary testing framework.
|
|
12
|
+
|
|
13
|
+
## Testing Framework
|
|
14
|
+
|
|
15
|
+
Use **pytest** as the testing framework for its powerful features and clean syntax.
|
|
16
|
+
|
|
17
|
+
### Basic Test Structure
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
def test_user_creation():
|
|
21
|
+
"""Test that a user can be created with valid data"""
|
|
22
|
+
user = User(name="Alice", email="alice@example.com")
|
|
23
|
+
|
|
24
|
+
assert user.name == "Alice"
|
|
25
|
+
assert user.email == "alice@example.com"
|
|
26
|
+
assert user.is_active is True
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Test Discovery
|
|
30
|
+
|
|
31
|
+
pytest automatically discovers tests following these conventions:
|
|
32
|
+
- Files: `test_*.py` or `*_test.py`
|
|
33
|
+
- Functions: `test_*`
|
|
34
|
+
- Classes: `Test*` (without `__init__`)
|
|
35
|
+
- Methods: `test_*`
|
|
36
|
+
|
|
37
|
+
## Fixtures
|
|
38
|
+
|
|
39
|
+
Fixtures provide reusable test setup and teardown:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import pytest
|
|
43
|
+
from sqlalchemy import create_engine
|
|
44
|
+
from sqlalchemy.orm import sessionmaker
|
|
45
|
+
|
|
46
|
+
@pytest.fixture
|
|
47
|
+
def db_session():
|
|
48
|
+
"""Provide a database session for tests"""
|
|
49
|
+
engine = create_engine("sqlite:///:memory:")
|
|
50
|
+
Session = sessionmaker(bind=engine)
|
|
51
|
+
session = Session()
|
|
52
|
+
|
|
53
|
+
# Setup
|
|
54
|
+
Base.metadata.create_all(engine)
|
|
55
|
+
|
|
56
|
+
yield session
|
|
57
|
+
|
|
58
|
+
# Teardown
|
|
59
|
+
session.close()
|
|
60
|
+
|
|
61
|
+
def test_user_repository(db_session):
|
|
62
|
+
"""Test using the db_session fixture"""
|
|
63
|
+
repo = UserRepository(db_session)
|
|
64
|
+
user = repo.create(name="Alice", email="alice@example.com")
|
|
65
|
+
|
|
66
|
+
assert user.id is not None
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Fixture Scopes
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
@pytest.fixture(scope="function") # Default: per test
|
|
73
|
+
def user():
|
|
74
|
+
return User(name="Alice")
|
|
75
|
+
|
|
76
|
+
@pytest.fixture(scope="class") # Per test class
|
|
77
|
+
def database():
|
|
78
|
+
db = Database()
|
|
79
|
+
db.connect()
|
|
80
|
+
yield db
|
|
81
|
+
db.disconnect()
|
|
82
|
+
|
|
83
|
+
@pytest.fixture(scope="module") # Per module
|
|
84
|
+
def app():
|
|
85
|
+
return create_app()
|
|
86
|
+
|
|
87
|
+
@pytest.fixture(scope="session") # Once per test session
|
|
88
|
+
def config():
|
|
89
|
+
return load_config()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Fixture Dependencies
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
@pytest.fixture
|
|
96
|
+
def database():
|
|
97
|
+
db = Database()
|
|
98
|
+
db.connect()
|
|
99
|
+
yield db
|
|
100
|
+
db.disconnect()
|
|
101
|
+
|
|
102
|
+
@pytest.fixture
|
|
103
|
+
def user_repository(database):
|
|
104
|
+
"""Fixture that depends on database fixture"""
|
|
105
|
+
return UserRepository(database)
|
|
106
|
+
|
|
107
|
+
def test_create_user(user_repository):
|
|
108
|
+
user = user_repository.create(name="Alice")
|
|
109
|
+
assert user.id is not None
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Parametrization
|
|
113
|
+
|
|
114
|
+
Test multiple inputs with `@pytest.mark.parametrize`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import pytest
|
|
118
|
+
|
|
119
|
+
@pytest.mark.parametrize("email,expected", [
|
|
120
|
+
("user@example.com", True),
|
|
121
|
+
("invalid-email", False),
|
|
122
|
+
("", False),
|
|
123
|
+
("user@", False),
|
|
124
|
+
("@example.com", False),
|
|
125
|
+
])
|
|
126
|
+
def test_email_validation(email, expected):
|
|
127
|
+
result = validate_email(email)
|
|
128
|
+
assert result == expected
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Multiple Parameters
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
@pytest.mark.parametrize("name,age,valid", [
|
|
135
|
+
("Alice", 25, True),
|
|
136
|
+
("Bob", 17, False),
|
|
137
|
+
("", 25, False),
|
|
138
|
+
("Charlie", -1, False),
|
|
139
|
+
])
|
|
140
|
+
def test_user_validation(name, age, valid):
|
|
141
|
+
result = validate_user(name, age)
|
|
142
|
+
assert result == valid
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Parametrize with IDs
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
@pytest.mark.parametrize("input,expected", [
|
|
149
|
+
("hello", "HELLO"),
|
|
150
|
+
("world", "WORLD"),
|
|
151
|
+
], ids=["lowercase", "another_lowercase"])
|
|
152
|
+
def test_uppercase(input, expected):
|
|
153
|
+
assert input.upper() == expected
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Test Markers
|
|
157
|
+
|
|
158
|
+
Use markers for test categorization and selective execution:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
import pytest
|
|
162
|
+
|
|
163
|
+
@pytest.mark.unit
|
|
164
|
+
def test_calculate_total():
|
|
165
|
+
"""Fast unit test"""
|
|
166
|
+
assert calculate_total([1, 2, 3]) == 6
|
|
167
|
+
|
|
168
|
+
@pytest.mark.integration
|
|
169
|
+
def test_database_connection():
|
|
170
|
+
"""Slower integration test"""
|
|
171
|
+
db = Database()
|
|
172
|
+
assert db.connect() is True
|
|
173
|
+
|
|
174
|
+
@pytest.mark.slow
|
|
175
|
+
def test_large_dataset():
|
|
176
|
+
"""Very slow test"""
|
|
177
|
+
process_million_records()
|
|
178
|
+
|
|
179
|
+
@pytest.mark.skip(reason="Not implemented yet")
|
|
180
|
+
def test_future_feature():
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
@pytest.mark.skipif(sys.version_info < (3, 10), reason="Requires Python 3.10+")
|
|
184
|
+
def test_new_syntax():
|
|
185
|
+
pass
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Run specific markers:**
|
|
189
|
+
```bash
|
|
190
|
+
pytest -m unit # Run only unit tests
|
|
191
|
+
pytest -m "not slow" # Skip slow tests
|
|
192
|
+
pytest -m "unit or integration" # Run unit OR integration
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Mocking
|
|
196
|
+
|
|
197
|
+
### Using unittest.mock
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from unittest.mock import Mock, patch, MagicMock
|
|
201
|
+
|
|
202
|
+
def test_user_service_with_mock():
|
|
203
|
+
"""Test with mock repository"""
|
|
204
|
+
mock_repo = Mock()
|
|
205
|
+
mock_repo.find_by_id.return_value = User(id="1", name="Alice")
|
|
206
|
+
|
|
207
|
+
service = UserService(mock_repo)
|
|
208
|
+
user = service.get_user("1")
|
|
209
|
+
|
|
210
|
+
assert user.name == "Alice"
|
|
211
|
+
mock_repo.find_by_id.assert_called_once_with("1")
|
|
212
|
+
|
|
213
|
+
@patch('myapp.services.EmailService')
|
|
214
|
+
def test_send_notification(mock_email_service):
|
|
215
|
+
"""Test with patched dependency"""
|
|
216
|
+
service = NotificationService()
|
|
217
|
+
service.send("user@example.com", "Hello")
|
|
218
|
+
|
|
219
|
+
mock_email_service.send.assert_called_once()
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### pytest-mock Plugin
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
def test_with_mocker(mocker):
|
|
226
|
+
"""Using pytest-mock plugin"""
|
|
227
|
+
mock_repo = mocker.Mock()
|
|
228
|
+
mock_repo.find_by_id.return_value = User(id="1", name="Alice")
|
|
229
|
+
|
|
230
|
+
service = UserService(mock_repo)
|
|
231
|
+
user = service.get_user("1")
|
|
232
|
+
|
|
233
|
+
assert user.name == "Alice"
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Coverage Analysis
|
|
237
|
+
|
|
238
|
+
### Basic Coverage
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
pytest --cov=src --cov-report=term-missing
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### HTML Coverage Report
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
pytest --cov=src --cov-report=html
|
|
248
|
+
open htmlcov/index.html
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Coverage Configuration
|
|
252
|
+
|
|
253
|
+
```ini
|
|
254
|
+
# pytest.ini or pyproject.toml
|
|
255
|
+
[tool.pytest.ini_options]
|
|
256
|
+
addopts = """
|
|
257
|
+
--cov=src
|
|
258
|
+
--cov-report=term-missing
|
|
259
|
+
--cov-report=html
|
|
260
|
+
--cov-fail-under=80
|
|
261
|
+
"""
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Branch Coverage
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
pytest --cov=src --cov-branch
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Async Testing
|
|
271
|
+
|
|
272
|
+
### Testing Async Functions
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
import pytest
|
|
276
|
+
|
|
277
|
+
@pytest.mark.asyncio
|
|
278
|
+
async def test_async_fetch_user():
|
|
279
|
+
"""Test async function"""
|
|
280
|
+
user = await fetch_user("1")
|
|
281
|
+
assert user.name == "Alice"
|
|
282
|
+
|
|
283
|
+
@pytest.fixture
|
|
284
|
+
async def async_client():
|
|
285
|
+
"""Async fixture"""
|
|
286
|
+
client = AsyncClient()
|
|
287
|
+
await client.connect()
|
|
288
|
+
yield client
|
|
289
|
+
await client.disconnect()
|
|
290
|
+
|
|
291
|
+
@pytest.mark.asyncio
|
|
292
|
+
async def test_with_async_fixture(async_client):
|
|
293
|
+
result = await async_client.get("/users/1")
|
|
294
|
+
assert result.status == 200
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Test Organization
|
|
298
|
+
|
|
299
|
+
### Directory Structure
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
tests/
|
|
303
|
+
├── unit/
|
|
304
|
+
│ ├── test_models.py
|
|
305
|
+
│ ├── test_services.py
|
|
306
|
+
│ └── test_utils.py
|
|
307
|
+
├── integration/
|
|
308
|
+
│ ├── test_database.py
|
|
309
|
+
│ └── test_api.py
|
|
310
|
+
├── conftest.py # Shared fixtures
|
|
311
|
+
└── pytest.ini # Configuration
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### conftest.py
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
# tests/conftest.py
|
|
318
|
+
import pytest
|
|
319
|
+
|
|
320
|
+
@pytest.fixture(scope="session")
|
|
321
|
+
def app():
|
|
322
|
+
"""Application fixture available to all tests"""
|
|
323
|
+
return create_app()
|
|
324
|
+
|
|
325
|
+
@pytest.fixture
|
|
326
|
+
def client(app):
|
|
327
|
+
"""Test client fixture"""
|
|
328
|
+
return app.test_client()
|
|
329
|
+
|
|
330
|
+
def pytest_configure(config):
|
|
331
|
+
"""Register custom markers"""
|
|
332
|
+
config.addinivalue_line("markers", "unit: Unit tests")
|
|
333
|
+
config.addinivalue_line("markers", "integration: Integration tests")
|
|
334
|
+
config.addinivalue_line("markers", "slow: Slow tests")
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Assertions
|
|
338
|
+
|
|
339
|
+
### Basic Assertions
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
def test_assertions():
|
|
343
|
+
assert value == expected
|
|
344
|
+
assert value != other
|
|
345
|
+
assert value > 0
|
|
346
|
+
assert value in collection
|
|
347
|
+
assert isinstance(value, str)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### pytest Assertions with Better Error Messages
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
def test_with_context():
|
|
354
|
+
"""pytest provides detailed assertion introspection"""
|
|
355
|
+
result = calculate_total([1, 2, 3])
|
|
356
|
+
expected = 6
|
|
357
|
+
|
|
358
|
+
# pytest shows: assert 5 == 6
|
|
359
|
+
assert result == expected
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Custom Assertion Messages
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
def test_with_message():
|
|
366
|
+
result = process_data(input_data)
|
|
367
|
+
assert result.is_valid, f"Expected valid result, got errors: {result.errors}"
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Approximate Comparisons
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
import pytest
|
|
374
|
+
|
|
375
|
+
def test_float_comparison():
|
|
376
|
+
result = 0.1 + 0.2
|
|
377
|
+
assert result == pytest.approx(0.3)
|
|
378
|
+
|
|
379
|
+
# With tolerance
|
|
380
|
+
assert result == pytest.approx(0.3, abs=1e-9)
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Exception Testing
|
|
384
|
+
|
|
385
|
+
```python
|
|
386
|
+
import pytest
|
|
387
|
+
|
|
388
|
+
def test_raises_exception():
|
|
389
|
+
"""Test that function raises expected exception"""
|
|
390
|
+
with pytest.raises(ValueError):
|
|
391
|
+
validate_age(-1)
|
|
392
|
+
|
|
393
|
+
def test_exception_message():
|
|
394
|
+
"""Test exception message"""
|
|
395
|
+
with pytest.raises(ValueError, match="Age must be positive"):
|
|
396
|
+
validate_age(-1)
|
|
397
|
+
|
|
398
|
+
def test_exception_details():
|
|
399
|
+
"""Capture and inspect exception"""
|
|
400
|
+
with pytest.raises(ValidationError) as exc_info:
|
|
401
|
+
validate_user(name="", age=-1)
|
|
402
|
+
|
|
403
|
+
assert "name" in exc_info.value.errors
|
|
404
|
+
assert "age" in exc_info.value.errors
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Test Helpers
|
|
408
|
+
|
|
409
|
+
```python
|
|
410
|
+
# tests/helpers.py
|
|
411
|
+
def assert_user_equal(actual, expected):
|
|
412
|
+
"""Custom assertion helper"""
|
|
413
|
+
assert actual.id == expected.id
|
|
414
|
+
assert actual.name == expected.name
|
|
415
|
+
assert actual.email == expected.email
|
|
416
|
+
|
|
417
|
+
def create_test_user(**kwargs):
|
|
418
|
+
"""Test data factory"""
|
|
419
|
+
defaults = {
|
|
420
|
+
"name": "Test User",
|
|
421
|
+
"email": "test@example.com",
|
|
422
|
+
"age": 25,
|
|
423
|
+
}
|
|
424
|
+
defaults.update(kwargs)
|
|
425
|
+
return User(**defaults)
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## Property-Based Testing
|
|
429
|
+
|
|
430
|
+
Using `hypothesis` for property-based testing:
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
from hypothesis import given, strategies as st
|
|
434
|
+
|
|
435
|
+
@given(st.integers(), st.integers())
|
|
436
|
+
def test_addition_commutative(a, b):
|
|
437
|
+
"""Test that addition is commutative"""
|
|
438
|
+
assert a + b == b + a
|
|
439
|
+
|
|
440
|
+
@given(st.lists(st.integers()))
|
|
441
|
+
def test_sort_idempotent(lst):
|
|
442
|
+
"""Test that sorting twice gives same result"""
|
|
443
|
+
sorted_once = sorted(lst)
|
|
444
|
+
sorted_twice = sorted(sorted_once)
|
|
445
|
+
assert sorted_once == sorted_twice
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Best Practices
|
|
449
|
+
|
|
450
|
+
1. **One assertion per test** (when possible)
|
|
451
|
+
2. **Use descriptive test names** - describe what's being tested
|
|
452
|
+
3. **Arrange-Act-Assert pattern** - clear test structure
|
|
453
|
+
4. **Use fixtures for setup** - avoid duplication
|
|
454
|
+
5. **Mock external dependencies** - keep tests fast and isolated
|
|
455
|
+
6. **Test edge cases** - empty inputs, None, boundaries
|
|
456
|
+
7. **Use parametrize** - test multiple scenarios efficiently
|
|
457
|
+
8. **Keep tests independent** - no shared state between tests
|
|
458
|
+
|
|
459
|
+
## Running Tests
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
# Run all tests
|
|
463
|
+
pytest
|
|
464
|
+
|
|
465
|
+
# Run specific file
|
|
466
|
+
pytest tests/test_user.py
|
|
467
|
+
|
|
468
|
+
# Run specific test
|
|
469
|
+
pytest tests/test_user.py::test_create_user
|
|
470
|
+
|
|
471
|
+
# Run with verbose output
|
|
472
|
+
pytest -v
|
|
473
|
+
|
|
474
|
+
# Run with output capture disabled
|
|
475
|
+
pytest -s
|
|
476
|
+
|
|
477
|
+
# Run in parallel (requires pytest-xdist)
|
|
478
|
+
pytest -n auto
|
|
479
|
+
|
|
480
|
+
# Run only failed tests from last run
|
|
481
|
+
pytest --lf
|
|
482
|
+
|
|
483
|
+
# Run failed tests first
|
|
484
|
+
pytest --ff
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## When to Use This Skill
|
|
488
|
+
|
|
489
|
+
- Writing new Python tests
|
|
490
|
+
- Improving test coverage
|
|
491
|
+
- Setting up pytest infrastructure
|
|
492
|
+
- Debugging flaky tests
|
|
493
|
+
- Implementing integration tests
|
|
494
|
+
- Testing async Python code
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Antigravity User
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Affaan Mustafa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Third-party notice
|
|
2
|
+
|
|
3
|
+
This skill is adapted from two MIT-licensed community skills:
|
|
4
|
+
|
|
5
|
+
- ECC docker-patterns: https://github.com/affaan-m/ECC/tree/main/.kiro/skills/docker-patterns
|
|
6
|
+
- ECC source commit: 0f84c0e2796703fbda87d577b2636351418c7442
|
|
7
|
+
- Antigravity docker-expert: https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/docker-expert
|
|
8
|
+
- Antigravity source commit: b806b97a9a48063f7bdbee5611caf40edd17e305
|
|
9
|
+
|
|
10
|
+
The upstream MIT license texts are included in `LICENSE.ECC` and
|
|
11
|
+
`LICENSE.ANTIGRAVITY`.
|