mirror of
https://github.com/arkorty/B.Tech-Project-III.git
synced 2026-04-19 12:41:48 +00:00
206 lines
8.2 KiB
Python
206 lines
8.2 KiB
Python
"""
|
|
Test Milestone 18: Jira Signal Agent.
|
|
Seeds real signals and raises actual Jira tickets.
|
|
Requires Milestone 17 (Jira client) to be passing.
|
|
"""
|
|
import asyncio
|
|
import os
|
|
import sys
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
|
|
|
|
# ─── Sample signals ───────────────────────────────────────────────────────────
|
|
|
|
SAMPLE_SIGNALS = [
|
|
{
|
|
"id": "test-signal-001",
|
|
"type": "recurring_bug",
|
|
"summary": "Checkout endpoint hits intermittent timeout — third time this sprint. Restarting the pod is the workaround.",
|
|
"raw_quote": "Sam: Timeout error AGAIN. That's the third time. We have a systemic issue here.",
|
|
"severity": "high",
|
|
"status": "open",
|
|
"sentiment": "negative",
|
|
"urgency": "high",
|
|
"entities": ["@Sam", "@Alex"],
|
|
"keywords": ["timeout", "checkout", "pod", "systemic"],
|
|
"timestamp": "2026-03-21T09:00:00Z",
|
|
"group_id": "acme_dev",
|
|
"lens": "dev",
|
|
},
|
|
{
|
|
"id": "test-signal-002",
|
|
"type": "tech_debt",
|
|
"summary": "JWT secret is hardcoded in auth service. Will move to Vault later, no timeline set.",
|
|
"raw_quote": "Alex: For the auth service, I'm hardcoding the JWT secret for now. We'll move to vault later.",
|
|
"severity": "medium",
|
|
"status": "open",
|
|
"sentiment": "neutral",
|
|
"urgency": "low",
|
|
"entities": ["@Alex"],
|
|
"keywords": ["jwt", "hardcode", "vault", "auth", "secret"],
|
|
"timestamp": "2026-03-21T09:05:00Z",
|
|
"group_id": "acme_dev",
|
|
"lens": "dev",
|
|
},
|
|
{
|
|
"id": "test-signal-003",
|
|
"type": "meet_blocker",
|
|
"summary": "Dashboard spec has been blocked waiting on design for two weeks. Dev cannot start work.",
|
|
"raw_quote": "Alex: Still no dashboard specs from design. This is blocking my entire sprint work.",
|
|
"severity": "high",
|
|
"status": "open",
|
|
"sentiment": "negative",
|
|
"urgency": "high",
|
|
"entities": ["@Alex", "@design"],
|
|
"keywords": ["dashboard", "blocked", "design", "specs", "sprint"],
|
|
"timestamp": "2026-03-21T10:00:00Z",
|
|
"group_id": "meet_sessions",
|
|
"lens": "meet",
|
|
"meeting_id": "sprint-planning-test",
|
|
},
|
|
]
|
|
|
|
# A signal type that should NOT be raised (raw chunk is not a raiseable type)
|
|
NON_RAISEABLE_SIGNAL = {
|
|
"id": "test-signal-999",
|
|
"type": "meet_chunk_raw",
|
|
"summary": "Raw transcript chunk — should not be raised as a ticket",
|
|
"raw_quote": "...",
|
|
"severity": "low",
|
|
"status": "open",
|
|
"sentiment": "neutral",
|
|
"urgency": "none",
|
|
"entities": [],
|
|
"keywords": [],
|
|
"timestamp": "2026-03-21T10:00:00Z",
|
|
"group_id": "meet_sessions",
|
|
"lens": "meet",
|
|
}
|
|
|
|
|
|
async def test_ticket_generation():
|
|
"""Test that LLM generates a valid ticket from a signal."""
|
|
from backend.agents.jira_agent import generate_ticket_content
|
|
|
|
print("Testing LLM ticket content generation...")
|
|
signal = SAMPLE_SIGNALS[0] # recurring_bug
|
|
content = await generate_ticket_content(signal)
|
|
|
|
assert "summary" in content and len(content["summary"]) > 5, "Summary too short or missing"
|
|
assert len(content["summary"]) <= 100, f"Summary exceeds 100 chars: {len(content['summary'])}"
|
|
assert "description" in content and len(content["description"]) > 30, "Description too short"
|
|
assert "labels" in content and "thirdeye" in content["labels"], "Missing 'thirdeye' label"
|
|
assert "assignee_name" in content # can be None, that's fine
|
|
|
|
print(f" ✅ Summary ({len(content['summary'])} chars): {content['summary']}")
|
|
print(f" ✅ Description ({len(content['description'])} chars)")
|
|
print(f" ✅ Labels: {content['labels']}")
|
|
print(f" ✅ Assignee hint: {content.get('assignee_name')}")
|
|
|
|
|
|
async def test_raise_single_ticket():
|
|
"""Test raising a single ticket for a real signal."""
|
|
from backend.agents.jira_agent import raise_ticket_for_signal
|
|
|
|
print("\nTesting raise_ticket_for_signal()...")
|
|
signal = SAMPLE_SIGNALS[0] # recurring_bug, high severity
|
|
group_id = "test_jira_m18"
|
|
|
|
result = await raise_ticket_for_signal(signal, group_id, force=True)
|
|
|
|
assert result.get("ok"), f"raise_ticket_for_signal failed: {result}"
|
|
print(f" ✅ Ticket raised: {result['key']}")
|
|
print(f" URL: {result['url']}")
|
|
print(f" Type: {result['issue_type']} | Priority: {result['priority']}")
|
|
print(f" Summary: {result['summary'][:90]}")
|
|
return result["key"]
|
|
|
|
|
|
async def test_dedup_prevents_double_raise():
|
|
"""Test that the same signal cannot be raised twice."""
|
|
from backend.agents.jira_agent import raise_ticket_for_signal
|
|
from backend.db.chroma import mark_signal_as_raised
|
|
|
|
print("\nTesting dedup — cannot raise the same signal twice...")
|
|
signal = SAMPLE_SIGNALS[1] # tech_debt
|
|
group_id = "test_jira_m18_dedup"
|
|
|
|
# First raise
|
|
result1 = await raise_ticket_for_signal(signal, group_id, force=True)
|
|
assert result1.get("ok"), f"First raise failed: {result1}"
|
|
print(f" ✅ First raise succeeded: {result1['key']}")
|
|
|
|
# Second raise of the same signal — should be blocked
|
|
result2 = await raise_ticket_for_signal(signal, group_id, force=False)
|
|
assert not result2.get("ok"), "Expected second raise to be blocked"
|
|
assert result2.get("reason") == "already_raised", f"Expected 'already_raised', got: {result2.get('reason')}"
|
|
print(f" ✅ Second raise correctly blocked: reason='{result2['reason']}'")
|
|
|
|
|
|
async def test_non_raiseable_signal():
|
|
"""Test that non-raiseable signal types are rejected."""
|
|
from backend.agents.jira_agent import raise_ticket_for_signal
|
|
|
|
print("\nTesting non-raiseable signal type rejection...")
|
|
result = await raise_ticket_for_signal(NON_RAISEABLE_SIGNAL, "test_group", force=True)
|
|
assert not result.get("ok")
|
|
assert result.get("reason") == "not_raiseable"
|
|
print(f" ✅ Non-raiseable type correctly rejected: {NON_RAISEABLE_SIGNAL['type']}")
|
|
|
|
|
|
async def test_bulk_raise():
|
|
"""Test bulk raising multiple signals at once."""
|
|
from backend.agents.jira_agent import bulk_raise_for_group
|
|
|
|
print("\nTesting bulk_raise_for_group()...")
|
|
group_id = "test_jira_m18_bulk"
|
|
|
|
# Mix of raiseable and non-raiseable, different severities
|
|
all_signals = SAMPLE_SIGNALS + [NON_RAISEABLE_SIGNAL]
|
|
results = await bulk_raise_for_group(
|
|
group_id=group_id,
|
|
signals=all_signals,
|
|
min_severity="medium", # low severity signals should be skipped
|
|
max_tickets=5,
|
|
)
|
|
|
|
raised = [r for r in results if r.get("ok")]
|
|
skipped_type = [r for r in results if r.get("reason") == "not_raiseable"]
|
|
|
|
assert len(raised) >= 1, "Expected at least 1 ticket raised from bulk"
|
|
print(f" ✅ Bulk raised {len(raised)} ticket(s) from {len(all_signals)} signals")
|
|
for r in raised:
|
|
print(f" [{r['key']}] {r.get('signal_type')} — {r.get('signal_summary', '')[:60]}")
|
|
if skipped_type:
|
|
print(f" ✅ {len(skipped_type)} non-raiseable signal(s) correctly skipped")
|
|
|
|
|
|
async def test_priority_mapping():
|
|
"""Test that signal severity maps to correct Jira priority."""
|
|
from backend.agents.jira_agent import SEVERITY_TO_PRIORITY, SIGNAL_TYPE_MAP
|
|
|
|
print("\nTesting priority and type mapping...")
|
|
assert SEVERITY_TO_PRIORITY["critical"] == "Highest"
|
|
assert SEVERITY_TO_PRIORITY["high"] == "High"
|
|
assert SEVERITY_TO_PRIORITY["medium"] == "Medium"
|
|
assert SEVERITY_TO_PRIORITY["low"] == "Low"
|
|
print(" ✅ Severity → Priority mapping correct")
|
|
|
|
assert SIGNAL_TYPE_MAP["recurring_bug"] == ("Task", "High")
|
|
assert SIGNAL_TYPE_MAP["meet_blocker"] == ("Task", "Highest")
|
|
assert SIGNAL_TYPE_MAP["feature_request"] == ("Task", "Medium")
|
|
print(" ✅ Signal type → Jira type mapping correct")
|
|
|
|
|
|
async def main():
|
|
print("Running Milestone 18 tests...\n")
|
|
await test_priority_mapping()
|
|
await test_ticket_generation()
|
|
key = await test_raise_single_ticket()
|
|
await test_dedup_prevents_double_raise()
|
|
await test_non_raiseable_signal()
|
|
await test_bulk_raise()
|
|
print(f"\n🎉 MILESTONE 18 PASSED — Jira Signal Agent working. First ticket: {key}")
|
|
|
|
|
|
asyncio.run(main()) |