Skip to content

Commit f5faa58

Browse files
committed
added more Admin_UI tests
couple improvements to Fast_API_Server
1 parent 667aa85 commit f5faa58

11 files changed

Lines changed: 740 additions & 36 deletions

osbot_fast_api/admin_ui/api/routes/Routes__Admin__Cookies.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def api__cookies_bulk_set(self, bulk_request : Cookies__Bulk_Request, #cookies:
224224
"results": results
225225
}
226226

227-
def api__generate_value(self, value_type: str = "uuid") -> Dict[str, str]:
227+
def api__generate_value__value_type(self, value_type: str = "uuid") -> Dict[str, str]:
228228
"""Generate a value for cookies (UUID, random string, etc.)"""
229229
if value_type == "uuid":
230230
return {"value": random_guid(), "type": "uuid"}
@@ -256,4 +256,4 @@ def setup_routes(self):
256256
self.add_route_post (self.api__cookie_set__cookie_name )
257257
self.add_route_delete(self.api__cookie_delete__cookie_name)
258258
self.add_route_post (self.api__cookies_bulk_set )
259-
self.add_route_get (self.api__generate_value )
259+
self.add_route_get (self.api__generate_value__value_type )

osbot_fast_api/admin_ui/api/routes/Routes__Admin__Docs.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,6 @@ def api__client_examples(self) -> Dict[str, Any]: # Get client code ex
123123
return examples
124124

125125
def api__api_info(self) -> Dict[str, Any]: # Get API metadata and information
126-
if not self.parent_app:
127-
return {"error": "Parent app not configured"}
128126

129127
openapi = self.parent_app.open_api_json()
130128

osbot_fast_api/admin_ui/api/routes/Routes__Admin__Info.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ def api__server_info(self) -> Dict[str, Any]: # Get server information
1919
}
2020

2121
def api__app_info(self) -> Dict[str, Any]: # Get FastAPI application information # todo: convert this object to Schema__Fast_API__Admin__App_Info
22-
if not self.parent_app:
23-
return {"error": "Parent app not configured"}
2422

2523
return { "name" : self.parent_app.name ,
2624
"version" : self.parent_app.version ,
@@ -31,8 +29,6 @@ def api__app_info(self) -> Dict[str, Any]: # Get FastAPI applicatio
3129
"enable_api_key": self.parent_app.enable_api_key }
3230

3331
def api__stats(self) -> Dict[str, Any]: # Get application statistics
34-
if not self.parent_app:
35-
return {"error": "Parent app not configured"}
3632

3733
routes = self.parent_app.routes(include_default=False, expand_mounts=True)
3834

osbot_fast_api/utils/Fast_API_Server.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,24 @@ def stop(self):
6666
self.running = False
6767
return result
6868

69+
def requests_delete(self, path='', **kwargs):
70+
url = url_join_safe(self.url(), path)
71+
return requests.delete(url, **kwargs)
72+
6973
def requests_get(self, path='', **kwargs):
7074
url = url_join_safe(self.url(), path)
7175
return requests.get(url, **kwargs)
7276

73-
def requests_post(self, path='', data=None):
74-
if Type_Safe in base_types(data):
75-
json_data = data.json()
76-
elif type(data) is dict:
77-
json_data = data
78-
else:
79-
raise ValueError("data must be a Type_Safe or a dict")
77+
def requests_post(self, path='', data=None, json=None, **kwargs):
78+
if json is None:
79+
if Type_Safe in base_types(data):
80+
json = data.json()
81+
elif type(data) is dict:
82+
json = data
83+
else:
84+
raise ValueError("data must be a Type_Safe or a dict")
8085
url = urljoin(self.url(), path)
81-
return requests.post(url, json=json_data)
86+
return requests.post(url, json=json, **kwargs)
8287

8388
def url(self):
8489
return f'http://{FAST_API__HOST}:{self.port}/'

tests/unit/admin_ui/api/routes/test_Routes__Admin__Cookies.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,19 +97,19 @@ def test_05_api__generate_value(self):
9797
routes = self.routes_cookies
9898

