mirror of
https://github.com/himanshudongre/smriti.git
synced 2026-10-07 02:58:07 +00:00
Tasks in checkpoints evolve from flat strings to objects with optional intent_hint (implement/review/investigate/docs/test), blocked_by (dependency label), and status (open/done). Agents reading the state brief can now self-select complementary work by matching task intents against active claim intent_types — no founder routing needed. Backward-compatible: old string tasks normalize at render time. No schema migration. JSONB handles both shapes. Skill pack v1.7 teaches the autonomous selection reflex.
101 lines
3.6 KiB
Python
101 lines
3.6 KiB
Python
"""Integration tests for POST /api/v5/checkpoint/extract.
|
|
|
|
Tests use MockAdapter's JSON-mode path (registry._MOCK_JSON_RESPONSE),
|
|
which returns a deterministic canned blob when the caller passes
|
|
response_format={"type": "json_object"}. The extract endpoint always
|
|
requests JSON mode, so every test hits the canned response.
|
|
"""
|
|
|
|
|
|
def _sample_markdown() -> str:
|
|
return """# Design: envdiff CLI
|
|
|
|
## Objective
|
|
Build a stdlib-only CLI that compares two .env files.
|
|
|
|
## Decisions
|
|
- Use argparse, not click
|
|
- Single file, not a package
|
|
|
|
## Assumptions
|
|
- Python 3.11+ is available
|
|
|
|
```python
|
|
def main():
|
|
print("hello")
|
|
```
|
|
"""
|
|
|
|
|
|
def test_extract_happy_path_with_mock(client):
|
|
"""Extract endpoint returns canned mock fields when use_mock=True."""
|
|
r = client.post(
|
|
"/api/v5/checkpoint/extract",
|
|
json={"content": _sample_markdown(), "use_mock": True},
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
data = r.json()
|
|
# MockAdapter JSON mode returns the canned _MOCK_JSON_RESPONSE in
|
|
# registry.py — these are the exact values defined there.
|
|
assert data["title"] == "Mock Checkpoint"
|
|
assert data["summary"].startswith("Mock summary")
|
|
assert "Mock decision from provider" in data["decisions"]
|
|
assert "Mock assumption from provider" in data["assumptions"]
|
|
# Tasks are now structured objects; a plain string from the mock
|
|
# becomes {"text": "Mock task from provider"}.
|
|
assert any(
|
|
(t.get("text") if isinstance(t, dict) else t) == "Mock task from provider"
|
|
for t in data["tasks"]
|
|
)
|
|
assert "Mock open question from provider" in data["open_questions"]
|
|
assert "MockEntity" in data["entities"]
|
|
assert len(data["artifacts"]) == 1
|
|
assert data["artifacts"][0]["label"] == "Mock artifact"
|
|
|
|
|
|
def test_extract_default_path_returns_shape(client):
|
|
"""Default path (no use_mock flag) returns a valid CheckpointExtractResponse
|
|
shape regardless of which provider answers. In a test env with no API
|
|
keys this hits MockAdapter via the allow_mock=True fallback; in a dev
|
|
env with real keys it hits the real provider and returns real extracted
|
|
fields. Either way the response must be well-shaped.
|
|
"""
|
|
r = client.post(
|
|
"/api/v5/checkpoint/extract",
|
|
json={"content": _sample_markdown()},
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
data = r.json()
|
|
# Response shape: every field present, strings are strings, lists are lists.
|
|
assert isinstance(data["title"], str)
|
|
assert isinstance(data["objective"], str)
|
|
assert isinstance(data["summary"], str)
|
|
assert isinstance(data["decisions"], list)
|
|
assert isinstance(data["assumptions"], list)
|
|
assert isinstance(data["tasks"], list)
|
|
assert isinstance(data["open_questions"], list)
|
|
assert isinstance(data["entities"], list)
|
|
assert isinstance(data["artifacts"], list)
|
|
# Something must have been extracted from the sample — the title must be
|
|
# non-empty and at least one decision should appear since the sample has
|
|
# a "## Decisions" section with two bullet points.
|
|
assert data["title"].strip() != ""
|
|
assert len(data["decisions"]) > 0
|
|
|
|
|
|
def test_extract_rejects_empty_content(client):
|
|
"""Empty content is a 422 validation error."""
|
|
r = client.post(
|
|
"/api/v5/checkpoint/extract",
|
|
json={"content": " "},
|
|
)
|
|
assert r.status_code == 422, r.text
|
|
|
|
|
|
def test_extract_rejects_oversized_content(client):
|
|
"""Content exceeding 200000 character cap is a 422 validation error."""
|
|
r = client.post(
|
|
"/api/v5/checkpoint/extract",
|
|
json={"content": "x" * 300_000},
|
|
)
|
|
assert r.status_code == 422, r.text
|