roksta 0.3.2__cp311-cp311-macosx_10_9_universal2.whl → 0.3.8__cp311-cp311-macosx_10_9_universal2.whl
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.
- roksta/__init__.cpython-311-darwin.so +0 -0
- roksta/ai/__init__.cpython-311-darwin.so +0 -0
- roksta/ai/call_ai.cpython-311-darwin.so +0 -0
- roksta/ai/gemini.cpython-311-darwin.so +0 -0
- roksta/ai/generic.cpython-311-darwin.so +0 -0
- roksta/ai/llm.cpython-311-darwin.so +0 -0
- roksta/ai/openai.cpython-311-darwin.so +0 -0
- roksta/ai/tools/__init__.cpython-311-darwin.so +0 -0
- roksta/ai/tools/delete_file.cpython-311-darwin.so +0 -0
- roksta/ai/tools/edit_file.cpython-311-darwin.so +0 -0
- roksta/ai/tools/final_response.cpython-311-darwin.so +0 -0
- roksta/ai/tools/get_file_summaries.cpython-311-darwin.so +0 -0
- roksta/ai/tools/read_file.cpython-311-darwin.so +0 -0
- roksta/ai/tools/regex_replace.cpython-311-darwin.so +0 -0
- roksta/ai/tools/shell_any.cpython-311-darwin.so +0 -0
- roksta/ai/tools/shell_limited.cpython-311-darwin.so +0 -0
- roksta/ai/tools/tool_defs.cpython-311-darwin.so +0 -0
- roksta/ai/tools/tool_utils.cpython-311-darwin.so +0 -0
- roksta/ai/tools/web_fetch.cpython-311-darwin.so +0 -0
- roksta/ai/tools/write_file.cpython-311-darwin.so +0 -0
- roksta/analytics.cpython-311-darwin.so +0 -0
- roksta/balance.cpython-311-darwin.so +0 -0
- roksta/build_project.cpython-311-darwin.so +0 -0
- roksta/chat_workflow.cpython-311-darwin.so +0 -0
- roksta/check_for_updates.cpython-311-darwin.so +0 -0
- roksta/check_subtask_sequence.cpython-311-darwin.so +0 -0
- roksta/checkpoints.cpython-311-darwin.so +0 -0
- roksta/clarify_goal.cpython-311-darwin.so +0 -0
- roksta/codebase_listing.cpython-311-darwin.so +0 -0
- roksta/command_handlers/__init__.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_activate_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_add_funds_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_auto_charge_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_auto_commit_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_building_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_chat_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_dev_rate_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_feedback_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_goal_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_help_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_init_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_linting_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_login_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_logout_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_payment_details_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_quit_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_redeem_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_request_activation_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_testing_command.cpython-311-darwin.so +0 -0
- roksta/command_handlers/handle_usage_command.cpython-311-darwin.so +0 -0
- roksta/create_default_config.cpython-311-darwin.so +0 -0
- roksta/create_default_ignore_file.cpython-311-darwin.so +0 -0
- roksta/default_config.cpython-311-darwin.so +0 -0
- roksta/default_ignores.cpython-311-darwin.so +0 -0
- roksta/discover_test_command.cpython-311-darwin.so +0 -0
- roksta/enums.cpython-311-darwin.so +0 -0
- roksta/env.cpython-311-darwin.so +0 -0
- roksta/extended_text_area.cpython-311-darwin.so +0 -0
- roksta/firebase.cpython-311-darwin.so +0 -0
- roksta/firebase_auth_web.cpython-311-darwin.so +0 -0
- roksta/firebase_config.cpython-311-darwin.so +0 -0
- roksta/fix_tests.cpython-311-darwin.so +0 -0
- roksta/gen_codebase_summaries.cpython-311-darwin.so +0 -0
- roksta/gen_one_line_goal.cpython-311-darwin.so +0 -0
- roksta/gen_subtasks.cpython-311-darwin.so +0 -0
- roksta/get_codebase_structure.cpython-311-darwin.so +0 -0
- roksta/get_failing_tests.cpython-311-darwin.so +0 -0
- roksta/goal_workflow.cpython-311-darwin.so +0 -0
- roksta/init_codebase.cpython-311-darwin.so +0 -0
- roksta/lint_code.cpython-311-darwin.so +0 -0
- roksta/logger.cpython-311-darwin.so +0 -0
- roksta/main.cpython-311-darwin.so +0 -0
- roksta/make_issue.cpython-311-darwin.so +0 -0
- roksta/new_features.cpython-311-darwin.so +0 -0
- roksta/parse_directive_cli_tokens.cpython-311-darwin.so +0 -0
- roksta/parse_readme.cpython-311-darwin.so +0 -0
- roksta/propose_solution.cpython-311-darwin.so +0 -0
- roksta/response_formats.cpython-311-darwin.so +0 -0
- roksta/rewrite_goal.cpython-311-darwin.so +0 -0
- roksta/roksta.cpython-311-darwin.so +0 -0
- roksta/run_cli_goal.cpython-311-darwin.so +0 -0
- roksta/save_chat_transcript.cpython-311-darwin.so +0 -0
- roksta/select_files.cpython-311-darwin.so +0 -0
- roksta/tips.cpython-311-darwin.so +0 -0
- roksta/utils.cpython-311-darwin.so +0 -0
- roksta/write_code.cpython-311-darwin.so +0 -0
- roksta-0.3.8.dist-info/METADATA +471 -0
- roksta-0.3.8.dist-info/RECORD +91 -0
- {roksta-0.3.2.dist-info → roksta-0.3.8.dist-info}/top_level.txt +0 -1
- roksta-0.3.2.dist-info/METADATA +0 -40
- roksta-0.3.2.dist-info/RECORD +0 -121
- tests/__init__.py +0 -2
- tests/conftest.py +0 -211
- tests/functions/__init__.py +0 -2
- tests/functions/api_v1_00/__init__.py +0 -2
- tests/functions/api_v1_00/test__analytics.py +0 -416
- tests/functions/api_v1_00/test__gemini_proxy.py +0 -352
- tests/functions/api_v1_00/test__generic_proxy.py +0 -428
- tests/functions/api_v1_00/test__get_payment_details.py +0 -356
- tests/functions/api_v1_00/test__openai_proxy.py +0 -449
- tests/functions/api_v1_00/test__redeem_credit_code.py +0 -167
- tests/functions/api_v1_00/test__sync_emails.py +0 -325
- tests/functions/api_v1_00/test__take_payment.py +0 -491
- tests/functions/api_v1_00/test__use_activation_code.py +0 -438
- tests/functions/api_v1_01/__init__.py +0 -2
- tests/functions/api_v1_01/test__analytics.py +0 -416
- tests/functions/api_v1_01/test__gemini_proxy.py +0 -352
- tests/functions/api_v1_01/test__generic_proxy.py +0 -428
- tests/functions/api_v1_01/test__get_payment_details.py +0 -356
- tests/functions/api_v1_01/test__openai_proxy.py +0 -449
- tests/functions/api_v1_01/test__redeem_credit_code.py +0 -167
- tests/functions/api_v1_01/test__sync_emails.py +0 -325
- tests/functions/api_v1_01/test__take_payment.py +0 -491
- tests/functions/api_v1_01/test__use_activation_code.py +0 -438
- tests/functions/api_v1_02/__init__.py +0 -2
- tests/functions/api_v1_02/test__analytics.py +0 -416
- tests/functions/api_v1_02/test__gemini_proxy.py +0 -352
- tests/functions/api_v1_02/test__generic_proxy.py +0 -428
- tests/functions/api_v1_02/test__get_payment_details.py +0 -356
- tests/functions/api_v1_02/test__openai_proxy.py +0 -449
- tests/functions/api_v1_02/test__redeem_credit_code.py +0 -167
- tests/functions/api_v1_02/test__sync_emails.py +0 -325
- tests/functions/api_v1_02/test__take_payment.py +0 -491
- tests/functions/api_v1_02/test__use_activation_code.py +0 -438
- tests/functions/api_v1_02/test_proxy_keyword_replacement.py +0 -557
- tests/functions/api_v1_02/test_replace_keywords.py +0 -74
- tests/functions/test_auth.py +0 -24
- tests/functions/test_main.py +0 -73
- tests/functions/test_utils.py +0 -484
- {roksta-0.3.2.dist-info → roksta-0.3.8.dist-info}/WHEEL +0 -0
- {roksta-0.3.2.dist-info → roksta-0.3.8.dist-info}/entry_points.txt +0 -0
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import json
|
|
4
|
-
import types
|
|
5
|
-
import importlib
|
|
6
|
-
from unittest.mock import patch
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# Ensure the functions/ directory is importable as a top-level module location
|
|
10
|
-
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
|
11
|
-
FUNCTIONS_DIR = os.path.join(PROJECT_ROOT, 'functions')
|
|
12
|
-
if FUNCTIONS_DIR not in sys.path:
|
|
13
|
-
sys.path.insert(0, FUNCTIONS_DIR)
|
|
14
|
-
|
|
15
|
-
# Prepare lightweight fake modules to satisfy imports inside _gemini_proxy
|
|
16
|
-
# We'll temporarily inject these into sys.modules while importing the module
|
|
17
|
-
|
|
18
|
-
# Save any originals so we can restore them after import
|
|
19
|
-
_orig_sys_modules = {}
|
|
20
|
-
_names_to_fake = [
|
|
21
|
-
'firebase_functions',
|
|
22
|
-
'firebase_admin',
|
|
23
|
-
'firebase_admin.firestore',
|
|
24
|
-
'utils',
|
|
25
|
-
'auth',
|
|
26
|
-
'google',
|
|
27
|
-
'google.genai',
|
|
28
|
-
'google.genai.types',
|
|
29
|
-
]
|
|
30
|
-
for name in _names_to_fake:
|
|
31
|
-
_orig_sys_modules[name] = sys.modules.get(name)
|
|
32
|
-
|
|
33
|
-
# Fake firebase_functions.https_fn.Response to capture returned data
|
|
34
|
-
firebase_functions = types.ModuleType('firebase_functions')
|
|
35
|
-
|
|
36
|
-
class FakeResponse:
|
|
37
|
-
def __init__(self, response=None, mimetype=None, status=200, **kwargs):
|
|
38
|
-
# Mirror the small subset of the interface tests expect
|
|
39
|
-
self.status_code = status
|
|
40
|
-
if isinstance(response, (dict, list)):
|
|
41
|
-
self._body_text = json.dumps(response)
|
|
42
|
-
else:
|
|
43
|
-
self._body_text = '' if response is None else response
|
|
44
|
-
self.headers = kwargs.get('headers', {})
|
|
45
|
-
|
|
46
|
-
def get_data(self, as_text=False):
|
|
47
|
-
if as_text:
|
|
48
|
-
return self._body_text
|
|
49
|
-
return self._body_text.encode('utf-8')
|
|
50
|
-
|
|
51
|
-
firebase_functions.https_fn = types.SimpleNamespace(Request=object, Response=FakeResponse)
|
|
52
|
-
sys.modules['firebase_functions'] = firebase_functions
|
|
53
|
-
|
|
54
|
-
# Fake firebase_admin and its firestore submodule to satisfy optional imports in the proxy
|
|
55
|
-
firebase_admin = types.ModuleType('firebase_admin')
|
|
56
|
-
firestore_mod = types.ModuleType('firebase_admin.firestore')
|
|
57
|
-
|
|
58
|
-
# Minimal Client type for annotations and a no-op client() factory
|
|
59
|
-
class _DummyClient: # pragma: no cover - just a stub for import-time type hints
|
|
60
|
-
pass
|
|
61
|
-
|
|
62
|
-
def _dummy_client():
|
|
63
|
-
# Return a simple object; logic using it is patched in tests
|
|
64
|
-
return types.SimpleNamespace()
|
|
65
|
-
|
|
66
|
-
# Decorator stub used by perform_atomic_debit; never actually invoked in tests
|
|
67
|
-
def _transactional(fn=None):
|
|
68
|
-
def wrapper(*args, **kwargs):
|
|
69
|
-
return fn(*args, **kwargs)
|
|
70
|
-
return wrapper
|
|
71
|
-
|
|
72
|
-
# Populate stubs on the firestore module
|
|
73
|
-
setattr(firestore_mod, 'Client', _DummyClient)
|
|
74
|
-
setattr(firestore_mod, 'client', _dummy_client)
|
|
75
|
-
setattr(firestore_mod, 'transactional', _transactional)
|
|
76
|
-
setattr(firestore_mod, 'SERVER_TIMESTAMP', object())
|
|
77
|
-
|
|
78
|
-
# Attach firestore submodule to firebase_admin package and register in sys.modules
|
|
79
|
-
setattr(firebase_admin, 'firestore', firestore_mod)
|
|
80
|
-
sys.modules['firebase_admin'] = firebase_admin
|
|
81
|
-
sys.modules['firebase_admin.firestore'] = firestore_mod
|
|
82
|
-
|
|
83
|
-
# Fake utils module (provides functions imported by _gemini_proxy)
|
|
84
|
-
utils_mod = types.ModuleType('utils')
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def _fake_create_json_response(success: bool, payload: any, status_code: int):
|
|
88
|
-
response_body = {"success": success, "payload": payload}
|
|
89
|
-
return firebase_functions.https_fn.Response(response=json.dumps(response_body), status=status_code, headers={'Content-Type': 'application/json'})
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def _fake_get_api_key(llm_family=None):
|
|
93
|
-
return 'DUMMY_GEMINI_KEY'
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def _fake_verify_firebase_token(req):
|
|
97
|
-
# Default: no-op (successful)
|
|
98
|
-
return {}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
utils_mod.create_json_response = _fake_create_json_response
|
|
102
|
-
utils_mod.get_api_key = _fake_get_api_key
|
|
103
|
-
utils_mod.verify_firebase_token = _fake_verify_firebase_token
|
|
104
|
-
sys.modules['utils'] = utils_mod
|
|
105
|
-
|
|
106
|
-
# Fake auth module with validate_auth_key
|
|
107
|
-
auth_mod = types.ModuleType('auth')
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def _fake_validate_auth_key(val: str) -> bool:
|
|
111
|
-
return True
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
auth_mod.validate_auth_key = _fake_validate_auth_key
|
|
115
|
-
sys.modules['auth'] = auth_mod
|
|
116
|
-
|
|
117
|
-
# Fake google.genai and types modules
|
|
118
|
-
google_mod = types.ModuleType('google')
|
|
119
|
-
genai_mod = types.ModuleType('google.genai')
|
|
120
|
-
types_mod = types.ModuleType('google.genai.types')
|
|
121
|
-
|
|
122
|
-
class DummyGenerateContentConfig:
|
|
123
|
-
def __init__(self, **kwargs):
|
|
124
|
-
self.kwargs = kwargs
|
|
125
|
-
|
|
126
|
-
class DummyContent:
|
|
127
|
-
def __init__(self, **kwargs):
|
|
128
|
-
self.kwargs = kwargs
|
|
129
|
-
|
|
130
|
-
types_mod.GenerateContentConfig = DummyGenerateContentConfig
|
|
131
|
-
types_mod.Content = DummyContent
|
|
132
|
-
|
|
133
|
-
# Provide a dummy Client implementation; tests will patch this when needed
|
|
134
|
-
class DummyClient:
|
|
135
|
-
def __init__(self, api_key):
|
|
136
|
-
self.api_key = api_key
|
|
137
|
-
class Models:
|
|
138
|
-
def generate_content(self, **params):
|
|
139
|
-
raise NotImplementedError("generate_content not implemented for dummy client")
|
|
140
|
-
self.models = Models()
|
|
141
|
-
|
|
142
|
-
genai_mod.Client = DummyClient
|
|
143
|
-
# Also expose types on genai module (in case of attribute access)
|
|
144
|
-
genai_mod.types = types_mod
|
|
145
|
-
|
|
146
|
-
google_mod.genai = genai_mod
|
|
147
|
-
sys.modules['google'] = google_mod
|
|
148
|
-
sys.modules['google.genai'] = genai_mod
|
|
149
|
-
sys.modules['google.genai.types'] = types_mod
|
|
150
|
-
|
|
151
|
-
# Import the module under test after preparing the fake imports
|
|
152
|
-
# Resolve the actual repository 'functions' directory (tests files live under tests/, so climb up to repo root)
|
|
153
|
-
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
|
154
|
-
functions_root = os.path.join(repo_root, 'functions')
|
|
155
|
-
module_path = os.path.join(functions_root, 'api_v1_00', '_gemini_proxy.py')
|
|
156
|
-
spec = importlib.util.spec_from_file_location('api_v1_00._gemini_proxy', module_path)
|
|
157
|
-
_gemini = importlib.util.module_from_spec(spec)
|
|
158
|
-
spec.loader.exec_module(_gemini)
|
|
159
|
-
|
|
160
|
-
# Restore original sys.modules mappings to avoid side-effects for other tests
|
|
161
|
-
for name, orig in _orig_sys_modules.items():
|
|
162
|
-
if orig is None:
|
|
163
|
-
try:
|
|
164
|
-
del sys.modules[name]
|
|
165
|
-
except KeyError:
|
|
166
|
-
pass
|
|
167
|
-
else:
|
|
168
|
-
sys.modules[name] = orig
|
|
169
|
-
|
|
170
|
-
# Helper request stub used in tests
|
|
171
|
-
class DummyRequest:
|
|
172
|
-
def __init__(self, headers=None, method='POST', json_data=None, raise_on_get_json=False):
|
|
173
|
-
self.headers = headers or {}
|
|
174
|
-
self.method = method
|
|
175
|
-
self._json_data = json_data
|
|
176
|
-
self._raise = raise_on_get_json
|
|
177
|
-
|
|
178
|
-
def get_json(self, silent=False):
|
|
179
|
-
if self._raise:
|
|
180
|
-
raise Exception('Malformed JSON')
|
|
181
|
-
return self._json_data
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def _parse_response(resp):
|
|
185
|
-
data = resp.get_data(as_text=True)
|
|
186
|
-
return json.loads(data)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
# -----------------------------
|
|
190
|
-
# Tests
|
|
191
|
-
# -----------------------------
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def test_verify_firebase_token_failure_returns_401():
|
|
195
|
-
req = DummyRequest(headers={_gemini.AUTH_HEADER_NAME: 'ok'}, method='POST')
|
|
196
|
-
with patch.object(_gemini, 'verify_firebase_token', side_effect=Exception('invalid token')):
|
|
197
|
-
resp = _gemini._gemini_proxy(req)
|
|
198
|
-
|
|
199
|
-
assert resp.status_code == 401
|
|
200
|
-
payload = _parse_response(resp)
|
|
201
|
-
assert payload['success'] is False
|
|
202
|
-
assert 'Unauthorized' in payload['payload']
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
def test_missing_auth_header_returns_401():
|
|
206
|
-
# no app auth header
|
|
207
|
-
req = DummyRequest(headers={}, method='POST', json_data={'model': 'm', 'contents': 'c', 'config': {}})
|
|
208
|
-
with patch.object(_gemini, 'validate_auth_key', return_value=True), patch.object(_gemini, 'verify_firebase_token', return_value={}):
|
|
209
|
-
resp = _gemini._gemini_proxy(req)
|
|
210
|
-
|
|
211
|
-
assert resp.status_code == 401
|
|
212
|
-
payload = _parse_response(resp)
|
|
213
|
-
assert payload['success'] is False
|
|
214
|
-
assert 'Missing app authentication key' in payload['payload']
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def test_invalid_auth_key_returns_403():
|
|
218
|
-
headers = {_gemini.AUTH_HEADER_NAME: 'bad'}
|
|
219
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'model': 'm', 'contents': 'c', 'config': {}})
|
|
220
|
-
with patch.object(_gemini, 'validate_auth_key', return_value=False), patch.object(_gemini, 'verify_firebase_token', return_value={}):
|
|
221
|
-
resp = _gemini._gemini_proxy(req)
|
|
222
|
-
|
|
223
|
-
assert resp.status_code == 403
|
|
224
|
-
payload = _parse_response(resp)
|
|
225
|
-
assert payload['success'] is False
|
|
226
|
-
assert 'Invalid app authentication key' in payload['payload']
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
def test_non_post_method_returns_405():
|
|
230
|
-
headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
|
|
231
|
-
req = DummyRequest(headers=headers, method='GET')
|
|
232
|
-
with patch.object(_gemini, 'validate_auth_key', return_value=True), \
|
|
233
|
-
patch.object(_gemini, 'verify_firebase_token', return_value={}), \
|
|
234
|
-
patch.object(_gemini, 'ensure_balance_positive', return_value=(True, 100.0)):
|
|
235
|
-
resp = _gemini._gemini_proxy(req)
|
|
236
|
-
|
|
237
|
-
assert resp.status_code == 405
|
|
238
|
-
payload = _parse_response(resp)
|
|
239
|
-
assert payload['success'] is False
|
|
240
|
-
assert 'POST method required' in payload['payload']
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
def test_malformed_json_returns_400():
|
|
244
|
-
headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
|
|
245
|
-
req = DummyRequest(headers=headers, method='POST', raise_on_get_json=True)
|
|
246
|
-
with patch.object(_gemini, 'validate_auth_key', return_value=True), \
|
|
247
|
-
patch.object(_gemini, 'verify_firebase_token', return_value={}), \
|
|
248
|
-
patch.object(_gemini, 'ensure_balance_positive', return_value=(True, 100.0)):
|
|
249
|
-
resp = _gemini._gemini_proxy(req)
|
|
250
|
-
|
|
251
|
-
assert resp.status_code == 400
|
|
252
|
-
payload = _parse_response(resp)
|
|
253
|
-
assert payload['success'] is False
|
|
254
|
-
assert 'Invalid JSON payload' in payload['payload']
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
def test_rehydrate_params_error_returns_400():
|
|
258
|
-
headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
|
|
259
|
-
# missing 'model' to cause KeyError inside rehydrate_params
|
|
260
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'contents': 'x', 'config': {}})
|
|
261
|
-
with patch.object(_gemini, 'validate_auth_key', return_value=True), \
|
|
262
|
-
patch.object(_gemini, 'verify_firebase_token', return_value={}), \
|
|
263
|
-
patch.object(_gemini, 'ensure_balance_positive', return_value=(True, 100.0)):
|
|
264
|
-
resp = _gemini._gemini_proxy(req)
|
|
265
|
-
|
|
266
|
-
assert resp.status_code == 400
|
|
267
|
-
payload = _parse_response(resp)
|
|
268
|
-
assert payload['success'] is False
|
|
269
|
-
assert 'Error hydrating params' in payload['payload']
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
def test_get_api_key_failure_returns_500():
|
|
273
|
-
headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
|
|
274
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'model': 'g', 'contents': 'hi', 'config': {}})
|
|
275
|
-
with patch.object(_gemini, 'validate_auth_key', return_value=True), \
|
|
276
|
-
patch.object(_gemini, 'verify_firebase_token', return_value={}), \
|
|
277
|
-
patch.object(_gemini, 'ensure_balance_positive', return_value=(True, 100.0)), \
|
|
278
|
-
patch.object(_gemini, 'get_api_key', side_effect=Exception('boom')):
|
|
279
|
-
resp = _gemini._gemini_proxy(req)
|
|
280
|
-
|
|
281
|
-
assert resp.status_code == 500
|
|
282
|
-
payload = _parse_response(resp)
|
|
283
|
-
assert payload['success'] is False
|
|
284
|
-
assert 'Could not retrieve API key' in payload['payload']
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def test_successful_flow_calls_genai_and_returns_payload():
|
|
288
|
-
headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
|
|
289
|
-
req_payload = {'model': 'gem-model', 'contents': 'hello', 'config': {}}
|
|
290
|
-
req = DummyRequest(headers=headers, method='POST', json_data=req_payload)
|
|
291
|
-
|
|
292
|
-
class FakeClient:
|
|
293
|
-
def __init__(self, api_key):
|
|
294
|
-
self.api_key = api_key
|
|
295
|
-
self.models = self
|
|
296
|
-
|
|
297
|
-
def generate_content(self, **params):
|
|
298
|
-
class FakeResp:
|
|
299
|
-
def __init__(self):
|
|
300
|
-
# include usage metadata expected by get_usage()
|
|
301
|
-
self.usage_metadata = types.SimpleNamespace(prompt_token_count=1, total_token_count=2)
|
|
302
|
-
|
|
303
|
-
def to_json_dict(self_inner):
|
|
304
|
-
return { 'result': 'ok', 'received_model': params.get('model') }
|
|
305
|
-
return FakeResp()
|
|
306
|
-
|
|
307
|
-
with patch.object(_gemini, 'validate_auth_key', return_value=True), \
|
|
308
|
-
patch.object(_gemini, 'verify_firebase_token', return_value={}), \
|
|
309
|
-
patch.object(_gemini, 'ensure_balance_positive', return_value=(True, 100.0)), \
|
|
310
|
-
patch.object(_gemini, 'bill_with_retry', return_value=("ok", 99.5)), \
|
|
311
|
-
patch.object(_gemini, 'get_api_key', return_value='GEMINI-KEY'), \
|
|
312
|
-
patch.object(_gemini.genai, 'Client', FakeClient):
|
|
313
|
-
resp = _gemini._gemini_proxy(req)
|
|
314
|
-
|
|
315
|
-
assert resp.status_code == 200
|
|
316
|
-
payload = _parse_response(resp)
|
|
317
|
-
assert payload['success'] is True
|
|
318
|
-
assert isinstance(payload['payload'], dict)
|
|
319
|
-
assert payload['payload']['result'] == 'ok'
|
|
320
|
-
assert payload['payload']['received_model'] == 'gem-model'
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
def test_genai_exception_with_status_code_is_returned_as_error_status():
|
|
324
|
-
headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
|
|
325
|
-
req_payload = {'model': 'g', 'contents': 'hey', 'config': {}}
|
|
326
|
-
req = DummyRequest(headers=headers, method='POST', json_data=req_payload)
|
|
327
|
-
|
|
328
|
-
class CustomErr(Exception):
|
|
329
|
-
pass
|
|
330
|
-
|
|
331
|
-
err = CustomErr('rate limited')
|
|
332
|
-
err.status_code = 502
|
|
333
|
-
|
|
334
|
-
class ErrClient:
|
|
335
|
-
def __init__(self, api_key):
|
|
336
|
-
self.api_key = api_key
|
|
337
|
-
self.models = self
|
|
338
|
-
|
|
339
|
-
def generate_content(self, **params):
|
|
340
|
-
raise err
|
|
341
|
-
|
|
342
|
-
with patch.object(_gemini, 'validate_auth_key', return_value=True), \
|
|
343
|
-
patch.object(_gemini, 'verify_firebase_token', return_value={}), \
|
|
344
|
-
patch.object(_gemini, 'ensure_balance_positive', return_value=(True, 100.0)), \
|
|
345
|
-
patch.object(_gemini, 'get_api_key', return_value='GEMINI-KEY'), \
|
|
346
|
-
patch.object(_gemini.genai, 'Client', ErrClient):
|
|
347
|
-
resp = _gemini._gemini_proxy(req)
|
|
348
|
-
|
|
349
|
-
assert resp.status_code == 502
|
|
350
|
-
payload = _parse_response(resp)
|
|
351
|
-
assert payload['success'] is False
|
|
352
|
-
assert 'Internal Server Error' in payload['payload']
|