test_api_hub.py•6.04 kB
from typing import Any, List
import pytest
from jentic.lib.agent_runtime.api_hub import JenticAPIClient
# Assuming models are accessible via the path below
# Split imports to potentially resolve import errors
from jentic.lib.models import AssociatedFiles, FileEntry, FileId, WorkflowEntry
# Minimal mock models for testing
class MockAssociatedFiles(AssociatedFiles):
arazzo: List[FileId] = [] # Use FileId
open_api: List[FileId] = [] # Use FileId
class MockFileEntry(FileEntry):
pass
class MockWorkflowEntry(WorkflowEntry):
# Override fields that expect complex types if not needed directly
api_references: List[Any] = [] # Keep it simple for this test
files: MockAssociatedFiles = MockAssociatedFiles() # Use updated mock with defaults
@pytest.fixture
def api_client() -> JenticAPIClient:
"""Fixture to create a JenticAPIClient instance for testing."""
# No need for real URLs or API keys for testing this method
return JenticAPIClient()
# --- Test Cases --- #
def test_build_source_descriptions_happy_path(api_client):
"""Test URL-based matching of Arazzo OpenAPI sources to OpenAPI files."""
workflow_entry = MockWorkflowEntry(
workflow_id="wf1",
workflow_uuid="uuid1",
name="Test Workflow",
files=MockAssociatedFiles(open_api=[FileId(id="file1_id"), FileId(id="file2_id")]),
api_references=[],
)
# Content for the first available file (file1_id)
content1 = {"openapi": "3.0", "info": {"title": "API One - First File"}}
# Content for the second file (file2_id)
content2 = {"openapi": "3.0", "info": {"title": "API Two - Second File"}}
all_openapi_files = {
"file1_id": MockFileEntry(
id="file1_id",
type="open_api",
filename="api_one.json",
content=content1,
source_path="./specs/api_one.json",
),
"file2_id": MockFileEntry(
id="file2_id",
type="open_api",
filename="api_two.yaml",
content=content2,
source_path="./specs/api_two.yaml",
),
}
# Sources with URLs that match the filenames
first_source_name = "ApiOneSourceFirst"
second_source_name = "ApiTwoSourceSecond"
arazzo_doc = {
"sourceDescriptions": [
{"name": first_source_name, "url": "./specs/api_one.json", "type": "openapi"},
{"name": second_source_name, "url": "./specs/api_two.yaml", "type": "openapi"},
]
}
result = api_client._build_source_descriptions(
workflow_entry=workflow_entry,
all_openapi_files=all_openapi_files,
arazzo_doc=arazzo_doc,
)
# Each source should map to the file with matching filename in the URL
assert len(result) == 2
assert first_source_name in result
assert second_source_name in result
assert result[first_source_name] == content1
assert result[second_source_name] == content2
def test_build_source_descriptions_no_arazzo_sources(api_client):
"""Test when Arazzo doc has no sourceDescriptions. Should return empty dict."""
workflow_entry = MockWorkflowEntry(
workflow_id="wf1",
workflow_uuid="uuid1",
name="Test",
files=MockAssociatedFiles(open_api=[FileId(id="file1_id")]),
)
all_openapi_files = {
"file1_id": MockFileEntry(id="file1_id", type="open_api", filename="./api.json", content={})
}
arazzo_doc = {} # No sourceDescriptions
result = api_client._build_source_descriptions(workflow_entry, all_openapi_files, arazzo_doc)
assert result == {}
def test_build_source_descriptions_empty_arazzo_sources(api_client):
"""Test when Arazzo doc has empty sourceDescriptions list. Should return empty dict."""
workflow_entry = MockWorkflowEntry(
workflow_id="wf1",
workflow_uuid="uuid1",
name="Test",
files=MockAssociatedFiles(open_api=[FileId(id="file1_id")]),
)
all_openapi_files = {
"file1_id": MockFileEntry(id="file1_id", type="open_api", filename="./api.json", content={})
}
arazzo_doc = {"sourceDescriptions": []}
result = api_client._build_source_descriptions(workflow_entry, all_openapi_files, arazzo_doc)
assert result == {}
def test_build_source_descriptions_missing_file_in_response(api_client):
"""Test when there's no URL match but a default content is available.
Should use the default content (first available file content) as fallback.
"""
# Workflow references 'missing_id' first, then 'file2_id'
workflow_entry = MockWorkflowEntry(
workflow_id="wf1",
workflow_uuid="uuid1",
name="Test",
# The actual file IDs listed here ('missing_id', 'file2_id') are less critical
# than what's in `all_openapi_files` for this specific test of matching logic.
files=MockAssociatedFiles(open_api=[FileId(id="file2_id")]),
)
# Only the second file's content is available in the response
content2 = {"info": "API Two - Content"}
all_openapi_files = {
"file2_id": MockFileEntry(
id="file2_id",
type="open_api",
filename="api_two.json",
content=content2,
source_path="./actual_path/api_two.json",
)
# 'missing_id' is not present here, which is fine for this test's purpose.
}
# Arazzo source with URL that doesn't match any source_path
arazzo_source_name = "ApiSourceNoMatch"
arazzo_doc = {
"sourceDescriptions": [
{"name": arazzo_source_name, "url": "./specs/no_match.json", "type": "openapi"}
]
}
result = api_client._build_source_descriptions(
workflow_entry=workflow_entry,
all_openapi_files=all_openapi_files,
arazzo_doc=arazzo_doc,
)
# With source_path matching, if no exact match is found, the source is not included.
# The previous fallback logic is removed.
assert len(result) == 0
assert arazzo_source_name not in result