@oleksandr.rudnychenko/sync_loop 0.2.1

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.
@@ -0,0 +1,258 @@
1
+ # Testing Guide (R2)
2
+
3
+ Comprehensive testing patterns for safe iteration and regression prevention.
4
+ Referenced from [../patterns.md](../patterns.md).
5
+
6
+ ---
7
+
8
+ ## Test Organization
9
+
10
+ ```
11
+ tests/
12
+ ├── conftest.py # Shared fixtures, global setup
13
+ ├── factories.py # Test data builders
14
+ ├── mocks.py # Mock implementations for external deps
15
+ ├── api/ # API/endpoint tests (HTTP client)
16
+ ├── integration/ # Multi-component workflow tests
17
+ └── unit/ # Pure logic tests (no I/O)
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Pattern 1: Fixture-Based Setup
23
+
24
+ ```python
25
+ # Environment isolation — reset global state each test
26
+ @pytest.fixture(autouse=True)
27
+ def reset_context():
28
+ """Reset global state before each test."""
29
+ import app.context
30
+ app.context._context = None
31
+ yield
32
+ app.context._context = None
33
+
34
+ # Test app configuration
35
+ @pytest.fixture
36
+ def test_app(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
37
+ """Configure app with test-safe environment."""
38
+ monkeypatch.setenv("APP_ENV", "testing")
39
+ monkeypatch.setenv("ENABLE_BACKGROUND_TASKS", "0")
40
+ yield create_app()
41
+
42
+ # API client for endpoint tests
43
+ @pytest.fixture
44
+ def client(test_app):
45
+ return TestClient(test_app)
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Pattern 2: Factory Pattern for Test Data
51
+
52
+ ```python
53
+ class EntityFactory:
54
+ @staticmethod
55
+ def create(
56
+ id: str | None = None,
57
+ name: str = "Test Entity",
58
+ entity_type: str = "default",
59
+ **extras: Any,
60
+ ) -> dict[str, Any]:
61
+ return {
62
+ "id": id or str(uuid4()),
63
+ "name": name,
64
+ "metadata": {"type": entity_type, **extras},
65
+ }
66
+
67
+ @staticmethod
68
+ def with_full_analysis() -> dict[str, Any]:
69
+ """Semantic constructor for entity with all analysis fields populated."""
70
+ return EntityFactory.create(
71
+ name="Fully Analyzed Entity",
72
+ entity_type="analyzed",
73
+ score=85.0,
74
+ grade="B",
75
+ )
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Pattern 3: Mock External Dependencies
81
+
82
+ ```python
83
+ class MockExternalService:
84
+ """Mock for any external service boundary (LLM, API, storage)."""
85
+ def __init__(self, response: dict | str | None = None):
86
+ self.response = response or self._default_response()
87
+ self.calls: list[dict] = []
88
+
89
+ async def __call__(self, prompt: str, context: str, **kwargs):
90
+ self.calls.append({"prompt": prompt, "context": context})
91
+ return json.dumps(self.response), {}
92
+
93
+ @property
94
+ def call_count(self) -> int:
95
+ return len(self.calls)
96
+ ```
97
+
98
+ Usage:
99
+ ```python
100
+ @pytest.fixture
101
+ def mock_service() -> MockExternalService:
102
+ return MockExternalService(response={"score": 85})
103
+
104
+ def test_analysis_delegates_to_service(mock_service):
105
+ service = AnalysisService(external=mock_service)
106
+ result = service.analyze(...)
107
+ assert mock_service.call_count == 1
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Pattern 4: Class-Based Test Organization
113
+
114
+ ```python
115
+ class TestParseResponse:
116
+ def test_valid_json_response(self):
117
+ response = '{"entity_type": "analyzed", "score": 85}'
118
+ result = parse_response(response)
119
+ assert result.entity_type == EntityType.ANALYZED
120
+
121
+ def test_invalid_json_fallback(self):
122
+ result = parse_response("Not JSON")
123
+ assert result.entity_type == EntityType.UNKNOWN
124
+
125
+ def test_missing_fields_use_defaults(self):
126
+ result = parse_response('{"entity_type": "analyzed"}')
127
+ assert result.score == 0.0
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Pattern 5: Parametrized Tests
133
+
134
+ ```python
135
+ @pytest.mark.parametrize("score,expected_grade", [
136
+ (95, "A"), (90, "A"),
137
+ (89.9, "B"), (85, "B"), (80, "B"),
138
+ (79, "C"), (70, "C"),
139
+ (69, "D"), (60, "D"),
140
+ (59, "F"), (0, "F"),
141
+ ])
142
+ def test_grade_thresholds(score: float, expected_grade: str):
143
+ assert compute_grade(score) == expected_grade
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Pattern 6: Three-Layer Test Strategy
149
+
150
+ ```python
151
+ # UNIT: Pure logic, no I/O
152
+ def test_parse_response():
153
+ result = parse_response('{"entity_type": "analyzed"}')
154
+ assert result.entity_type == EntityType.ANALYZED
155
+
156
+ # INTEGRATION: Multiple components, mocked external I/O
157
+ def test_analysis_pipeline(mock_service, mock_repository):
158
+ service = AnalysisService(external=mock_service, repo=mock_repository)
159
+ report = service.analyze(entities=[...])
160
+ assert report.aggregate_score > 0
161
+
162
+ # API: HTTP endpoint, full stack with test client
163
+ def test_analyze_endpoint(client, mock_data):
164
+ response = client.post("/api/v1/analyze", json=mock_data)
165
+ assert response.status_code == 200
166
+ assert "score" in response.json()
167
+ ```
168
+
169
+ ---
170
+
171
+ ## Pattern 7: Assertion Patterns
172
+
173
+ ```python
174
+ # ✅ Specific property checks
175
+ assert report.task_id == "task-1"
176
+ assert len(report.metrics) > 0
177
+ assert "total_items" in [m.name for m in report.metrics]
178
+
179
+ # ✅ Range/constraint checks
180
+ assert 0 <= result.score <= 100
181
+ assert result.entity_type in EntityType
182
+
183
+ # ✅ Collection checks
184
+ assert all(m.value >= 0 for m in report.metrics)
185
+ assert any(r.entity_type == "analyzed" for r in results)
186
+
187
+ # ❌ Vague — what is being verified?
188
+ assert report
189
+ assert result is not None
190
+ ```
191
+
192
+ ---
193
+
194
+ ## Pattern 8: Naming Convention
195
+
196
+ Format: `test_<action>_<condition>_<outcome>`
197
+
198
+ ```
199
+ test_returns_report # Basic happy path
200
+ test_metrics_computed_correctly # Specific behavior
201
+ test_unknown_type_falls_back_to_default # Edge case
202
+ test_invalid_json_fallback # Error handling
203
+ test_missing_fields_use_defaults # Default behavior
204
+ test_empty_input_returns_empty_report # Boundary condition
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Test Metrics (Target)
210
+
211
+ | Metric | Target |
212
+ |--------|--------|
213
+ | Pass Rate | 100% |
214
+ | Unit proportion | ≥ 70% |
215
+ | Integration proportion | ≤ 20% |
216
+ | API proportion | ≤ 10% |
217
+
218
+ ---
219
+
220
+ ## Test Heuristics
221
+
222
+ | Heuristic | Guideline |
223
+ |-----------|-----------|
224
+ | **Speed** | Unit < 10ms, Integration < 100ms, API < 1s |
225
+ | **Isolation** | Each test independent, no shared mutable state |
226
+ | **Coverage** | Critical paths and edge cases tested, not chasing 100% line coverage |
227
+ | **Clarity** | Test name explains what behavior is being verified |
228
+ | **Simplicity** | One logical assertion per test (multiple `assert` OK if testing one concept) |
229
+ | **Structure** | Arrange → Act → Assert (AAA pattern) |
230
+ | **Determinism** | No flaky tests — mock time, randomness, external systems |
231
+
232
+ ---
233
+
234
+ ## Validation Strategy
235
+
236
+ 1. **Changed symbols first** — run tests for files you changed
237
+ 2. **Adjacent modules second** — run tests for callers/consumers
238
+ 3. **Full suite last** — once local confidence is high, run everything
239
+
240
+ ```bash
241
+ # Targeted
242
+ pytest tests/unit/test_processor.py -x -v
243
+
244
+ # Adjacent
245
+ pytest tests/unit/ tests/integration/ -x -v -k "processor or analysis"
246
+
247
+ # Full suite
248
+ pytest tests/ -x -v
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Related Documents
254
+
255
+ | Document | Purpose |
256
+ |----------|---------|
257
+ | [../validate-env.md](../validate-env.md) | Gate orchestration (test gate details) |
258
+ | [refactoring-workflow.md](refactoring-workflow.md) | Safe code movement (tests after each move) |
@@ -0,0 +1,256 @@
1
+ # Pattern Registry & Routing Index
2
+
3
+ Central pattern index for GKP routing and LEARN persistence.
4
+ Referenced from [reasoning-kernel.md](reasoning-kernel.md).
5
+
6
+ ---
7
+
8
+ ## Pattern Families
9
+
10
+ | IDs | Family | Primary Spec |
11
+ |-----|--------|--------------|
12
+ | **P1–P11** | Core code architecture patterns | [patterns/code-patterns.md](patterns/code-patterns.md) |
13
+ | **R1–R3** | Refactoring, testing, and API quality patterns | [patterns/refactoring-workflow.md](patterns/refactoring-workflow.md), [patterns/testing-guide.md](patterns/testing-guide.md), [patterns/api-standards.md](patterns/api-standards.md) |
14
+
15
+ ---
16
+
17
+ ## Architecture Baseline
18
+
19
+ ```
20
+ Routes → Services → Repositories → Libs
21
+ ```
22
+
23
+ {Replace with actual project layers during bootstrap.}
24
+
25
+ ### Layer Intent
26
+ - **Routes**: Transport/boundary only. No business logic.
27
+ - **Services**: Business orchestration. Domain logic lives here.
28
+ - **Repositories**: Data access contracts.
29
+ - **Libs**: Infrastructure utilities. No imports from app modules.
30
+
31
+ ### Non-Bypass Rules
32
+ - Never import across incompatible layers
33
+ - Never bypass the service layer from transport handlers
34
+ - Never change public APIs without explicit approval
35
+
36
+ {Add project-specific non-bypass rules here.}
37
+
38
+ ---
39
+
40
+ ## Core Code Patterns (P1–P11)
41
+
42
+ > Full spec with examples → [patterns/code-patterns.md](patterns/code-patterns.md)
43
+
44
+ #### P1 · Port/Adapter
45
+
46
+ Infrastructure abstraction via interface protocols (ports) with concrete implementations (adapters).
47
+
48
+ **Use when:** Adding external service · Swapping infrastructure impl · Writing tests that mock I/O · Reviewing layer boundary compliance · Creating a new infrastructure package
49
+
50
+ #### P2 · Domain Module
51
+
52
+ Standard multi-file layout for each business domain. Enforces separation of concerns within features.
53
+
54
+ **Use when:** Creating a new feature module · Adding a file to existing module · Checking module completeness · Reviewing cross-module dependencies · Moving logic out of routes into services
55
+
56
+ #### P3 · Background Task Boundary
57
+
58
+ Async background processing with runtime dependency injection. Task handlers stay thin; business logic lives in services.
59
+
60
+ **Use when:** Adding a background processing task · Debugging task dependency injection · Understanding task status tracking · Reviewing task error handling and retry logic
61
+
62
+ #### P4 · App Context / Composition Root
63
+
64
+ Global dependency container initialized once at startup. Provides config, session factory, and shared services.
65
+
66
+ **Use when:** Understanding runtime dependency wiring · Adding a new global dependency · Debugging initialization errors · Writing test fixtures that reset global state
67
+
68
+ #### P5 · Transport Route
69
+
70
+ Boundary endpoints using dependency injection for service access. Routes contain only transport concerns.
71
+
72
+ **Use when:** Adding a new endpoint · Wiring a service into a route · Reviewing route-to-service boundary · Adding request/response validation
73
+
74
+ #### P6 · Typed Models
75
+
76
+ Domain entities with explicit types, default factories for collections, and serialization methods.
77
+
78
+ **Use when:** Defining a new entity or value object · Adding fields to existing model · Reviewing serialization behavior · Deciding model type (dataclass vs schema model)
79
+
80
+ #### P7 · Collection/Enum Safety
81
+
82
+ Type-safe namespace references replacing magic strings with enums/constants.
83
+
84
+ **Use when:** Adding a new namespace/collection · Debugging name mismatches · Reviewing normalization logic
85
+
86
+ #### P8 · Error Handling
87
+
88
+ Layered exception hierarchy: domain base → specific errors. Routes catch domain errors and map to transport status codes.
89
+
90
+ **Use when:** Adding error handling · Defining domain exceptions · Mapping service errors to transport responses · Reviewing error propagation across layers
91
+
92
+ #### P9 · Type Hints Everywhere
93
+
94
+ All functions require complete type annotations: parameters, returns, generics. No bare `Any` without justification.
95
+
96
+ **Use when:** Writing any new function · Fixing type checker errors · Reviewing type completeness · Using project-specific type aliases
97
+
98
+ #### P10 · Service Orchestration
99
+
100
+ Services accept all dependencies via constructor. No hidden imports or global access in service methods.
101
+
102
+ **Use when:** Creating a new service · Making a service testable · Wiring mocks in test fixtures · Reviewing service constructor signatures
103
+
104
+ #### P11 · Config Isolation
105
+
106
+ Environment-based config with centralized source and startup validation.
107
+
108
+ **Use when:** Adding environment variable · Understanding config overrides · Debugging config initialization
109
+
110
+ ---
111
+
112
+ ## Process Patterns (R1–R3)
113
+
114
+ #### R1 · Refactoring Workflow
115
+
116
+ 4-phase checklist for safe file moves, import changes, and module restructuring.
117
+
118
+ **Use when:** Moving a file · Renaming a module · Splitting a large file · Merging modules · Running post-refactor validation
119
+
120
+ **Spec:** [patterns/refactoring-workflow.md](patterns/refactoring-workflow.md)
121
+
122
+ #### R2 · Testing Guide
123
+
124
+ Test patterns covering the full test pyramid with fixtures, factories, mocks, and parameterized tests.
125
+
126
+ **Use when:** Writing a new test · Setting up fixtures · Creating mock adapters · Deciding test layer · Reviewing naming conventions · Structuring parameterized tests
127
+
128
+ **Spec:** [patterns/testing-guide.md](patterns/testing-guide.md)
129
+
130
+ #### R3 · API / Boundary Standards
131
+
132
+ Documentation and contract requirements for boundary endpoints: typed models, docstrings, schema generation.
133
+
134
+ **Use when:** Adding a new endpoint · Documenting existing routes · Reviewing response models · Generating or updating specs
135
+
136
+ **Spec:** [patterns/api-standards.md](patterns/api-standards.md)
137
+
138
+ ---
139
+
140
+ ## GKP Routing Table
141
+
142
+ | If task involves… | Use IDs | Read this first |
143
+ |-------------------|---------|-----------------|
144
+ | Ports/adapters and layering | P1, P2, P10 | [patterns/code-patterns.md](patterns/code-patterns.md) |
145
+ | Task orchestration/runtime composition | P3, P4 | [patterns/code-patterns.md](patterns/code-patterns.md) |
146
+ | Boundary contracts and typing | P5, P6, P9 | [patterns/code-patterns.md](patterns/code-patterns.md) |
147
+ | Error policies and config safety | P8, P11 | [patterns/code-patterns.md](patterns/code-patterns.md) |
148
+ | File moves and API-safe refactors | R1 | [patterns/refactoring-workflow.md](patterns/refactoring-workflow.md) |
149
+ | Verification strategy | R2 | [patterns/testing-guide.md](patterns/testing-guide.md) |
150
+ | Interface docs/schema discipline | R3 | [patterns/api-standards.md](patterns/api-standards.md) |
151
+
152
+ ---
153
+
154
+ ## Artifact Placement
155
+
156
+ ### Application Code
157
+
158
+ | Artifact Type | Location |
159
+ |---------------|----------|
160
+ | Domain models | `{modules}/{domain}/models.*` |
161
+ | Business services | `{modules}/{domain}/services.*` |
162
+ | Transport routes | `{modules}/{domain}/routes.*` |
163
+ | Infrastructure ports | `{libs}/{component}/port.*` |
164
+ | Infrastructure adapters | `{libs}/{component}/{impl}.*` |
165
+ | Utilities | `{libs}/utils/` |
166
+
167
+ ### Tests
168
+
169
+ | Test Type | Location | Purpose |
170
+ |-----------|----------|---------|
171
+ | Unit | `tests/unit/test_{component}.*` | Pure logic, no I/O |
172
+ | Integration | `tests/integration/test_{feature}.*` | Multi-component workflows |
173
+ | API/Endpoint | `tests/api/test_{endpoint}.*` | Transport endpoint tests |
174
+ | Fixtures | `tests/conftest.*` | Shared test fixtures |
175
+ | Factories | `tests/factories.*` | Test data builders |
176
+ | Mocks | `tests/mocks.*` | Mock adapters |
177
+
178
+ ### Documentation & Agent Loop
179
+
180
+ | Doc Type | Location |
181
+ |----------|----------|
182
+ | Architecture overview | `docs/ARCHITECTURE.md` |
183
+ | Engineering reports | `docs/reports/YYYY-MM-DD-*.md` |
184
+ | Report artifacts | `docs/reports/artifacts/` |
185
+ | Pattern index + learned fixes | `.agent-loop/patterns.md` |
186
+ | Pattern detail specs | `.agent-loop/patterns/*.md` |
187
+ | Domain terminology | `.agent-loop/glossary.md` |
188
+
189
+ ---
190
+
191
+ ## Naming Anti-Patterns
192
+
193
+ | Anti-Pattern | Correct Approach | Reason |
194
+ |--------------|------------------|--------|
195
+ | `*_v1.py`, `*_v2.py` | Update existing or rename clearly | Git handles versions |
196
+ | `*_new.py`, `*_old.py` | Refactor into `*_next.py` then swap | "New" becomes old quickly |
197
+ | `test_*_v2.py` | `test_<original_name>.py` | 1:1 mapping to source module |
198
+
199
+ ---
200
+
201
+ ## Learned Patterns (LEARN writes here)
202
+
203
+ ### Auto-Fixes
204
+
205
+ | Trigger | Root Cause | Minimal Safe Fix | Auto-Apply |
206
+ |---------|------------|------------------|------------|
207
+ | Missing explicit return type | Signature drift | Add concrete return annotation | ✅ |
208
+ | Stray debug artifact | Temporary local instrumentation left in patch | Remove or replace with standard logging | ✅ |
209
+ | Caller mismatch after rename | Incomplete refactor propagation | Update all known call sites before merge | ⚠️ |
210
+
211
+ ### Common Errors
212
+
213
+ | Symptom | Why It Happens | Prevention Heuristic |
214
+ |---------|----------------|----------------------|
215
+ | Gate keeps failing with small patch changes | Symptoms fixed, not root cause | Patch the first cause in dependency chain |
216
+ | Neighbor break after “safe” refactor | Consumer map incomplete | Run validate-n immediately after structural edits |
217
+ | Quality regressions in fast fixes | No targeted test sequence | Start with narrow tests, then expand |
218
+
219
+ ### Heuristics
220
+
221
+ | Do More | Do Less |
222
+ |---------|---------|
223
+ | Compress context into explicit constraints | Carry large raw snippets forward |
224
+ | Validate boundaries early | Delay compatibility checks |
225
+ | Keep patches reversible | Mix refactor and feature changes together |
226
+
227
+ ### Pruning Records
228
+
229
+ Populated by [feedback.md §Branch Pruning Protocol](feedback.md) during the LEARN phase when a failed approach is pruned.
230
+
231
+ | Attempt | Strategy | Failure Reason | Constraint Added |
232
+ |---------|----------|----------------|------------------|
233
+ | — | — | — | — |
234
+
235
+ ---
236
+
237
+ ## Pattern Addition Policy
238
+
239
+ Add a new pattern when all are true:
240
+ 1. The approach is reusable across multiple tasks
241
+ 2. It reduces failure recurrence
242
+ 3. It has clear constraints and examples
243
+
244
+ Write location:
245
+ - Short rule: table in this file
246
+ - Deep implementation: dedicated section/file under `patterns/`
247
+
248
+ ---
249
+
250
+ ## Related Documents
251
+
252
+ | Document | Purpose |
253
+ |----------|---------|
254
+ | [reasoning-kernel.md](reasoning-kernel.md) | End-to-end protocol and mode selection |
255
+ | [feedback.md](feedback.md) | Failure-to-learning bridge |
256
+ | [glossary.md](glossary.md) | Canonical naming and ambiguity resolution |