adaptsapi 0.1.7__tar.gz → 0.1.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {adaptsapi-0.1.7/src/adaptsapi.egg-info → adaptsapi-0.1.9}/PKG-INFO +3 -3
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/pyproject.toml +4 -4
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/setup.py +1 -1
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/src/adaptsapi/cli.py +9 -4
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/src/adaptsapi/config.py +6 -1
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/src/adaptsapi/generate_docs.py +25 -17
- {adaptsapi-0.1.7 → adaptsapi-0.1.9/src/adaptsapi.egg-info}/PKG-INFO +3 -3
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/tests/test_cli.py +110 -87
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/tests/test_config.py +117 -109
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/tests/test_generate_docs.py +90 -74
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/LICENSE +0 -0
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/README.md +0 -0
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/setup.cfg +0 -0
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/src/adaptsapi/__init__.py +0 -0
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/src/adaptsapi.egg-info/SOURCES.txt +0 -0
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/src/adaptsapi.egg-info/dependency_links.txt +0 -0
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/src/adaptsapi.egg-info/entry_points.txt +0 -0
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/src/adaptsapi.egg-info/requires.txt +0 -0
- {adaptsapi-0.1.7 → adaptsapi-0.1.9}/src/adaptsapi.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: adaptsapi
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: CLI to enqueue triggers via internal API Gateway → SNS
|
|
5
5
|
Home-page: https://github.com/adaptsai/adaptsapi
|
|
6
6
|
Author: adapts
|
|
7
7
|
Author-email: "VerifyAI Inc." <dev@adapts.ai>
|
|
8
8
|
License-Expression: LicenseRef-Proprietary
|
|
9
|
-
Project-URL: Homepage, https://
|
|
10
|
-
Project-URL: Source, https://github.com/
|
|
9
|
+
Project-URL: Homepage, https://adapts.ai/
|
|
10
|
+
Project-URL: Source, https://github.com/AdaptsAI/adapts_api_client
|
|
11
11
|
Keywords: adapts,api,cli,sns
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "adaptsapi"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.9"
|
|
8
8
|
description = "CLI to enqueue triggers via internal API Gateway → SNS"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
|
|
@@ -27,8 +27,8 @@ classifiers = [
|
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
[project.urls]
|
|
30
|
-
"Homepage" = "https://
|
|
31
|
-
"Source" = "https://github.com/
|
|
30
|
+
"Homepage" = "https://adapts.ai/"
|
|
31
|
+
"Source" = "https://github.com/AdaptsAI/adapts_api_client"
|
|
32
32
|
|
|
33
33
|
[project.scripts]
|
|
34
34
|
adaptsapi = "adaptsapi.cli:main"
|
|
@@ -62,7 +62,7 @@ ensure_newline_before_comments = true
|
|
|
62
62
|
skip = [".venv", "build", "dist", "*.egg-info"]
|
|
63
63
|
|
|
64
64
|
[tool.mypy]
|
|
65
|
-
python_version = "
|
|
65
|
+
python_version = "0.1.8"
|
|
66
66
|
warn_return_any = true
|
|
67
67
|
warn_unused_configs = true
|
|
68
68
|
disallow_untyped_defs = true
|
|
@@ -5,6 +5,7 @@ import json
|
|
|
5
5
|
from adaptsapi.generate_docs import post
|
|
6
6
|
from adaptsapi.config import load_token, load_default_endpoint
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
def main():
|
|
9
10
|
default_ep = load_default_endpoint()
|
|
10
11
|
parser = argparse.ArgumentParser(prog="adaptsapi")
|
|
@@ -12,10 +13,13 @@ def main():
|
|
|
12
13
|
"--endpoint",
|
|
13
14
|
default=default_ep,
|
|
14
15
|
required=(default_ep is None),
|
|
15
|
-
help="Full URL of the API endpoint (default from ./config.json)"
|
|
16
|
+
help="Full URL of the API endpoint (default from ./config.json)",
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--payload-file",
|
|
20
|
+
type=argparse.FileType("r"),
|
|
21
|
+
help="Path to JSON payload file (overrides inline --data)",
|
|
16
22
|
)
|
|
17
|
-
parser.add_argument("--payload-file", type=argparse.FileType('r'),
|
|
18
|
-
help="Path to JSON payload file (overrides inline --data)")
|
|
19
23
|
parser.add_argument("--data", help="Inline JSON payload string")
|
|
20
24
|
parser.add_argument("--timeout", type=int, default=30)
|
|
21
25
|
args = parser.parse_args()
|
|
@@ -36,5 +40,6 @@ def main():
|
|
|
36
40
|
sys.exit(resp.status_code)
|
|
37
41
|
print(resp.text)
|
|
38
42
|
|
|
43
|
+
|
|
39
44
|
if __name__ == "__main__":
|
|
40
|
-
main()
|
|
45
|
+
main()
|
|
@@ -6,10 +6,13 @@ from pathlib import Path
|
|
|
6
6
|
# Point at config.json in whatever directory you run the CLI from
|
|
7
7
|
CONFIG_PATH = Path.cwd() / "config.json"
|
|
8
8
|
|
|
9
|
+
|
|
9
10
|
class ConfigError(Exception):
|
|
10
11
|
"""Raised when no token can be found or loaded."""
|
|
12
|
+
|
|
11
13
|
pass
|
|
12
14
|
|
|
15
|
+
|
|
13
16
|
def load_token() -> str:
|
|
14
17
|
# 1) ENV override
|
|
15
18
|
token = os.getenv("ADAPTS_API_TOKEN")
|
|
@@ -35,6 +38,7 @@ def load_token() -> str:
|
|
|
35
38
|
print(f"Saved token to {CONFIG_PATH}")
|
|
36
39
|
return token
|
|
37
40
|
|
|
41
|
+
|
|
38
42
|
def load_default_endpoint() -> str | None:
|
|
39
43
|
"""Return `endpoint` from ./config.json if present, else None."""
|
|
40
44
|
if CONFIG_PATH.exists():
|
|
@@ -45,6 +49,7 @@ def load_default_endpoint() -> str | None:
|
|
|
45
49
|
return None
|
|
46
50
|
return None
|
|
47
51
|
|
|
52
|
+
|
|
48
53
|
def _ensure_config_dir_and_write(obj: dict) -> None:
|
|
49
54
|
"""Helper to merge+write config.json in cwd."""
|
|
50
55
|
# read existing if any
|
|
@@ -55,4 +60,4 @@ def _ensure_config_dir_and_write(obj: dict) -> None:
|
|
|
55
60
|
except json.JSONDecodeError:
|
|
56
61
|
existing = {}
|
|
57
62
|
merged = {**existing, **obj}
|
|
58
|
-
CONFIG_PATH.write_text(json.dumps(merged, indent=2))
|
|
63
|
+
CONFIG_PATH.write_text(json.dumps(merged, indent=2))
|
|
@@ -3,19 +3,20 @@ import requests
|
|
|
3
3
|
from typing import Dict, Any
|
|
4
4
|
from datetime import date
|
|
5
5
|
import uuid
|
|
6
|
+
import os
|
|
6
7
|
|
|
7
8
|
# regex for a very basic email validation
|
|
8
9
|
_EMAIL_RE = re.compile(r"^[^@]+@[^@]+\.[^@]+$")
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class PayloadValidationError(ValueError):
|
|
12
|
-
"""Raised when the payload is missing required fields or
|
|
13
|
+
"""Raised when the payload is missing required fields or
|
|
13
14
|
has invalid values."""
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def _validate_payload(payload: Dict[str, Any]) -> None:
|
|
17
18
|
# Top‐level required fields
|
|
18
|
-
required = {
|
|
19
|
+
required = {
|
|
19
20
|
"email_address": str,
|
|
20
21
|
"user_name": str,
|
|
21
22
|
"repo_object": dict,
|
|
@@ -62,18 +63,16 @@ def _validate_payload(payload: Dict[str, Any]) -> None:
|
|
|
62
63
|
if field not in repo:
|
|
63
64
|
raise PayloadValidationError(f"Missing repo_object.{field}")
|
|
64
65
|
if not isinstance(repo[field], typ):
|
|
65
|
-
raise PayloadValidationError(
|
|
66
|
-
f"repo_object.{field} must be {typ.__name__}"
|
|
67
|
-
)
|
|
66
|
+
raise PayloadValidationError(f"repo_object.{field} must be {typ.__name__}")
|
|
68
67
|
|
|
69
68
|
# optional loop
|
|
70
69
|
for field, typ in optional_repo_fields.items():
|
|
71
|
-
if
|
|
72
|
-
|
|
70
|
+
if (
|
|
71
|
+
field in repo
|
|
72
|
+
and repo[field] is not None
|
|
73
|
+
and not isinstance(repo[field], typ)
|
|
73
74
|
):
|
|
74
|
-
raise PayloadValidationError(
|
|
75
|
-
f"repo_object.{field} must be {typ.__name__}"
|
|
76
|
-
)
|
|
75
|
+
raise PayloadValidationError(f"repo_object.{field} must be {typ.__name__}")
|
|
77
76
|
|
|
78
77
|
# you can add more checks here (URL validation, branch name patterns, etc.)
|
|
79
78
|
|
|
@@ -100,13 +99,12 @@ def _populate_metadata(payload: Dict[str, Any]) -> None:
|
|
|
100
99
|
payload["request_id"] = str(uuid.uuid4())
|
|
101
100
|
payload["request_type"] = "code_to_wiki"
|
|
102
101
|
payload["status"] = "pending"
|
|
102
|
+
if not payload.get("service_name"):
|
|
103
|
+
payload["service_name"] = payload["repo_object"]["repository_name"]
|
|
103
104
|
|
|
104
105
|
|
|
105
106
|
def post(
|
|
106
|
-
endpoint: str,
|
|
107
|
-
token: str,
|
|
108
|
-
payload: Dict[str, Any],
|
|
109
|
-
timeout: int = 30
|
|
107
|
+
endpoint: str, token: str, payload: Dict[str, Any], timeout: int = 30
|
|
110
108
|
) -> requests.Response:
|
|
111
109
|
"""
|
|
112
110
|
Send a POST with validated payload and autopopulated metadata.
|
|
@@ -126,10 +124,20 @@ def post(
|
|
|
126
124
|
"Content-Type": "application/json",
|
|
127
125
|
}
|
|
128
126
|
|
|
127
|
+
try:
|
|
128
|
+
ip_address = requests.get("https://api.ipify.org").text
|
|
129
|
+
geo = requests.get(f"https://ipapi.co/{ip_address}/json/").json()
|
|
130
|
+
country = geo.get("country_name", "")
|
|
131
|
+
if ip_address and ip_address != "":
|
|
132
|
+
headers["x-ip"] = ip_address
|
|
133
|
+
if country and country != "":
|
|
134
|
+
headers["x-country"] = country
|
|
135
|
+
except Exception as e:
|
|
136
|
+
pass
|
|
137
|
+
|
|
129
138
|
print("payload:", payload)
|
|
130
139
|
print("testing")
|
|
131
|
-
response = requests.post(endpoint, json=payload, headers=headers,
|
|
132
|
-
timeout=timeout)
|
|
140
|
+
response = requests.post(endpoint, json=payload, headers=headers, timeout=timeout)
|
|
133
141
|
print("response:", response.json())
|
|
134
142
|
print("testing")
|
|
135
|
-
return response
|
|
143
|
+
return response
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: adaptsapi
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: CLI to enqueue triggers via internal API Gateway → SNS
|
|
5
5
|
Home-page: https://github.com/adaptsai/adaptsapi
|
|
6
6
|
Author: adapts
|
|
7
7
|
Author-email: "VerifyAI Inc." <dev@adapts.ai>
|
|
8
8
|
License-Expression: LicenseRef-Proprietary
|
|
9
|
-
Project-URL: Homepage, https://
|
|
10
|
-
Project-URL: Source, https://github.com/
|
|
9
|
+
Project-URL: Homepage, https://adapts.ai/
|
|
10
|
+
Project-URL: Source, https://github.com/AdaptsAI/adapts_api_client
|
|
11
11
|
Keywords: adapts,api,cli,sns
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
@@ -9,25 +9,25 @@ from adaptsapi.cli import main
|
|
|
9
9
|
|
|
10
10
|
class TestCLIArgumentParsing:
|
|
11
11
|
"""Test CLI argument parsing functionality"""
|
|
12
|
-
|
|
13
|
-
@patch(
|
|
14
|
-
@patch(
|
|
15
|
-
@patch(
|
|
12
|
+
|
|
13
|
+
@patch("adaptsapi.cli.load_default_endpoint")
|
|
14
|
+
@patch("adaptsapi.cli.load_token")
|
|
15
|
+
@patch("adaptsapi.cli.post")
|
|
16
16
|
def test_basic_cli_with_data(self, mock_post, mock_load_token, mock_load_endpoint):
|
|
17
17
|
"""Test CLI with inline JSON data"""
|
|
18
18
|
mock_load_endpoint.return_value = "https://api.example.com/test"
|
|
19
19
|
mock_load_token.return_value = "test-token"
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
mock_response = Mock()
|
|
22
22
|
mock_response.status_code = 200
|
|
23
23
|
mock_response.text = '{"status": "success"}'
|
|
24
24
|
mock_post.return_value = mock_response
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
# Test with inline JSON data
|
|
27
|
-
with patch(
|
|
28
|
-
with patch(
|
|
27
|
+
with patch("sys.argv", ["adaptsapi", "--data", '{"test": "data"}']):
|
|
28
|
+
with patch("sys.stdout", new=StringIO()) as mock_stdout:
|
|
29
29
|
main()
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Verify post was called correctly
|
|
32
32
|
mock_post.assert_called_once()
|
|
33
33
|
call_args = mock_post.call_args
|
|
@@ -35,160 +35,183 @@ class TestCLIArgumentParsing:
|
|
|
35
35
|
assert call_args[0][1] == "test-token"
|
|
36
36
|
assert call_args[0][2] == {"test": "data"}
|
|
37
37
|
assert call_args[1]["timeout"] == 30 # default timeout
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
# Verify output
|
|
40
40
|
assert mock_stdout.getvalue().strip() == '{"status": "success"}'
|
|
41
|
-
|
|
42
|
-
@patch(
|
|
43
|
-
@patch(
|
|
44
|
-
@patch(
|
|
45
|
-
def test_cli_with_payload_file(
|
|
41
|
+
|
|
42
|
+
@patch("adaptsapi.cli.load_default_endpoint")
|
|
43
|
+
@patch("adaptsapi.cli.load_token")
|
|
44
|
+
@patch("adaptsapi.cli.post")
|
|
45
|
+
def test_cli_with_payload_file(
|
|
46
|
+
self, mock_post, mock_load_token, mock_load_endpoint
|
|
47
|
+
):
|
|
46
48
|
"""Test CLI with payload file"""
|
|
47
49
|
mock_load_endpoint.return_value = "https://api.example.com/test"
|
|
48
50
|
mock_load_token.return_value = "test-token"
|
|
49
|
-
|
|
51
|
+
|
|
50
52
|
mock_response = Mock()
|
|
51
53
|
mock_response.status_code = 200
|
|
52
54
|
mock_response.text = '{"status": "success"}'
|
|
53
55
|
mock_post.return_value = mock_response
|
|
54
|
-
|
|
56
|
+
|
|
55
57
|
# Create a temporary payload file
|
|
56
|
-
with tempfile.NamedTemporaryFile(mode=
|
|
58
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
57
59
|
json.dump({"test": "data", "nested": {"value": 123}}, f)
|
|
58
60
|
temp_file_path = f.name
|
|
59
|
-
|
|
61
|
+
|
|
60
62
|
try:
|
|
61
|
-
with patch(
|
|
62
|
-
with patch(
|
|
63
|
+
with patch("sys.argv", ["adaptsapi", "--payload-file", temp_file_path]):
|
|
64
|
+
with patch("sys.stdout", new=StringIO()) as mock_stdout:
|
|
63
65
|
main()
|
|
64
|
-
|
|
66
|
+
|
|
65
67
|
# Verify post was called with correct payload
|
|
66
68
|
mock_post.assert_called_once()
|
|
67
69
|
call_args = mock_post.call_args
|
|
68
70
|
assert call_args[0][2] == {"test": "data", "nested": {"value": 123}}
|
|
69
71
|
finally:
|
|
70
72
|
os.unlink(temp_file_path)
|
|
71
|
-
|
|
72
|
-
@patch(
|
|
73
|
-
@patch(
|
|
74
|
-
@patch(
|
|
75
|
-
def test_cli_with_custom_endpoint(
|
|
73
|
+
|
|
74
|
+
@patch("adaptsapi.cli.load_default_endpoint")
|
|
75
|
+
@patch("adaptsapi.cli.load_token")
|
|
76
|
+
@patch("adaptsapi.cli.post")
|
|
77
|
+
def test_cli_with_custom_endpoint(
|
|
78
|
+
self, mock_post, mock_load_token, mock_load_endpoint
|
|
79
|
+
):
|
|
76
80
|
"""Test CLI with custom endpoint"""
|
|
77
81
|
mock_load_endpoint.return_value = None # No default endpoint
|
|
78
82
|
mock_load_token.return_value = "test-token"
|
|
79
|
-
|
|
83
|
+
|
|
80
84
|
mock_response = Mock()
|
|
81
85
|
mock_response.status_code = 200
|
|
82
86
|
mock_response.text = '{"status": "success"}'
|
|
83
87
|
mock_post.return_value = mock_response
|
|
84
|
-
|
|
85
|
-
with patch(
|
|
86
|
-
|
|
88
|
+
|
|
89
|
+
with patch(
|
|
90
|
+
"sys.argv",
|
|
91
|
+
[
|
|
92
|
+
"adaptsapi",
|
|
93
|
+
"--endpoint",
|
|
94
|
+
"https://custom.api.com",
|
|
95
|
+
"--data",
|
|
96
|
+
'{"test": "data"}',
|
|
97
|
+
],
|
|
98
|
+
):
|
|
99
|
+
with patch("sys.stdout", new=StringIO()):
|
|
87
100
|
main()
|
|
88
|
-
|
|
101
|
+
|
|
89
102
|
# Verify custom endpoint was used
|
|
90
103
|
mock_post.assert_called_once()
|
|
91
104
|
call_args = mock_post.call_args
|
|
92
105
|
assert call_args[0][0] == "https://custom.api.com"
|
|
93
|
-
|
|
94
|
-
@patch(
|
|
95
|
-
@patch(
|
|
96
|
-
@patch(
|
|
97
|
-
def test_cli_with_custom_timeout(
|
|
106
|
+
|
|
107
|
+
@patch("adaptsapi.cli.load_default_endpoint")
|
|
108
|
+
@patch("adaptsapi.cli.load_token")
|
|
109
|
+
@patch("adaptsapi.cli.post")
|
|
110
|
+
def test_cli_with_custom_timeout(
|
|
111
|
+
self, mock_post, mock_load_token, mock_load_endpoint
|
|
112
|
+
):
|
|
98
113
|
"""Test CLI with custom timeout"""
|
|
99
114
|
mock_load_endpoint.return_value = "https://api.example.com/test"
|
|
100
115
|
mock_load_token.return_value = "test-token"
|
|
101
|
-
|
|
116
|
+
|
|
102
117
|
mock_response = Mock()
|
|
103
118
|
mock_response.status_code = 200
|
|
104
119
|
mock_response.text = '{"status": "success"}'
|
|
105
120
|
mock_post.return_value = mock_response
|
|
106
|
-
|
|
107
|
-
with patch(
|
|
108
|
-
|
|
121
|
+
|
|
122
|
+
with patch(
|
|
123
|
+
"sys.argv", ["adaptsapi", "--timeout", "60", "--data", '{"test": "data"}']
|
|
124
|
+
):
|
|
125
|
+
with patch("sys.stdout", new=StringIO()):
|
|
109
126
|
main()
|
|
110
|
-
|
|
127
|
+
|
|
111
128
|
# Verify custom timeout was used
|
|
112
129
|
mock_post.assert_called_once()
|
|
113
130
|
call_args = mock_post.call_args
|
|
114
131
|
assert call_args[1]["timeout"] == 60
|
|
115
|
-
|
|
116
|
-
@patch(
|
|
117
|
-
@patch(
|
|
132
|
+
|
|
133
|
+
@patch("adaptsapi.cli.load_default_endpoint")
|
|
134
|
+
@patch("adaptsapi.cli.load_token")
|
|
118
135
|
def test_cli_missing_payload(self, mock_load_token, mock_load_endpoint):
|
|
119
136
|
"""Test CLI error when no payload is provided"""
|
|
120
137
|
mock_load_endpoint.return_value = "https://api.example.com/test"
|
|
121
138
|
mock_load_token.return_value = "test-token"
|
|
122
|
-
|
|
123
|
-
with patch(
|
|
124
|
-
with patch(
|
|
139
|
+
|
|
140
|
+
with patch("sys.argv", ["adaptsapi"]):
|
|
141
|
+
with patch("sys.stderr", new=StringIO()) as mock_stderr:
|
|
125
142
|
with pytest.raises(SystemExit) as exc_info:
|
|
126
143
|
main()
|
|
127
|
-
|
|
144
|
+
|
|
128
145
|
assert exc_info.value.code == 1
|
|
129
146
|
assert "Error: must specify --data or --payload-file" in mock_stderr.getvalue()
|
|
130
|
-
|
|
131
|
-
@patch(
|
|
132
|
-
@patch(
|
|
133
|
-
@patch(
|
|
134
|
-
def test_cli_api_error_response(
|
|
147
|
+
|
|
148
|
+
@patch("adaptsapi.cli.load_default_endpoint")
|
|
149
|
+
@patch("adaptsapi.cli.load_token")
|
|
150
|
+
@patch("adaptsapi.cli.post")
|
|
151
|
+
def test_cli_api_error_response(
|
|
152
|
+
self, mock_post, mock_load_token, mock_load_endpoint
|
|
153
|
+
):
|
|
135
154
|
"""Test CLI handling of API error responses"""
|
|
136
155
|
mock_load_endpoint.return_value = "https://api.example.com/test"
|
|
137
156
|
mock_load_token.return_value = "test-token"
|
|
138
|
-
|
|
157
|
+
|
|
139
158
|
mock_response = Mock()
|
|
140
159
|
mock_response.status_code = 400
|
|
141
160
|
mock_response.text = '{"error": "Bad Request"}'
|
|
142
161
|
mock_post.return_value = mock_response
|
|
143
|
-
|
|
144
|
-
with patch(
|
|
145
|
-
with patch(
|
|
162
|
+
|
|
163
|
+
with patch("sys.argv", ["adaptsapi", "--data", '{"test": "data"}']):
|
|
164
|
+
with patch("sys.stderr", new=StringIO()) as mock_stderr:
|
|
146
165
|
with pytest.raises(SystemExit) as exc_info:
|
|
147
166
|
main()
|
|
148
|
-
|
|
167
|
+
|
|
149
168
|
assert exc_info.value.code == 400
|
|
150
|
-
assert
|
|
151
|
-
|
|
152
|
-
@patch(
|
|
153
|
-
@patch(
|
|
154
|
-
@patch(
|
|
155
|
-
def test_cli_invalid_json_data(
|
|
169
|
+
assert 'Error 400: {"error": "Bad Request"}' in mock_stderr.getvalue()
|
|
170
|
+
|
|
171
|
+
@patch("adaptsapi.cli.load_default_endpoint")
|
|
172
|
+
@patch("adaptsapi.cli.load_token")
|
|
173
|
+
@patch("adaptsapi.cli.post")
|
|
174
|
+
def test_cli_invalid_json_data(
|
|
175
|
+
self, mock_post, mock_load_token, mock_load_endpoint
|
|
176
|
+
):
|
|
156
177
|
"""Test CLI error handling with invalid JSON data"""
|
|
157
178
|
mock_load_endpoint.return_value = "https://api.example.com/test"
|
|
158
179
|
mock_load_token.return_value = "test-token"
|
|
159
|
-
|
|
160
|
-
with patch(
|
|
180
|
+
|
|
181
|
+
with patch("sys.argv", ["adaptsapi", "--data", "invalid json"]):
|
|
161
182
|
with pytest.raises(json.JSONDecodeError):
|
|
162
183
|
main()
|
|
163
|
-
|
|
164
|
-
@patch(
|
|
165
|
-
@patch(
|
|
184
|
+
|
|
185
|
+
@patch("adaptsapi.cli.load_default_endpoint")
|
|
186
|
+
@patch("adaptsapi.cli.load_token")
|
|
166
187
|
def test_cli_missing_endpoint_no_default(self, mock_load_token, mock_load_endpoint):
|
|
167
188
|
"""Test CLI error when no endpoint is provided and no default exists"""
|
|
168
189
|
mock_load_endpoint.return_value = None
|
|
169
190
|
mock_load_token.return_value = "test-token"
|
|
170
|
-
|
|
171
|
-
with patch(
|
|
191
|
+
|
|
192
|
+
with patch("sys.argv", ["adaptsapi", "--data", '{"test": "data"}']):
|
|
172
193
|
with pytest.raises(SystemExit):
|
|
173
194
|
main()
|
|
174
195
|
|
|
175
196
|
|
|
176
197
|
class TestCLIIntegration:
|
|
177
198
|
"""Integration tests for CLI functionality"""
|
|
178
|
-
|
|
179
|
-
@patch(
|
|
180
|
-
@patch(
|
|
181
|
-
@patch(
|
|
182
|
-
def test_cli_with_real_payload_structure(
|
|
199
|
+
|
|
200
|
+
@patch("adaptsapi.cli.load_default_endpoint")
|
|
201
|
+
@patch("adaptsapi.cli.load_token")
|
|
202
|
+
@patch("adaptsapi.cli.post")
|
|
203
|
+
def test_cli_with_real_payload_structure(
|
|
204
|
+
self, mock_post, mock_load_token, mock_load_endpoint
|
|
205
|
+
):
|
|
183
206
|
"""Test CLI with a realistic payload structure"""
|
|
184
207
|
mock_load_endpoint.return_value = "https://api.example.com/test"
|
|
185
208
|
mock_load_token.return_value = "test-token"
|
|
186
|
-
|
|
209
|
+
|
|
187
210
|
mock_response = Mock()
|
|
188
211
|
mock_response.status_code = 200
|
|
189
212
|
mock_response.text = '{"status": "success", "request_id": "123"}'
|
|
190
213
|
mock_post.return_value = mock_response
|
|
191
|
-
|
|
214
|
+
|
|
192
215
|
real_payload = {
|
|
193
216
|
"email_address": "test@example.com",
|
|
194
217
|
"user_name": "testuser",
|
|
@@ -201,23 +224,23 @@ class TestCLIIntegration:
|
|
|
201
224
|
"language": "Python",
|
|
202
225
|
"is_private": False,
|
|
203
226
|
"git_provider_type": "github",
|
|
204
|
-
"refresh_token": "1234567890"
|
|
205
|
-
}
|
|
227
|
+
"refresh_token": "1234567890",
|
|
228
|
+
},
|
|
206
229
|
}
|
|
207
|
-
|
|
208
|
-
with patch(
|
|
209
|
-
with patch(
|
|
230
|
+
|
|
231
|
+
with patch("sys.argv", ["adaptsapi", "--data", json.dumps(real_payload)]):
|
|
232
|
+
with patch("sys.stdout", new=StringIO()) as mock_stdout:
|
|
210
233
|
main()
|
|
211
|
-
|
|
234
|
+
|
|
212
235
|
# Verify the payload was processed correctly
|
|
213
236
|
mock_post.assert_called_once()
|
|
214
237
|
call_args = mock_post.call_args
|
|
215
238
|
sent_payload = call_args[0][2]
|
|
216
|
-
|
|
239
|
+
|
|
217
240
|
# Check that the original payload structure is preserved
|
|
218
241
|
assert sent_payload["email_address"] == "test@example.com"
|
|
219
242
|
assert sent_payload["user_name"] == "testuser"
|
|
220
243
|
assert sent_payload["repo_object"]["repository_name"] == "test-repo"
|
|
221
|
-
|
|
244
|
+
|
|
222
245
|
# Note: The post function adds metadata, but we're testing the CLI integration
|
|
223
246
|
# The actual metadata addition is tested in test_generate_docs.py
|