9999
# Test UUID generation
100-
result = routes.api__generate_value('uuid')
100+
result = routes.api__generate_value__value_type('uuid')
101101
assert result['type'] == 'uuid'
102102
assert len(result['value']) == 36 # UUID length
103103
assert '-' in result['value']
104104

105105
# Test API key generation
106-
result = routes.api__generate_value('api_key')
106+
result = routes.api__generate_value__value_type('api_key')
107107
assert result['type'] == 'api_key'
108108
assert result['value'].startswith('sk-')
109109
assert len(result['value']) == 51 # sk- + 48 chars
110110

111111
# Test default generation
112-
result = routes.api__generate_value('unknown')
112+
result = routes.api__generate_value__value_type('unknown')
113113
assert result['type'] == 'default'
114114
assert len(result['value']) == 36 # Falls back to UUID
115115

tests/unit/admin_ui/api/routes/test_Routes__Admin__Cookies__client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def test_07__bulk_set_cookies(self):
160160
def test_08_generate_values(self):
161161
"""Test GET /api/generate-value"""
162162
# Generate UUID
163-
response = self.client.get('/admin-cookies/api/generate-value?value_type=uuid')
163+
response = self.client.get('/admin-cookies/api/generate-value/uuid')
164164

165165
assert response.status_code == 200
166166
data = response.json()
@@ -173,7 +173,7 @@ def test_08_generate_values(self):
173173
assert re.match(uuid_pattern, data['value'])
174174

175175
# Generate API key
176-
response = self.client.get('/admin-cookies/api/generate-value?value_type=api_key')
176+
response = self.client.get('/admin-cookies/api/generate-value/api_key')
177177
data = response.json()
178178

