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:
rob 2025-10-30 16:20:42 -03:00
parent 858dcae72e
commit 05002b766b
5 changed files with 160 additions and 7 deletions

0
assets/__init__.py Normal file
View File

0
automation/__init__.py Normal file
View File

View File

@ -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"]

View File

@ -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

82
tests/test_workflow.py Normal file
View File

@ -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"}