mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-11 19:47:12 +03:00
121 lines
5.5 KiB
Python
121 lines
5.5 KiB
Python
import importlib.machinery
|
|
import io
|
|
import json
|
|
import pathlib
|
|
from types import SimpleNamespace
|
|
import unittest
|
|
from contextlib import redirect_stderr, redirect_stdout
|
|
|
|
SCRIPT = pathlib.Path(__file__).parents[1] / "scripts" / "forgejo-cli"
|
|
cli = importlib.machinery.SourceFileLoader("forgejo_cli", str(SCRIPT)).load_module()
|
|
|
|
|
|
class CliTests(unittest.TestCase):
|
|
def run_cli(self, args):
|
|
out, err = io.StringIO(), io.StringIO()
|
|
with redirect_stdout(out), redirect_stderr(err):
|
|
try:
|
|
cli.main(args)
|
|
except SystemExit as exc:
|
|
return exc.code, out.getvalue(), err.getvalue()
|
|
return 0, out.getvalue(), err.getvalue()
|
|
|
|
def plan(self, args):
|
|
code, out, err = self.run_cli(["--dry-run", "--json"] + args)
|
|
self.assertEqual(code, 0, err)
|
|
return json.loads(out)
|
|
|
|
def test_help_needs_no_credentials(self):
|
|
self.assertEqual(self.run_cli(["--help"])[0], 0)
|
|
self.assertEqual(self.run_cli(["issue", "--help"])[0], 0)
|
|
|
|
def test_issue_create_plan(self):
|
|
plan = self.plan(["issue", "create", "--owner", "me", "--repo", "x", "--title", "hello"])
|
|
self.assertEqual((plan["method"], plan["path"]), ("POST", "/api/v1/repos/me/x/issues"))
|
|
self.assertEqual(plan["body"]["title"], "hello")
|
|
|
|
def test_mutation_requires_force(self):
|
|
code, _, err = self.run_cli(["repo", "create", "--name", "x"])
|
|
self.assertNotEqual(code, 0)
|
|
self.assertIn("mutation", err)
|
|
|
|
def test_live_request_requires_a_server(self):
|
|
old = cli.DEFAULT_SERVER
|
|
cli.DEFAULT_SERVER = ""
|
|
try:
|
|
code, _, err = self.run_cli(["user", "show"])
|
|
finally:
|
|
cli.DEFAULT_SERVER = old
|
|
self.assertNotEqual(code, 0)
|
|
self.assertIn("No Forgejo server selected", err)
|
|
|
|
def test_api_path_guard_and_plan(self):
|
|
self.assertNotEqual(self.run_cli(["api", "--method", "GET", "--path", "/bad"])[0], 0)
|
|
plan = self.plan(["api", "--method", "PATCH", "--path", "/api/v1/user/settings", "--query", "theme=dark", "--data", '{"language":"en"}'])
|
|
self.assertEqual(plan["query"], {"theme": "dark"})
|
|
self.assertEqual(plan["body"], {"language": "en"})
|
|
|
|
def test_representative_groups(self):
|
|
cases = [
|
|
(["pr", "create", "--owner", "me", "--repo", "x", "--title", "t", "--head", "h", "--base", "main"], "POST", "/api/v1/repos/me/x/pulls"),
|
|
(["release", "create", "--owner", "me", "--repo", "x", "--tag-name", "v2"], "POST", "/api/v1/repos/me/x/releases"),
|
|
(["content", "update", "--owner", "me", "--repo", "x", "--path", "a b.txt", "--content", "eA==", "--sha", "abc"], "PUT", "/api/v1/repos/me/x/contents/a%20b.txt"),
|
|
(["hook", "create", "--owner", "me", "--repo", "x", "--url", "https://hook"], "POST", "/api/v1/repos/me/x/hooks"),
|
|
]
|
|
for args, method, path in cases:
|
|
plan = self.plan(args)
|
|
self.assertEqual((plan["method"], plan["path"]), (method, path))
|
|
|
|
def test_merge_hook_and_release_payloads(self):
|
|
merge = self.plan(["pr", "merge", "--owner", "me", "--repo", "x", "--index", "1", "--style", "squash"])
|
|
self.assertEqual(merge["body"], {"Do": "squash"})
|
|
hook = self.plan(["hook", "create", "--owner", "me", "--repo", "x", "--url", "https://hook", "--events", "push"])
|
|
self.assertEqual(hook["body"]["config"]["url"], "https://hook")
|
|
self.assertEqual(hook["body"]["type"], "forgejo")
|
|
release = self.plan(["release", "create", "--owner", "me", "--repo", "x", "--tag-name", "v2", "--name", "Version 2"])
|
|
self.assertEqual(release["body"], {"tag_name": "v2", "name": "Version 2"})
|
|
|
|
def test_repo_creation_needs_no_owner(self):
|
|
self.assertEqual(self.plan(["repo", "create", "--name", "demo", "--private"])["path"], "/api/v1/user/repos")
|
|
|
|
def test_nested_content_path_and_pagination_plan(self):
|
|
plan = self.plan(["--page", "2", "--limit", "75", "content", "get", "--owner", "me", "--repo", "x", "--path", "dir/a b.txt"])
|
|
self.assertEqual(plan["path"], "/api/v1/repos/me/x/contents/dir/a%20b.txt")
|
|
self.assertEqual(plan["query"], {"page": 2, "limit": 75})
|
|
|
|
def test_form_transport_custom_authorization_and_response_metadata(self):
|
|
class Response:
|
|
status_code = 200
|
|
content = b'{"ok":true}'
|
|
text = '{"ok":true}'
|
|
headers = {"Link": '<https://forge.example/api/v1/user/repos?page=2>; rel="next"', "X-Total-Count": "51"}
|
|
|
|
def json(self):
|
|
return {"ok": True}
|
|
|
|
class Requests:
|
|
RequestException = Exception
|
|
call = None
|
|
|
|
@classmethod
|
|
def request(cls, *args, **kwargs):
|
|
cls.call = (args, kwargs)
|
|
return Response()
|
|
|
|
original = cli.requests
|
|
cli.requests = Requests
|
|
try:
|
|
args = SimpleNamespace(server="https://forge.example", dry_run=False, verbose=False, user=False)
|
|
result = cli.Client(args).request(
|
|
"POST", "/api/v1/repos/me/x/releases/1/assets", headers={"Authorization": "Basic test"},
|
|
form={"external_url": "https://example.invalid/file"}, include_response=True)
|
|
finally:
|
|
cli.requests = original
|
|
self.assertEqual(Requests.call[1]["data"], {"external_url": "https://example.invalid/file"})
|
|
self.assertEqual(result["data"], {"ok": True})
|
|
self.assertEqual(result["headers"]["x-total-count"], "51")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|