feat: initial commit — Billo Release Agent (LangGraph)
LangGraph-based release automation agent with: - PR discovery (webhook + polling) - AI code review via Claude Code CLI (subscription-based) - Auto-create Jira tickets for PRs without ticket ID - Jira ticket lifecycle management (code review -> staging -> done) - CI/CD pipeline trigger, polling, and approval gates - Slack interactive messages with approval buttons - Per-repo semantic versioning - PostgreSQL persistence (threads, staging, releases) - FastAPI API (webhooks, approvals, status, manual triggers) - Docker Compose deployment 1069 tests, 95%+ coverage.
This commit is contained in:
53
tests/graph/test_full_cycle.py
Normal file
53
tests/graph/test_full_cycle.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""Tests for graph/full_cycle.py.
|
||||
|
||||
Tests that the full cycle graph composes pr_completed and release subgraphs
|
||||
correctly, and that the routing conditional edge works as expected.
|
||||
"""
|
||||
|
||||
from release_agent.graph.full_cycle import build_full_cycle_graph
|
||||
from release_agent.graph.routing import should_continue_to_release
|
||||
|
||||
|
||||
class TestBuildFullCycleGraph:
|
||||
def test_returns_compiled_graph(self) -> None:
|
||||
graph = build_full_cycle_graph()
|
||||
assert graph is not None
|
||||
|
||||
def test_graph_can_be_built_multiple_times(self) -> None:
|
||||
graph1 = build_full_cycle_graph()
|
||||
graph2 = build_full_cycle_graph()
|
||||
assert graph1 is not None
|
||||
assert graph2 is not None
|
||||
|
||||
def test_graph_has_get_graph_method(self) -> None:
|
||||
graph = build_full_cycle_graph()
|
||||
assert hasattr(graph, "get_graph") or hasattr(graph, "nodes")
|
||||
|
||||
|
||||
class TestFullCycleRouting:
|
||||
"""Test that the routing function used by full_cycle correctly
|
||||
determines whether to continue to the release subgraph."""
|
||||
|
||||
def test_continue_when_flag_true_and_no_errors(self) -> None:
|
||||
state = {"continue_to_release": True, "errors": []}
|
||||
assert should_continue_to_release(state) == "yes"
|
||||
|
||||
def test_stop_when_flag_false(self) -> None:
|
||||
state = {"continue_to_release": False}
|
||||
assert should_continue_to_release(state) == "no"
|
||||
|
||||
def test_stop_when_flag_missing(self) -> None:
|
||||
state = {}
|
||||
assert should_continue_to_release(state) == "no"
|
||||
|
||||
def test_stop_when_errors_present(self) -> None:
|
||||
state = {"continue_to_release": True, "errors": ["some error"]}
|
||||
assert should_continue_to_release(state) == "no"
|
||||
|
||||
def test_stop_when_flag_true_but_errors_present(self) -> None:
|
||||
state = {"continue_to_release": True, "errors": ["critical failure"]}
|
||||
assert should_continue_to_release(state) == "no"
|
||||
|
||||
def test_continue_when_errors_empty_list(self) -> None:
|
||||
state = {"continue_to_release": True, "errors": []}
|
||||
assert should_continue_to_release(state) == "yes"
|
||||
Reference in New Issue
Block a user