179179
assert data['type'] == 'api_key'
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
from unittest import TestCase
2+
from osbot_fast_api.admin_ui.api.routes.Routes__Admin__Docs import Routes__Admin__Docs
3+
from osbot_fast_api.admin_ui.api.testing.Admin_UI__Test_Objs import setup__admin_ui_test_objs, cleanup_admin_ui_test_objs
4+
from osbot_fast_api.api.routes.Fast_API__Routes import Fast_API__Routes
5+
from osbot_utils.type_safe.Type_Safe import Type_Safe
6+
from osbot_utils.utils.Objects import base_types
7+
from osbot_utils.utils.Misc import list_set
8+
9+
10+
class test_Routes__Admin__Docs(TestCase):
11+
"""Test Routes__Admin__Docs class directly"""
12+
13+
@classmethod
14+
def setUpClass(cls):
15+
"""Setup routes instance"""
16+
cls.test_objs = setup__admin_ui_test_objs(with_parent=True)
17+
cls.routes_docs = Routes__Admin__Docs()
18+
cls.routes_docs.parent_app = cls.test_objs.parent_fast_api
19+
20+
@classmethod
21+
def tearDownClass(cls):
22+
"""Cleanup"""
23+
cleanup_admin_ui_test_objs(cls.test_objs)
24+
25+
def test_01_setUpClass(self):
26+
"""Verify class setup and inheritance"""
27+
with self.routes_docs as _:
28+
assert type(_) is Routes__Admin__Docs
29+
assert Fast_API__Routes in base_types(_)
30+
assert Type_Safe in base_types(_)
31+
assert _.tag == 'admin-docs'
32+
assert _.parent_app is not None
33+
34+
def test_02_api__docs_endpoints(self):
35+
"""Test docs endpoints listing"""
36+
result = self.routes_docs.api__docs_endpoints()
37+
38+
assert isinstance(result, list)
39+
assert len(result) == 6 # Should have 6 doc endpoints
40+
41+
# Check endpoint structure
42+
for endpoint in result:
43+
assert 'name' in endpoint
44+
assert 'description' in endpoint
45+
assert 'url' in endpoint
46+
assert 'type' in endpoint
47+
assert 'icon' in endpoint
48+
49+
# Check specific endpoints
50+
endpoint_types = [e['type'] for e in result]
51+
assert 'swagger' in endpoint_types
52+
assert 'redoc' in endpoint_types
53+
assert 'openapi' in endpoint_types
54+
assert 'client' in endpoint_types
55+
assert 'routes' in endpoint_types
56+
assert 'admin' in endpoint_types
57+
58+
# Verify Swagger endpoint
59+
swagger = next(e for e in result if e['type'] == 'swagger')
60+
assert swagger['name'] == 'Swagger UI'
61+
assert swagger['url'] == '/docs'
62+
assert swagger['icon'] == 'swagger'
63+
64+
# Verify ReDoc endpoint
65+
redoc = next(e for e in result if e['type'] == 'redoc')
66+
assert redoc['url'] == '/redoc'
67+
68+
def test_03_api__client_examples(self):
69+
"""Test client code examples"""
70+
result = self.routes_docs.api__client_examples()
71+
72+
assert isinstance(result, dict)
73+
assert len(result) == 3 # curl, python, javascript
74+
75+
# Check curl example
76+
assert 'curl' in result
77+
curl = result['curl']
78+
assert curl['name'] == 'cURL'
79+
assert 'curl' in curl['example']
80+
assert '/config/status' in curl['example']
81+
assert '/admin/api/cookie/set' in curl['example']
82+
83+
# Check Python example
84+
assert 'python' in result
85+
python = result['python']
86+
assert python['name'] == 'Python'
87+
assert 'import requests' in python['example']
88+
assert 'response.json()' in python['example']
89+
90+
# Check JavaScript example
91+
assert 'javascript' in result
92+
js = result['javascript']
93+
assert js['name'] == 'JavaScript'
94+
assert 'fetch' in js['example']
95+
assert 'then' in js['example']
96+
97+
def test_04_api__api_info(self):
98+
"""Test API metadata retrieval"""
99+
result = self.routes_docs.api__api_info()
100+
101+
assert isinstance(result, dict)
102+
103+
# Check expected fields
104+
expected_fields = [
105+
'openapi_version',
106+
'api_title',
107+
'api_version',
108+
'api_description',
109+
'servers',
110+
'total_paths',
111+
'total_schemas',
112+
'tags'
113+
]
114+
assert list_set(result.keys()) == sorted(expected_fields)
115+
116+
# Verify values
117+
assert result['api_title' ] == 'Test Parent API'
118+
assert result['api_version' ] == 'v1.0.99'
119+
assert result['api_description' ] == 'Parent API for Admin UI testing'
120+
assert result['total_paths' ] > 0
121+
assert isinstance(result['tags' ], list)
122+
123+
# def test_05_api__api_info_no_parent(self): # this is not a realistic scenario any more
124+
# """Test API info without parent app"""
125+
# routes = Routes__Admin__Docs()
126+
# routes.parent_app = None
127+
#
128+
# result = routes.api__api_info()
129+
#
130+
# assert result == {"error": "Parent app not configured"}
131+
132+
def test_06_extract_tags(self):
133+
"""Test tag extraction from OpenAPI spec"""
134+
# Create a mock OpenAPI spec
135+
mock_spec = {
136+
'paths': {
137+
'/users': {
138+
'get': {'tags': ['users'], 'summary': 'Get users'},
139+
'post': {'tags': ['users'], 'summary': 'Create user'}
140+
},
141+
'/items': {
142+
'get': {'tags': ['items'], 'summary': 'Get items'}
143+
},
144+
'/admin/info': {
145+
'get': {'tags': ['admin', 'info'], 'summary': 'Admin info'}
146+
}
147+
},
148+
'tags': [
149+
{'name': 'users', 'description': 'User operations'},
150+
{'name': 'items', 'description': 'Item operations'}
151+
]
152+
}
153+
154+
routes = self.routes_docs
155+
tags = routes._extract_tags(mock_spec)
156+
157+
assert isinstance(tags, list)
158+
assert len(tags) >= 2
159+
160+
# Check tag structure
161+
for tag in tags:
162+
assert 'name' in tag
163+
assert 'count' in tag
164+
assert 'paths' in tag
165+
166+
# Check users tag
167+
users_tag = next((t for t in tags if t['name'] == 'users'), None)
168+
if users_tag:
169+
assert users_tag['count'] == 2
170+
assert len(users_tag['paths']) == 2
171+
assert users_tag.get('description') == 'User operations'

0 commit comments

Comments
 (0)