Skip to content

Commit abf1c02

Browse files
committed
added really important test test_Fast_API__Type_Safe__support_for_core_FastAPI which shows a major milestone in this project :)
the ability to use Type_Safe and Type_Safe__Primitives in routes (which have runtime protection) can how happen without the use of the main Fast_API class
1 parent 96bce1c commit abf1c02

File tree

2 files changed

+158
-1
lines changed

2 files changed

+158
-1
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import re
2+
import pytest
3+
from unittest import TestCase
4+
from fastapi import FastAPI
5+
from osbot_utils.type_safe.primitives.core.Safe_Float import Safe_Float
6+
from osbot_utils.type_safe.primitives.domains.identifiers.safe_str.Safe_Str__Display_Name import Safe_Str__Display_Name
7+
from osbot_utils.type_safe.primitives.core.Safe_UInt import Safe_UInt
8+
from starlette.testclient import TestClient
9+
from osbot_fast_api.api.routes.type_safe.Type_Safe__Route__Registration import Type_Safe__Route__Registration
10+
from osbot_utils.type_safe.Type_Safe import Type_Safe
11+
from osbot_utils.type_safe.primitives.domains.identifiers.Safe_Id import Safe_Id
12+
13+
14+
class Schema__Core_User(Type_Safe): # Schema for Core FastAPI test
15+
user_id : Safe_Id
16+
name : Safe_Str__Display_Name
17+
age : Safe_UInt
18+
19+
20+
class Schema__Core_Product(Type_Safe): # Another schema for testing
21+
product_id : Safe_Id
22+
price : Safe_Float
23+
in_stock : bool = True
24+
25+
26+
class test_Fast_API__Type_Safe__support_for_core_FastAPI(TestCase):
27+
"""
28+
Important TEST: This demonstrates that Type_Safe route registration
29+
works with Core FastAPI, not just our Fast_API wrapper class.
30+
31+
This means developers can:
32+
1. Add Type_Safe to existing FastAPI projects incrementally
33+
2. Use Type_Safe without adopting our full Fast_API class
34+
3. Get automatic type conversion and validation in Core FastAPI
35+
"""
36+
37+
@classmethod
38+
def setUpClass(cls): # ONE-TIME setup with core FastAPI
39+
cls.app = FastAPI() # Pure FastAPI - no Fast_API wrapper!
40+
cls.registration = Type_Safe__Route__Registration()
41+
42+
# Register routes using Type_Safe system
43+
cls.registration.register_route(cls.app.router, cls.create_user , ['POST'])
44+
cls.registration.register_route(cls.app.router, cls.get_user , ['GET'])
45+
cls.registration.register_route(cls.app.router, cls.create_product , ['POST'])
46+
47+
cls.client = TestClient(cls.app)
48+
49+
@staticmethod
50+
def create_user(user: Schema__Core_User) -> Schema__Core_User: # Type_Safe parameter and return
51+
return user
52+
53+
@staticmethod
54+
def get_user(user_id: Safe_Id) -> dict: # Type_Safe primitive parameter
55+
return {'user_id': str(user_id), 'found': True}
56+
57+
@staticmethod
58+
def create_product(product: Schema__Core_Product) -> Schema__Core_Product:
59+
return product
60+
61+
def test__Core_fastapi__handles_type_safe_body_params(self): # Test Core FastAPI handles Type_Safe body parameters
62+
response = self.client.post('/create-user', json={ 'user_id': 'USER-456' ,
63+
'name' : 'Core Test' ,
64+
'age' : 30 })
65+
66+
assert response.status_code == 200
67+
result = response.json()
68+
69+
assert result['user_id'] == 'USER-456' # Type_Safe conversion worked in Core FastAPI!
70+
assert result['name'] == 'Core Test'
71+
assert result['age'] == 30
72+
73+
def test__Core_fastapi__handles_safe_primitives(self): # Test Core FastAPI handles Safe_Str primitives
74+
response = self.client.get('/get-user', params={'user_id': 'USER-789'})
75+
76+
assert response.status_code == 200
77+
result = response.json()
78+
79+
# Safe_Id was handled correctly
80+
assert result['user_id'] == 'USER-789'
81+
assert result['found'] is True
82+
83+
def test__Core_fastapi__auto_validates(self): # Test Core FastAPI gets Type_Safe validation
84+
85+
error_message = "Safe_UInt must be >= 0, got -5"
86+
with pytest.raises(ValueError, match=re.escape(error_message)):
87+
response = self.client.post('/create-user', json={ 'user_id': 'USER-999', # Invalid age will should be rejected by Safe_UInt
88+
'name': 'Invalid User',
89+
'age' : -5 }) # Negative not allowed in Safe_Int
90+
91+
92+
# Should get validation error (not 200) # note we don't get this far, since self.client is the one raising the exception
93+
# assert response.status_code == 400 # Type_Safe validation worked!
94+
95+
def test__Core_fastapi__sanitizes_input(self): # Test Core FastAPI gets Type_Safe sanitization
96+
# Safe_Id should sanitize special characters
97+
response = self.client.post('/create-user', json={
98+
'user_id': 'USER!@#$%123', # Special chars will be sanitized
99+
'name': 'Test',
100+
'age': 25
101+
})
102+
103+
assert response.status_code == 200
104+
result = response.json()
105+
106+
# Safe_Id sanitized the input automatically
107+
assert result['user_id'] == 'USER_____123' # Special chars replaced with _
108+
109+
def test__Core_fastapi__complex_schema(self): # Test Core FastAPI handles complex Type_Safe schemas
110+
response = self.client.post('/create-product', json={
111+
'product_id': 'PROD-001',
112+
'price': 99.99,
113+
'in_stock': False
114+
})
115+
116+
assert response.status_code == 200
117+
result = response.json()
118+
119+
assert result['product_id'] == 'PROD-001'
120+
assert result['price'] == 99.99
121+
assert result['in_stock'] is False
122+
123+
def test__Core_fastapi__route_extraction_works(self): # Test that route extraction works on Core FastAPI
124+
from osbot_fast_api.client.Fast_API__Route__Extractor import Fast_API__Route__Extractor
125+
126+
extractor = Fast_API__Route__Extractor(app=self.app, include_default=False)
127+
collection = extractor.extract_routes()
128+
129+
# Should extract all our Type_Safe routes
130+
route_names = [r.method_name for r in collection.routes]
131+
assert 'create_user' in route_names
132+
assert 'get_user' in route_names
133+
assert 'create_product' in route_names
134+
135+
# Verify original types preserved
136+
create_user_route = next(r for r in collection.routes if r.method_name == 'create_user')
137+
assert create_user_route.body_params[0].param_type is Schema__Core_User # Original Type_Safe class
138+
assert create_user_route.return_type is Schema__Core_User
139+
140+
def test__Core_fastapi__serialization_round_trip(self): # Test serialization works with Core FastAPI routes
141+
from osbot_fast_api.client.Fast_API__Route__Extractor import Fast_API__Route__Extractor
142+
143+
extractor = Fast_API__Route__Extractor(app=self.app, include_default=False)
144+
collection = extractor.extract_routes()
145+
146+
# Serialize
147+
json_data = collection.json()
148+
149+
# Deserialize
150+
restored = collection.__class__.from_json(json_data)
151+
152+
# Perfect round-trip
153+
assert collection.obj() == restored.obj()
154+
155+
# No __BaseModel artifacts
156+
json_str = str(json_data)
157+
assert '__BaseModel' not in json_str

tests/unit/client/test_Fast_API__Route__Extractor__integration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import List
55
from unittest import TestCase
66
from fastapi import FastAPI, APIRouter
7-
from osbot_fast_api.api.schemas.Schema__Fast_API__Config import Schema__Fast_API__Config
7+
from osbot_fast_api.api.schemas.Schema__Fast_API__Config import Schema__Fast_API__Config
88
from osbot_utils.testing.__ import __
99
from osbot_utils.type_safe.Type_Safe import Type_Safe
1010
from osbot_utils.type_safe.primitives.domains.identifiers.safe_str.Safe_Str__Id import Safe_Str__Id

0 commit comments

Comments
 (0)