mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-27 04:20:09 +00:00
162 lines
5.2 KiB
Python
162 lines
5.2 KiB
Python
"""Tests for repo: repositories on Hanzo git and their GitHub links, over /v1/sync.
|
|
|
|
The tool runs against a real HanzoCloud whose httpx transport answers as
|
|
api.hanzo.ai does, so each test reads the request that went out and what the
|
|
tool made of the answer. The live test is skipped when no Hanzo credential
|
|
resolves.
|
|
"""
|
|
|
|
import asyncio
|
|
|
|
import httpx
|
|
import pytest
|
|
from hanzo_tools.core import HanzoCloud
|
|
|
|
from hanzo_tools.vcs import TOOLS, RepoTool
|
|
|
|
UI = {
|
|
"id": "sync_713c4bb98167c774be9d6d01a35ac69f",
|
|
"kind": "git",
|
|
"source": {"provider": "github", "locator": "https://github.com/hanzoai/ui.git"},
|
|
"target": {"provider": "hanzo-git", "locator": "ui"},
|
|
"direction": "pull",
|
|
"trigger": "poll",
|
|
"createdAt": "2026-07-22T10:42:41Z",
|
|
"updatedAt": "2026-10-04T23:06:13Z",
|
|
"scope": "repo",
|
|
"native": {
|
|
"url": "https://git.hanzo.ai/hanzoai/hanzoai_ui",
|
|
"clone": "https://git.hanzo.ai/hanzoai/hanzoai_ui.git",
|
|
"branch": "main",
|
|
"status": "synced",
|
|
"sizeBytes": 131655680,
|
|
},
|
|
}
|
|
ARC = {
|
|
"id": "sync_0b1d",
|
|
"source": {"provider": "github", "locator": "https://github.com/hanzoai/arc.git"},
|
|
"target": {"provider": "hanzo-git", "locator": "arc"},
|
|
"direction": "both",
|
|
"trigger": "webhook",
|
|
"scope": "repo",
|
|
"native": {
|
|
"status": "conflict",
|
|
"branch": "main",
|
|
"url": "https://git.hanzo.ai/hanzoai/hanzoai_arc",
|
|
},
|
|
}
|
|
ACCOUNT = {
|
|
"id": "sync_acct",
|
|
"source": {"provider": "github", "locator": "https://github.com/hanzoai"},
|
|
"direction": "pull",
|
|
"trigger": "webhook",
|
|
"scope": "account",
|
|
}
|
|
|
|
|
|
class Gateway:
|
|
def __init__(self):
|
|
self.seen: list[httpx.Request] = []
|
|
|
|
def __call__(self, request: httpx.Request) -> httpx.Response:
|
|
self.seen.append(request)
|
|
path = request.url.path
|
|
if request.method == "GET" and path == "/v1/sync":
|
|
return httpx.Response(200, json={"data": [UI, ARC, ACCOUNT]})
|
|
if request.method == "POST" and path == f"/v1/sync/{UI['id']}/run":
|
|
return httpx.Response(202, json={"queued": True, "id": UI["id"]})
|
|
return httpx.Response(404, json={"error": "no such sync"})
|
|
|
|
|
|
def wired():
|
|
gw = Gateway()
|
|
cloud = HanzoCloud(base_url="https://api.hanzo.ai", key="sk-test")
|
|
cloud._client = httpx.AsyncClient(
|
|
base_url="https://api.hanzo.ai", transport=httpx.MockTransport(gw)
|
|
)
|
|
tool = RepoTool()
|
|
tool._cloud = cloud
|
|
return tool, gw
|
|
|
|
|
|
def run(tool, **kw):
|
|
async def call():
|
|
try:
|
|
return await tool.call(None, **kw)
|
|
finally:
|
|
if tool._cloud is not None:
|
|
await tool._cloud.aclose()
|
|
|
|
return asyncio.run(call())
|
|
|
|
|
|
def test_the_package_carries_git_and_repo():
|
|
assert [t.name for t in TOOLS] == ["git", "repo"]
|
|
assert RepoTool.DEFAULT_ACTION == "list"
|
|
|
|
|
|
def test_list_reads_every_link_and_where_it_stands():
|
|
tool, gw = wired()
|
|
env = run(tool)
|
|
assert env["ok"], env
|
|
assert (gw.seen[-1].method, gw.seen[-1].url.path) == ("GET", "/v1/sync")
|
|
assert gw.seen[-1].headers["authorization"] == "Bearer sk-test"
|
|
assert env["data"]["count"] == 3
|
|
assert env["data"]["repos"][0] == {
|
|
"id": "sync_713c4bb98167c774be9d6d01a35ac69f",
|
|
"scope": "repo",
|
|
"source": "https://github.com/hanzoai/ui.git",
|
|
"target": "ui",
|
|
"direction": "pull",
|
|
"trigger": "poll",
|
|
"status": "synced",
|
|
"branch": "main",
|
|
"url": "https://git.hanzo.ai/hanzoai/hanzoai_ui",
|
|
"synced_at": "2026-10-04T23:06:13Z",
|
|
}
|
|
# An account link has no native copy of its own: its standing reads None.
|
|
assert (env["data"]["repos"][2]["scope"], env["data"]["repos"][2]["status"]) == (
|
|
"account",
|
|
None,
|
|
)
|
|
|
|
|
|
def test_list_narrows_by_words_and_by_status():
|
|
tool, _ = wired()
|
|
assert [r["id"] for r in run(tool, action="list", search="hanzoai arc")["data"]["repos"]] == [
|
|
"sync_0b1d"
|
|
]
|
|
tool, _ = wired()
|
|
assert [r["id"] for r in run(tool, action="list", status="conflict")["data"]["repos"]] == [
|
|
"sync_0b1d"
|
|
]
|
|
|
|
|
|
def test_sync_runs_one_link_now():
|
|
tool, gw = wired()
|
|
env = run(tool, action="sync", id=UI["id"])
|
|
assert env["ok"], env
|
|
assert (gw.seen[-1].method, gw.seen[-1].url.path) == ("POST", f"/v1/sync/{UI['id']}/run")
|
|
assert env["data"] == {"queued": True, "id": UI["id"]}
|
|
|
|
|
|
def test_sync_needs_an_id_and_names_one_it_does_not_know():
|
|
tool, gw = wired()
|
|
env = run(tool, action="sync")
|
|
assert not env["ok"] and "id required" in env["error"]["message"]
|
|
assert gw.seen == []
|
|
tool, gw = wired()
|
|
env = run(tool, action="sync", id="sync_nope/../x")
|
|
assert not env["ok"] and env["error"]["code"] == "NOT_FOUND"
|
|
assert gw.seen[-1].url.raw_path.decode() == "/v1/sync/sync_nope%2F..%2Fx/run"
|
|
|
|
|
|
@pytest.mark.skipif(not HanzoCloud().configured(), reason="no Hanzo credential")
|
|
def test_live_list_reads_the_org_s_links():
|
|
env = run(RepoTool())
|
|
assert env["ok"], env
|
|
repos = env["data"]["repos"]
|
|
assert env["data"]["count"] == len(repos)
|
|
assert all(r["id"].startswith("sync_") for r in repos)
|
|
assert all(r["status"] in (None, "synced", "conflict", "pending", "paused") for r in repos)
|