Add unittests for MVP milestone tracking and update roadmap logic

This commit is contained in:
2026-01-09 14:35:26 +00:00
parent d19a264263
commit b6a76f5b2a
3 changed files with 120 additions and 9 deletions

View File

@@ -16,7 +16,7 @@
- [x] **Automated Testing**: Integration with test runners to verify changes before PR.
- [x] **Linting Integration**: Automatically run and fix linting issues.
- [x] **Multi-Model Support**: Easily switch between different LLM providers.
- [x] **CI/CD Integration**: Github Actions to run AutoMetabuilder on schedule or trigger.
- [ ] **CI/CD Integration**: Github Actions to run AutoMetabuilder on schedule or trigger.
## Phase 4: Optimization & Scalability
- [x] **Dockerization**: Provide a Dockerfile and docker-compose for easy environment setup. Added `run_docker_task` tool.

View File

@@ -19,16 +19,21 @@ def is_mvp_reached() -> bool:
with open("ROADMAP.md", "r", encoding="utf-8") as f:
content = f.read()
# Find the MVP section
mvp_match = re.search(r"## .*?\(MVP\)(.*?)##", content, re.DOTALL | re.IGNORECASE)
if not mvp_match:
# Try finding it if it's the last section
mvp_match = re.search(r"## .*?\(MVP\)(.*)", content, re.DOTALL | re.IGNORECASE)
if not mvp_match:
# Find the header line containing (MVP)
header_match = re.search(r"^## .*?\(MVP\).*?$", content, re.MULTILINE | re.IGNORECASE)
if not header_match:
return False
mvp_section = mvp_match.group(1)
# Get the position of the header
start_pos = header_match.end()
# Find the next header starting from start_pos
next_header_match = re.search(r"^## ", content[start_pos:], re.MULTILINE)
if next_header_match:
mvp_section = content[start_pos : start_pos + next_header_match.start()]
else:
mvp_section = content[start_pos:]
# Check if there are any unchecked items [ ]
if "[ ]" in mvp_section:
return False

106
tests/test_roadmap.py Normal file
View File

@@ -0,0 +1,106 @@
import os
import unittest
from src.autometabuilder.roadmap_utils import is_mvp_reached, update_roadmap
class TestRoadmap(unittest.TestCase):
def setUp(self):
# Backup original ROADMAP.md if it exists
self.original_content = None
if os.path.exists("ROADMAP.md"):
with open("ROADMAP.md", "r", encoding="utf-8") as f:
self.original_content = f.read()
def tearDown(self):
# Restore original ROADMAP.md
if self.original_content is not None:
with open("ROADMAP.md", "w", encoding="utf-8") as f:
f.write(self.original_content)
elif os.path.exists("ROADMAP.md"):
os.remove("ROADMAP.md")
def test_is_mvp_reached_true(self):
content = """
# Roadmap
## Phase 3: Advanced Automation (MVP)
- [x] Item 1
- [x] Item 2
"""
update_roadmap(content)
self.assertTrue(is_mvp_reached())
def test_is_mvp_reached_false_unchecked(self):
content = """
# Roadmap
## Phase 3: Advanced Automation (MVP)
- [x] Item 1
- [ ] Item 2
"""
update_roadmap(content)
self.assertFalse(is_mvp_reached())
def test_is_mvp_reached_false_no_items(self):
content = """
# Roadmap
## Phase 3: Advanced Automation (MVP)
No items here
"""
update_roadmap(content)
self.assertFalse(is_mvp_reached())
def test_is_mvp_reached_case_insensitive(self):
content = """
# Roadmap
## Phase 3: (mvp)
- [x] Done
"""
update_roadmap(content)
self.assertTrue(is_mvp_reached())
def test_is_mvp_reached_with_other_sections(self):
content = """
# Roadmap
## Phase 1
- [x] Done
## Phase 3 (MVP)
- [ ] Not done
## Phase 4
- [x] Done
"""
update_roadmap(content)
self.assertFalse(is_mvp_reached())
def test_is_mvp_reached_no_section(self):
content = """
# Roadmap
## Phase 1
- [x] Done
"""
update_roadmap(content)
self.assertFalse(is_mvp_reached())
def test_is_mvp_reached_multiple_mvp_markers(self):
# Should probably pick the first one or behave consistently
content = """
# Roadmap
## Phase 3 (MVP)
- [x] Done
## Phase 5 (MVP)
- [ ] Not done
"""
update_roadmap(content)
# Current logic picks the first match
self.assertTrue(is_mvp_reached())
def test_is_mvp_reached_not_in_header(self):
content = """
# Roadmap
## Phase 1
This is not (MVP) but it mentions it.
- [x] Done
"""
update_roadmap(content)
# Should be False because (MVP) is not in a header
self.assertFalse(is_mvp_reached())