roksta 0.2.4__cp314-cp314-macosx_10_13_universal2.whl → 0.2.6__cp314-cp314-macosx_10_13_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.

Potentially problematic release.


This version of roksta might be problematic. Click here for more details.

Files changed (54) hide show
  1. roksta/ai/gemini.cpython-314-darwin.so +0 -0
  2. roksta/ai/generic.cpython-314-darwin.so +0 -0
  3. roksta/ai/llm.cpython-314-darwin.so +0 -0
  4. roksta/ai/openai.cpython-314-darwin.so +0 -0
  5. roksta/ai/tools.cpython-314-darwin.so +0 -0
  6. roksta/analytics.cpython-314-darwin.so +0 -0
  7. roksta/chat_workflow.cpython-314-darwin.so +0 -0
  8. roksta/command_handlers.cpython-314-darwin.so +0 -0
  9. roksta/env.cpython-314-darwin.so +0 -0
  10. roksta/extended_text_area.cpython-314-darwin.so +0 -0
  11. roksta/firebase_auth_web.cpython-314-darwin.so +0 -0
  12. roksta/gen_codebase_summaries.cpython-314-darwin.so +0 -0
  13. roksta/goal_workflow.cpython-314-darwin.so +0 -0
  14. roksta/lint_code.cpython-314-darwin.so +0 -0
  15. roksta/main.cpython-314-darwin.so +0 -0
  16. roksta/make_issue.cpython-314-darwin.so +0 -0
  17. roksta/new_features.cpython-314-darwin.so +0 -0
  18. roksta/parse_readme.cpython-314-darwin.so +0 -0
  19. roksta/roksta.cpython-314-darwin.so +0 -0
  20. roksta/run_cli_goal.cpython-314-darwin.so +0 -0
  21. roksta/tips.cpython-314-darwin.so +0 -0
  22. roksta/utils.cpython-314-darwin.so +0 -0
  23. {roksta-0.2.4.dist-info → roksta-0.2.6.dist-info}/METADATA +1 -1
  24. roksta-0.2.6.dist-info/RECORD +78 -0
  25. {roksta-0.2.4.dist-info → roksta-0.2.6.dist-info}/top_level.txt +1 -0
  26. tests/__init__.py +2 -0
  27. tests/conftest.py +169 -0
  28. tests/functions/__init__.py +2 -0
  29. tests/functions/api_v0_01/__init__.py +2 -0
  30. tests/functions/api_v0_01/test__analytics.py +417 -0
  31. tests/functions/api_v0_01/test__gemini_proxy.py +307 -0
  32. tests/functions/api_v0_01/test__generic_proxy.py +399 -0
  33. tests/functions/api_v0_01/test__get_payment_details.py +356 -0
  34. tests/functions/api_v0_01/test__openai_proxy.py +413 -0
  35. tests/functions/api_v0_01/test__redeem_credit_code.py +167 -0
  36. tests/functions/api_v0_01/test__sync_emails.py +324 -0
  37. tests/functions/api_v0_01/test__take_payment.py +491 -0
  38. tests/functions/api_v0_01/test__use_activation_code.py +437 -0
  39. tests/functions/api_v1_00/__init__.py +2 -0
  40. tests/functions/api_v1_00/test__analytics.py +416 -0
  41. tests/functions/api_v1_00/test__gemini_proxy.py +352 -0
  42. tests/functions/api_v1_00/test__generic_proxy.py +428 -0
  43. tests/functions/api_v1_00/test__get_payment_details.py +356 -0
  44. tests/functions/api_v1_00/test__openai_proxy.py +449 -0
  45. tests/functions/api_v1_00/test__redeem_credit_code.py +167 -0
  46. tests/functions/api_v1_00/test__sync_emails.py +325 -0
  47. tests/functions/api_v1_00/test__take_payment.py +491 -0
  48. tests/functions/api_v1_00/test__use_activation_code.py +438 -0
  49. tests/functions/test_auth.py +24 -0
  50. tests/functions/test_main_functions.py +73 -0
  51. tests/functions/test_utils_functions.py +222 -0
  52. roksta-0.2.4.dist-info/RECORD +0 -51
  53. {roksta-0.2.4.dist-info → roksta-0.2.6.dist-info}/WHEEL +0 -0
  54. {roksta-0.2.4.dist-info → roksta-0.2.6.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,307 @@
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
+ 'utils',
23
+ 'auth',
24
+ 'google',
25
+ 'google.genai',
26
+ 'google.genai.types',
27
+ ]
28
+ for name in _names_to_fake:
29
+ _orig_sys_modules[name] = sys.modules.get(name)
30
+
31
+ # Fake firebase_functions.https_fn.Response to capture returned data
32
+ firebase_functions = types.ModuleType('firebase_functions')
33
+
34
+ class FakeResponse:
35
+ def __init__(self, response=None, mimetype=None, status=200, **kwargs):
36
+ # Mirror the small subset of the interface tests expect
37
+ self.status_code = status
38
+ if isinstance(response, (dict, list)):
39
+ self._body_text = json.dumps(response)
40
+ else:
41
+ self._body_text = '' if response is None else response
42
+ self.headers = kwargs.get('headers', {})
43
+
44
+ def get_data(self, as_text=False):
45
+ if as_text:
46
+ return self._body_text
47
+ return self._body_text.encode('utf-8')
48
+
49
+ firebase_functions.https_fn = types.SimpleNamespace(Request=object, Response=FakeResponse)
50
+ sys.modules['firebase_functions'] = firebase_functions
51
+
52
+ # Fake utils module (provides functions imported by _gemini_proxy)
53
+ utils_mod = types.ModuleType('utils')
54
+
55
+
56
+ def _fake_create_json_response(success: bool, payload: any, status_code: int):
57
+ response_body = {"success": success, "payload": payload}
58
+ return firebase_functions.https_fn.Response(response=json.dumps(response_body), status=status_code, headers={'Content-Type': 'application/json'})
59
+
60
+
61
+ def _fake_get_api_key(llm_family=None):
62
+ return 'DUMMY_GEMINI_KEY'
63
+
64
+
65
+ def _fake_verify_firebase_token(req):
66
+ # Default: no-op (successful)
67
+ return {}
68
+
69
+
70
+ utils_mod.create_json_response = _fake_create_json_response
71
+ utils_mod.get_api_key = _fake_get_api_key
72
+ utils_mod.verify_firebase_token = _fake_verify_firebase_token
73
+ sys.modules['utils'] = utils_mod
74
+
75
+ # Fake auth module with validate_auth_key
76
+ auth_mod = types.ModuleType('auth')
77
+
78
+
79
+ def _fake_validate_auth_key(val: str) -> bool:
80
+ return True
81
+
82
+
83
+ auth_mod.validate_auth_key = _fake_validate_auth_key
84
+ sys.modules['auth'] = auth_mod
85
+
86
+ # Fake google.genai and types modules
87
+ google_mod = types.ModuleType('google')
88
+ genai_mod = types.ModuleType('google.genai')
89
+ types_mod = types.ModuleType('google.genai.types')
90
+
91
+ class DummyGenerateContentConfig:
92
+ def __init__(self, **kwargs):
93
+ self.kwargs = kwargs
94
+
95
+ class DummyContent:
96
+ def __init__(self, **kwargs):
97
+ self.kwargs = kwargs
98
+
99
+ types_mod.GenerateContentConfig = DummyGenerateContentConfig
100
+ types_mod.Content = DummyContent
101
+
102
+ # Provide a dummy Client implementation; tests will patch this when needed
103
+ class DummyClient:
104
+ def __init__(self, api_key):
105
+ self.api_key = api_key
106
+ class Models:
107
+ def generate_content(self, **params):
108
+ raise NotImplementedError("generate_content not implemented for dummy client")
109
+ self.models = Models()
110
+
111
+ genai_mod.Client = DummyClient
112
+ # Also expose types on genai module (in case of attribute access)
113
+ genai_mod.types = types_mod
114
+
115
+ google_mod.genai = genai_mod
116
+ sys.modules['google'] = google_mod
117
+ sys.modules['google.genai'] = genai_mod
118
+ sys.modules['google.genai.types'] = types_mod
119
+
120
+ # Import the module under test after preparing the fake imports
121
+ repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
122
+ functions_root = os.path.join(repo_root, 'functions')
123
+ module_path = os.path.join(functions_root, 'api_v0_01', '_gemini_proxy.py')
124
+ spec = importlib.util.spec_from_file_location('api_v0_01._gemini_proxy', module_path)
125
+ _gemini = importlib.util.module_from_spec(spec)
126
+ spec.loader.exec_module(_gemini)
127
+
128
+
129
+ # Restore original sys.modules mappings to avoid side-effects for other tests
130
+ for name, orig in _orig_sys_modules.items():
131
+ if orig is None:
132
+ try:
133
+ del sys.modules[name]
134
+ except KeyError:
135
+ pass
136
+ else:
137
+ sys.modules[name] = orig
138
+
139
+ # Helper request stub used in tests
140
+ class DummyRequest:
141
+ def __init__(self, headers=None, method='POST', json_data=None, raise_on_get_json=False):
142
+ self.headers = headers or {}
143
+ self.method = method
144
+ self._json_data = json_data
145
+ self._raise = raise_on_get_json
146
+
147
+ def get_json(self, silent=False):
148
+ if self._raise:
149
+ raise Exception('Malformed JSON')
150
+ return self._json_data
151
+
152
+
153
+ def _parse_response(resp):
154
+ data = resp.get_data(as_text=True)
155
+ return json.loads(data)
156
+
157
+
158
+ # -----------------------------
159
+ # Tests
160
+ # -----------------------------
161
+
162
+
163
+ def test_verify_firebase_token_failure_returns_401():
164
+ req = DummyRequest(headers={_gemini.AUTH_HEADER_NAME: 'ok'}, method='POST')
165
+ with patch.object(_gemini, 'verify_firebase_token', side_effect=Exception('invalid token')):
166
+ resp = _gemini._gemini_proxy(req)
167
+
168
+ assert resp.status_code == 401
169
+ payload = _parse_response(resp)
170
+ assert payload['success'] is False
171
+ assert 'Unauthorized' in payload['payload']
172
+
173
+
174
+ def test_missing_auth_header_returns_401():
175
+ # no app auth header
176
+ req = DummyRequest(headers={}, method='POST', json_data={'model': 'm', 'contents': 'c', 'config': {}})
177
+ with patch.object(_gemini, 'validate_auth_key', return_value=True), patch.object(_gemini, 'verify_firebase_token', return_value={}):
178
+ resp = _gemini._gemini_proxy(req)
179
+
180
+ assert resp.status_code == 401
181
+ payload = _parse_response(resp)
182
+ assert payload['success'] is False
183
+ assert 'Missing app authentication key' in payload['payload']
184
+
185
+
186
+ def test_invalid_auth_key_returns_403():
187
+ headers = {_gemini.AUTH_HEADER_NAME: 'bad'}
188
+ req = DummyRequest(headers=headers, method='POST', json_data={'model': 'm', 'contents': 'c', 'config': {}})
189
+ with patch.object(_gemini, 'validate_auth_key', return_value=False), patch.object(_gemini, 'verify_firebase_token', return_value={}):
190
+ resp = _gemini._gemini_proxy(req)
191
+
192
+ assert resp.status_code == 403
193
+ payload = _parse_response(resp)
194
+ assert payload['success'] is False
195
+ assert 'Invalid app authentication key' in payload['payload']
196
+
197
+
198
+ def test_non_post_method_returns_405():
199
+ headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
200
+ req = DummyRequest(headers=headers, method='GET')
201
+ with patch.object(_gemini, 'validate_auth_key', return_value=True), patch.object(_gemini, 'verify_firebase_token', return_value={}):
202
+ resp = _gemini._gemini_proxy(req)
203
+
204
+ assert resp.status_code == 405
205
+ payload = _parse_response(resp)
206
+ assert payload['success'] is False
207
+ assert 'POST method required' in payload['payload']
208
+
209
+
210
+ def test_malformed_json_returns_400():
211
+ headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
212
+ req = DummyRequest(headers=headers, method='POST', raise_on_get_json=True)
213
+ with patch.object(_gemini, 'validate_auth_key', return_value=True), patch.object(_gemini, 'verify_firebase_token', return_value={}):
214
+ resp = _gemini._gemini_proxy(req)
215
+
216
+ assert resp.status_code == 400
217
+ payload = _parse_response(resp)
218
+ assert payload['success'] is False
219
+ assert 'Invalid JSON payload' in payload['payload']
220
+
221
+
222
+ def test_rehydrate_params_error_returns_400():
223
+ headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
224
+ # missing 'model' to cause KeyError inside rehydrate_params
225
+ req = DummyRequest(headers=headers, method='POST', json_data={'contents': 'x', 'config': {}})
226
+ with patch.object(_gemini, 'validate_auth_key', return_value=True), patch.object(_gemini, 'verify_firebase_token', return_value={}):
227
+ resp = _gemini._gemini_proxy(req)
228
+
229
+ assert resp.status_code == 400
230
+ payload = _parse_response(resp)
231
+ assert payload['success'] is False
232
+ assert 'Error hydrating params' in payload['payload']
233
+
234
+
235
+ def test_get_api_key_failure_returns_500():
236
+ headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
237
+ req = DummyRequest(headers=headers, method='POST', json_data={'model': 'g', 'contents': 'hi', 'config': {}})
238
+ with patch.object(_gemini, 'validate_auth_key', return_value=True), \
239
+ patch.object(_gemini, 'verify_firebase_token', return_value={}), \
240
+ patch.object(_gemini, 'get_api_key', side_effect=Exception('boom')):
241
+ resp = _gemini._gemini_proxy(req)
242
+
243
+ assert resp.status_code == 500
244
+ payload = _parse_response(resp)
245
+ assert payload['success'] is False
246
+ assert 'Could not retrieve API key' in payload['payload']
247
+
248
+
249
+ def test_successful_flow_calls_genai_and_returns_payload():
250
+ headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
251
+ req_payload = {'model': 'gem-model', 'contents': 'hello', 'config': {}}
252
+ req = DummyRequest(headers=headers, method='POST', json_data=req_payload)
253
+
254
+ class FakeClient:
255
+ def __init__(self, api_key):
256
+ self.api_key = api_key
257
+ self.models = self
258
+
259
+ def generate_content(self, **params):
260
+ class FakeResp:
261
+ def to_json_dict(self_inner):
262
+ return { 'result': 'ok', 'received_model': params.get('model') }
263
+ return FakeResp()
264
+
265
+ with patch.object(_gemini, 'validate_auth_key', return_value=True), \
266
+ patch.object(_gemini, 'verify_firebase_token', return_value={}), \
267
+ patch.object(_gemini, 'get_api_key', return_value='GEMINI-KEY'), \
268
+ patch.object(_gemini.genai, 'Client', FakeClient):
269
+ resp = _gemini._gemini_proxy(req)
270
+
271
+ assert resp.status_code == 200
272
+ payload = _parse_response(resp)
273
+ assert payload['success'] is True
274
+ assert isinstance(payload['payload'], dict)
275
+ assert payload['payload']['result'] == 'ok'
276
+ assert payload['payload']['received_model'] == 'gem-model'
277
+
278
+
279
+ def test_genai_exception_with_status_code_is_returned_as_error_status():
280
+ headers = {_gemini.AUTH_HEADER_NAME: 'ok'}
281
+ req_payload = {'model': 'g', 'contents': 'hey', 'config': {}}
282
+ req = DummyRequest(headers=headers, method='POST', json_data=req_payload)
283
+
284
+ class CustomErr(Exception):
285
+ pass
286
+
287
+ err = CustomErr('rate limited')
288
+ err.status_code = 502
289
+
290
+ class ErrClient:
291
+ def __init__(self, api_key):
292
+ self.api_key = api_key
293
+ self.models = self
294
+
295
+ def generate_content(self, **params):
296
+ raise err
297
+
298
+ with patch.object(_gemini, 'validate_auth_key', return_value=True), \
299
+ patch.object(_gemini, 'verify_firebase_token', return_value={}), \
300
+ patch.object(_gemini, 'get_api_key', return_value='GEMINI-KEY'), \
301
+ patch.object(_gemini.genai, 'Client', ErrClient):
302
+ resp = _gemini._gemini_proxy(req)
303
+
304
+ assert resp.status_code == 502
305
+ payload = _parse_response(resp)
306
+ assert payload['success'] is False
307
+ assert 'Internal Server Error' in payload['payload']