Files
2026-04-05 00:43:23 +05:30

185 lines
6.7 KiB
Python

"""
Test Milestone 17: Jira API client.
Tests real API connectivity — your .env must have valid Jira credentials.
"""
import asyncio
import os
import sys
# Fix Windows console encoding for emoji support
if sys.platform == "win32":
import codecs
sys.stdout = codecs.getwriter("utf-8")(sys.stdout.buffer, errors="replace")
sys.stderr = codecs.getwriter("utf-8")(sys.stderr.buffer, errors="replace")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
async def test_config_loaded():
"""Test that all Jira config vars are present."""
from backend.config import (
JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN,
JIRA_DEFAULT_PROJECT, ENABLE_JIRA
)
checks = {
"JIRA_BASE_URL": JIRA_BASE_URL,
"JIRA_EMAIL": JIRA_EMAIL,
"JIRA_API_TOKEN": JIRA_API_TOKEN,
"JIRA_DEFAULT_PROJECT": JIRA_DEFAULT_PROJECT,
}
all_pass = True
for name, val in checks.items():
ok = bool(val and len(val) >= 2)
status = "" if ok else "❌ MISSING"
if not ok:
all_pass = False
print(f" {status} {name}: {val[:30] if val else '(empty)'}...")
assert all_pass, "Fix missing Jira config vars in .env before continuing"
print(f" ✅ ENABLE_JIRA: {ENABLE_JIRA}")
async def test_connection():
"""Test that credentials are valid and can reach the Jira API."""
from backend.integrations.jira_client import test_connection
print("\nTesting Jira API connection...")
result = await test_connection()
assert result.get("ok"), f"Connection failed: {result.get('error')}"
print(f" ✅ Connected as: {result['display_name']} ({result['email']})")
print(f" Account ID: {result['account_id']}")
async def test_list_projects():
"""Test listing projects — must return at least one."""
from backend.integrations.jira_client import list_projects
from backend.config import JIRA_DEFAULT_PROJECT
print("\nTesting list_projects()...")
projects = await list_projects()
assert len(projects) > 0, "No projects returned. Make sure your account has at least one project."
print(f" ✅ Found {len(projects)} project(s):")
for p in projects[:5]:
print(f" [{p['key']}] {p['name']}")
keys = [p["key"] for p in projects]
assert JIRA_DEFAULT_PROJECT in keys, (
f"JIRA_DEFAULT_PROJECT '{JIRA_DEFAULT_PROJECT}' not found in your projects: {keys}\n"
"Update JIRA_DEFAULT_PROJECT in .env to one of the listed keys."
)
print(f" ✅ Default project '{JIRA_DEFAULT_PROJECT}' exists")
async def test_list_issue_types():
"""Test listing issue types for the default project."""
from backend.integrations.jira_client import list_issue_types
from backend.config import JIRA_DEFAULT_PROJECT
print("\nTesting list_issue_types()...")
types = await list_issue_types(JIRA_DEFAULT_PROJECT)
assert len(types) > 0, f"No issue types returned for project {JIRA_DEFAULT_PROJECT}"
names = [t["name"] for t in types]
print(f" ✅ Issue types in '{JIRA_DEFAULT_PROJECT}': {names}")
async def test_create_and_get_issue():
"""Test creating a real Jira issue and then retrieving it."""
from backend.integrations.jira_client import create_issue, get_issue
from backend.config import JIRA_DEFAULT_PROJECT
print("\nTesting create_issue() and get_issue()...")
result = await create_issue(
project_key=JIRA_DEFAULT_PROJECT,
summary="[ThirdEye Test] Milestone 17 verification ticket",
description=(
"This ticket was created automatically by the ThirdEye test suite.\n\n"
"It verifies that the Jira API client can create issues successfully.\n\n"
"Safe to close or delete."
),
issue_type="Task",
priority="Low",
labels=["thirdeye", "test", "automated"],
)
assert result.get("ok"), f"create_issue failed: {result.get('error')} — details: {result.get('details')}"
issue_key = result["key"]
print(f" ✅ Created issue: {issue_key}")
print(f" URL: {result['url']}")
# Retrieve it
issue = await get_issue(issue_key)
assert issue["key"] == issue_key
assert "ThirdEye Test" in issue["summary"]
assert issue["status"] in ("To Do", "Open", "Backlog", "New")
print(f" ✅ Retrieved issue: [{issue['key']}] {issue['summary']}")
print(f" Status: {issue['status']} | Priority: {issue['priority']}")
return issue_key
async def test_search_issues(issue_key: str):
"""Test searching issues by JQL."""
from backend.integrations.jira_client import search_issues
from backend.config import JIRA_DEFAULT_PROJECT
print("\nTesting search_issues() via JQL...")
jql = f'project = {JIRA_DEFAULT_PROJECT} AND labels = "thirdeye" AND labels = "test" ORDER BY created DESC'
results = await search_issues(jql, max_results=5)
assert len(results) > 0, f"Expected at least one result for JQL: {jql}"
keys = [r["key"] for r in results]
assert issue_key in keys, f"Newly created issue {issue_key} not found in search results"
print(f" ✅ JQL search returned {len(results)} result(s), including {issue_key}")
async def test_add_comment(issue_key: str):
"""Test adding a comment to an existing issue."""
from backend.integrations.jira_client import add_comment
print("\nTesting add_comment()...")
result = await add_comment(
issue_key,
"ThirdEye test comment — verifying comment API works correctly."
)
assert result.get("ok"), f"add_comment failed: {result.get('error')}"
print(f" ✅ Comment added to {issue_key} (comment id: {result.get('id')})")
async def test_adf_conversion():
"""Test that _text_to_adf produces valid ADF structure."""
from backend.integrations.jira_client import _text_to_adf
print("\nTesting ADF conversion...")
text = "This is paragraph one.\n\nThis is paragraph two.\n\n- Bullet A\n- Bullet B"
adf = _text_to_adf(text)
assert adf["type"] == "doc"
assert adf["version"] == 1
assert len(adf["content"]) >= 2
print(f" ✅ ADF produced {len(adf['content'])} content block(s)")
# Empty text should not crash
adf_empty = _text_to_adf("")
assert adf_empty["type"] == "doc"
print(" ✅ Empty text handled gracefully")
async def main():
print("Running Milestone 17 tests...\n")
await test_config_loaded()
await test_connection()
await test_list_projects()
await test_list_issue_types()
issue_key = await test_create_and_get_issue()
await test_search_issues(issue_key)
await test_add_comment(issue_key)
await test_adf_conversion()
print(f"\n🎉 MILESTONE 17 PASSED — Jira API client working. Test ticket: {issue_key}")
asyncio.run(main())