test: Add comprehensive workflow tests and improve template tests
Testing improvements completing Week 1 consolidation: 1. Add tests/test_workflow.py (6 comprehensive tests) - test_extract_vote_value: Vote value extraction - test_parse_votes_single_participant_single_vote: Basic parsing - test_parse_votes_single_participant_multiple_votes: Latest vote wins - test_parse_votes_multiple_participants: Multi-participant tracking - test_parse_votes_malformed_lines: Error handling - test_parse_votes_mixed_content: Real-world scenarios 2. Improve tests/test_template_meta.py - Replace stub tests with real implementations - test_find_template_fields: Field extraction from templates - test_render_request_from_template: Template rendering - test_render_request_from_template_with_existing_meta: Preserve existing data 3. Add __init__.py files for test imports - assets/__init__.py: Make assets importable - automation/__init__.py: Make automation importable - Enables tests to import workflow.py and create_feature.py 4. Update pyproject.toml pytest configuration - Add ".", "assets" to pythonpath - Allows tests to import from automation/ and assets/ Test Results: - All 11 tests passing - Coverage: workflow vote parsing, template rendering, utils - Foundation ready for Stage 2 development 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
858dcae72e
commit
05002b766b
|
|
@ -21,4 +21,4 @@ packages = ["cascadingdev"]
|
||||||
version = { file = "VERSION" }
|
version = { file = "VERSION" }
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
pythonpath = ["src"]
|
pythonpath = ["src", ".", "assets"]
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,81 @@
|
||||||
# tests/test_template_meta.py
|
# tests/test_template_meta.py
|
||||||
import pytest
|
import pytest
|
||||||
|
from assets.runtime.create_feature import find_template_fields, render_request_from_template
|
||||||
|
|
||||||
def test_load_template_with_meta():
|
def test_find_template_fields():
|
||||||
# Test META parsing
|
template_content = """
|
||||||
assert True
|
# Feature Request
|
||||||
|
|
||||||
|
**Title**: <title>
|
||||||
|
**Intent**: <one paragraph describing purpose>
|
||||||
|
**Motivation / Problem**: <why this is needed now>
|
||||||
|
**Feature ID**: <will be generated>
|
||||||
|
**Meta**: <will be generated>
|
||||||
|
**Author**: <name>
|
||||||
|
"""
|
||||||
|
fields = find_template_fields(template_content)
|
||||||
|
expected_fields = [
|
||||||
|
("Title", "<title>"),
|
||||||
|
("Intent", "<one paragraph describing purpose>"),
|
||||||
|
("Motivation / Problem", "<why this is needed now>"),
|
||||||
|
("Author", "<name>")
|
||||||
|
]
|
||||||
|
assert fields == expected_fields
|
||||||
|
|
||||||
|
def test_render_request_from_template():
|
||||||
|
template_content = """
|
||||||
|
# Feature Request: <title>
|
||||||
|
|
||||||
|
**Intent**: <one paragraph describing purpose>
|
||||||
|
**Author**: <name>
|
||||||
|
"""
|
||||||
|
fields = {
|
||||||
|
"Title": "My Awesome Feature",
|
||||||
|
"Intent": "This feature will do amazing things.",
|
||||||
|
"Author": "Test User"
|
||||||
|
}
|
||||||
|
fid = "FR_2023-10-27_my-awesome-feature"
|
||||||
|
created = "2023-10-27"
|
||||||
|
|
||||||
|
rendered_content = render_request_from_template(template_content, fields, fid, created)
|
||||||
|
expected_content = """
|
||||||
|
# Feature Request: My Awesome Feature
|
||||||
|
|
||||||
|
**Intent**: This feature will do amazing things.
|
||||||
|
**Author**: Test User
|
||||||
|
|
||||||
|
**Feature ID**: FR_2023-10-27_my-awesome-feature
|
||||||
|
**Meta**: Created: 2023-10-27 • Author: Test User
|
||||||
|
"""
|
||||||
|
assert rendered_content.strip() == expected_content.strip()
|
||||||
|
|
||||||
|
def test_render_request_from_template_with_existing_meta():
|
||||||
|
template_content = """
|
||||||
|
# Feature Request: <title>
|
||||||
|
|
||||||
|
**Intent**: <one paragraph describing purpose>
|
||||||
|
**Author**: <name>
|
||||||
|
|
||||||
|
**Feature ID**: EXISTING_FID
|
||||||
|
**Meta**: Existing Meta Info
|
||||||
|
"""
|
||||||
|
fields = {
|
||||||
|
"Title": "Another Feature",
|
||||||
|
"Intent": "This is another feature.",
|
||||||
|
"Author": "Another User"
|
||||||
|
}
|
||||||
|
fid = "FR_2023-10-28_another-feature"
|
||||||
|
created = "2023-10-28"
|
||||||
|
|
||||||
|
rendered_content = render_request_from_template(template_content, fields, fid, created)
|
||||||
|
expected_content = """
|
||||||
|
# Feature Request: Another Feature
|
||||||
|
|
||||||
|
**Intent**: This is another feature.
|
||||||
|
**Author**: Another User
|
||||||
|
|
||||||
|
**Feature ID**: EXISTING_FID
|
||||||
|
**Meta**: Existing Meta Info
|
||||||
|
"""
|
||||||
|
assert rendered_content.strip() == expected_content.strip()
|
||||||
|
|
||||||
def test_render_placeholders():
|
|
||||||
# Test token replacement
|
|
||||||
assert True
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
from automation.workflow import parse_votes, _extract_vote_value
|
||||||
|
|
||||||
|
def test_extract_vote_value():
|
||||||
|
assert _extract_vote_value("READY") == "READY"
|
||||||
|
assert _extract_vote_value("CHANGES ") == "CHANGES"
|
||||||
|
assert _extract_vote_value(" REJECT") == "REJECT"
|
||||||
|
assert _extract_vote_value("INVALID") is None
|
||||||
|
assert _extract_vote_value("Some text READY") is None
|
||||||
|
assert _extract_vote_value("READY ") == "READY"
|
||||||
|
assert _extract_vote_value("No vote here") is None
|
||||||
|
|
||||||
|
def test_parse_votes_single_participant_single_vote(tmp_path):
|
||||||
|
discussion_content = """
|
||||||
|
- Participant A: Initial comment.
|
||||||
|
- Participant A: VOTE: READY
|
||||||
|
"""
|
||||||
|
discussion_file = tmp_path / "discussion.md"
|
||||||
|
discussion_file.write_text(discussion_content)
|
||||||
|
|
||||||
|
votes = parse_votes(discussion_file)
|
||||||
|
assert votes == {"Participant A": "READY"}
|
||||||
|
|
||||||
|
def test_parse_votes_single_participant_multiple_votes(tmp_path):
|
||||||
|
discussion_content = """
|
||||||
|
- Participant B: First comment. VOTE: CHANGES
|
||||||
|
- Participant B: Second comment.
|
||||||
|
- Participant B: VOTE: READY
|
||||||
|
"""
|
||||||
|
discussion_file = tmp_path / "discussion.md"
|
||||||
|
discussion_file.write_text(discussion_content)
|
||||||
|
|
||||||
|
votes = parse_votes(discussion_file)
|
||||||
|
assert votes == {"Participant B": "READY"}
|
||||||
|
|
||||||
|
def test_parse_votes_multiple_participants(tmp_path):
|
||||||
|
discussion_content = """
|
||||||
|
- Participant C: Comment one. VOTE: READY
|
||||||
|
- Participant D: Comment two. VOTE: CHANGES
|
||||||
|
- Participant C: Another comment.
|
||||||
|
- Participant D: Final thoughts. VOTE: READY
|
||||||
|
"""
|
||||||
|
discussion_file = tmp_path / "discussion.md"
|
||||||
|
discussion_file.write_text(discussion_content)
|
||||||
|
|
||||||
|
votes = parse_votes(discussion_file)
|
||||||
|
assert votes == {"Participant C": "READY", "Participant D": "READY"}
|
||||||
|
|
||||||
|
def test_parse_votes_malformed_lines(tmp_path):
|
||||||
|
discussion_content = """
|
||||||
|
- Participant E: VOTE: READY
|
||||||
|
- Participant F: VOTE: INVALID_VOTE
|
||||||
|
- Participant E: Another comment. VOTE: CHANGES
|
||||||
|
- Participant F: Just a comment.
|
||||||
|
"""
|
||||||
|
discussion_file = tmp_path / "discussion.md"
|
||||||
|
discussion_file.write_text(discussion_content)
|
||||||
|
|
||||||
|
votes = parse_votes(discussion_file)
|
||||||
|
assert votes == {"Participant E": "CHANGES"} # Participant F's vote is invalid and ignored
|
||||||
|
|
||||||
|
def test_parse_votes_mixed_content(tmp_path):
|
||||||
|
discussion_content = """
|
||||||
|
# Discussion Title
|
||||||
|
|
||||||
|
Some introductory text.
|
||||||
|
|
||||||
|
- Participant G: First point.
|
||||||
|
- Participant G: Second point. VOTE: READY
|
||||||
|
|
||||||
|
- Participant H: Question?
|
||||||
|
- Participant H: VOTE: CHANGES
|
||||||
|
|
||||||
|
- Participant G: Response to H. VOTE: REJECT
|
||||||
|
|
||||||
|
"""
|
||||||
|
discussion_file = tmp_path / "discussion.md"
|
||||||
|
discussion_file.write_text(discussion_content)
|
||||||
|
|
||||||
|
votes = parse_votes(discussion_file)
|
||||||
|
assert votes == {"Participant G": "REJECT", "Participant H": "CHANGES"}
|
||||||
Loading…
Reference in New Issue