roksta 0.2.4__cp313-cp313-win_amd64.whl → 0.2.6__cp313-cp313-win_amd64.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 (79) hide show
  1. roksta/__init__.cp313-win_amd64.pyd +0 -0
  2. roksta/ai/__init__.cp313-win_amd64.pyd +0 -0
  3. roksta/ai/call_ai.cp313-win_amd64.pyd +0 -0
  4. roksta/ai/gemini.cp313-win_amd64.pyd +0 -0
  5. roksta/ai/generic.cp313-win_amd64.pyd +0 -0
  6. roksta/ai/llm.cp313-win_amd64.pyd +0 -0
  7. roksta/ai/openai.cp313-win_amd64.pyd +0 -0
  8. roksta/ai/tools.cp313-win_amd64.pyd +0 -0
  9. roksta/analytics.cp313-win_amd64.pyd +0 -0
  10. roksta/balance.cp313-win_amd64.pyd +0 -0
  11. roksta/build_project.cp313-win_amd64.pyd +0 -0
  12. roksta/chat_workflow.cp313-win_amd64.pyd +0 -0
  13. roksta/check_for_updates.cp313-win_amd64.pyd +0 -0
  14. roksta/checkpoints.cp313-win_amd64.pyd +0 -0
  15. roksta/clarify_goal.cp313-win_amd64.pyd +0 -0
  16. roksta/codebase_listing.cp313-win_amd64.pyd +0 -0
  17. roksta/command_handlers.cp313-win_amd64.pyd +0 -0
  18. roksta/create_default_config.cp313-win_amd64.pyd +0 -0
  19. roksta/default_config.cp313-win_amd64.pyd +0 -0
  20. roksta/enums.cp313-win_amd64.pyd +0 -0
  21. roksta/env.cp313-win_amd64.pyd +0 -0
  22. roksta/extended_text_area.cp313-win_amd64.pyd +0 -0
  23. roksta/firebase.cp313-win_amd64.pyd +0 -0
  24. roksta/firebase_auth_web.cp313-win_amd64.pyd +0 -0
  25. roksta/firebase_config.cp313-win_amd64.pyd +0 -0
  26. roksta/fix_tests.cp313-win_amd64.pyd +0 -0
  27. roksta/gen_codebase_summaries.cp313-win_amd64.pyd +0 -0
  28. roksta/gen_one_line_goal.cp313-win_amd64.pyd +0 -0
  29. roksta/get_codebase_structure.cp313-win_amd64.pyd +0 -0
  30. roksta/get_failing_tests.cp313-win_amd64.pyd +0 -0
  31. roksta/goal_workflow.cp313-win_amd64.pyd +0 -0
  32. roksta/init_codebase.cp313-win_amd64.pyd +0 -0
  33. roksta/lint_code.cp313-win_amd64.pyd +0 -0
  34. roksta/logger.cp313-win_amd64.pyd +0 -0
  35. roksta/main.cp313-win_amd64.pyd +0 -0
  36. roksta/make_issue.cp313-win_amd64.pyd +0 -0
  37. roksta/new_features.cp313-win_amd64.pyd +0 -0
  38. roksta/parse_readme.cp313-win_amd64.pyd +0 -0
  39. roksta/propose_solution.cp313-win_amd64.pyd +0 -0
  40. roksta/response_formats.cp313-win_amd64.pyd +0 -0
  41. roksta/rewrite_goal.cp313-win_amd64.pyd +0 -0
  42. roksta/roksta.cp313-win_amd64.pyd +0 -0
  43. roksta/run_cli_goal.cp313-win_amd64.pyd +0 -0
  44. roksta/select_files.cp313-win_amd64.pyd +0 -0
  45. roksta/tips.cp313-win_amd64.pyd +0 -0
  46. roksta/utils.cp313-win_amd64.pyd +0 -0
  47. roksta/write_code.cp313-win_amd64.pyd +0 -0
  48. {roksta-0.2.4.dist-info → roksta-0.2.6.dist-info}/METADATA +1 -1
  49. roksta-0.2.6.dist-info/RECORD +78 -0
  50. {roksta-0.2.4.dist-info → roksta-0.2.6.dist-info}/top_level.txt +1 -0
  51. tests/__init__.py +2 -0
  52. tests/conftest.py +169 -0
  53. tests/functions/__init__.py +2 -0
  54. tests/functions/api_v0_01/__init__.py +2 -0
  55. tests/functions/api_v0_01/test__analytics.py +417 -0
  56. tests/functions/api_v0_01/test__gemini_proxy.py +307 -0
  57. tests/functions/api_v0_01/test__generic_proxy.py +399 -0
  58. tests/functions/api_v0_01/test__get_payment_details.py +356 -0
  59. tests/functions/api_v0_01/test__openai_proxy.py +413 -0
  60. tests/functions/api_v0_01/test__redeem_credit_code.py +167 -0
  61. tests/functions/api_v0_01/test__sync_emails.py +324 -0
  62. tests/functions/api_v0_01/test__take_payment.py +491 -0
  63. tests/functions/api_v0_01/test__use_activation_code.py +437 -0
  64. tests/functions/api_v1_00/__init__.py +2 -0
  65. tests/functions/api_v1_00/test__analytics.py +416 -0
  66. tests/functions/api_v1_00/test__gemini_proxy.py +352 -0
  67. tests/functions/api_v1_00/test__generic_proxy.py +428 -0
  68. tests/functions/api_v1_00/test__get_payment_details.py +356 -0
  69. tests/functions/api_v1_00/test__openai_proxy.py +449 -0
  70. tests/functions/api_v1_00/test__redeem_credit_code.py +167 -0
  71. tests/functions/api_v1_00/test__sync_emails.py +325 -0
  72. tests/functions/api_v1_00/test__take_payment.py +491 -0
  73. tests/functions/api_v1_00/test__use_activation_code.py +438 -0
  74. tests/functions/test_auth.py +24 -0
  75. tests/functions/test_main_functions.py +73 -0
  76. tests/functions/test_utils_functions.py +222 -0
  77. roksta-0.2.4.dist-info/RECORD +0 -51
  78. {roksta-0.2.4.dist-info → roksta-0.2.6.dist-info}/WHEEL +0 -0
  79. {roksta-0.2.4.dist-info → roksta-0.2.6.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,399 @@
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 _generic_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
+ 'openai',
25
+ ]
26
+ for name in _names_to_fake:
27
+ _orig_sys_modules[name] = sys.modules.get(name)
28
+
29
+ # Fake firebase_functions.https_fn.Response to capture returned data
30
+ firebase_functions = types.ModuleType('firebase_functions')
31
+
32
+ class FakeResponse:
33
+ def __init__(self, response=None, mimetype=None, status=200, **kwargs):
34
+ # Mirror the small subset of the interface tests expect
35
+ self.status_code = status
36
+ if isinstance(response, (dict, list)):
37
+ self._body_text = json.dumps(response)
38
+ else:
39
+ self._body_text = '' if response is None else response
40
+ self.headers = kwargs.get('headers', {})
41
+
42
+ def get_data(self, as_text=False):
43
+ if as_text:
44
+ return self._body_text
45
+ return self._body_text.encode('utf-8')
46
+
47
+ firebase_functions.https_fn = types.SimpleNamespace(Request=object, Response=FakeResponse)
48
+ sys.modules['firebase_functions'] = firebase_functions
49
+
50
+ # Fake utils module (provides functions imported by _generic_proxy)
51
+ utils_mod = types.ModuleType('utils')
52
+
53
+
54
+ def _fake_create_json_response(success: bool, payload: any, status_code: int):
55
+ response_body = {"success": success, "payload": payload}
56
+ return firebase_functions.https_fn.Response(response=json.dumps(response_body), status=status_code, headers={'Content-Type': 'application/json'})
57
+
58
+
59
+ def _fake_get_api_key(llm_family=None):
60
+ return 'DUMMY_KEY'
61
+
62
+
63
+ def _fake_verify_firebase_token(req):
64
+ # Default: no-op (successful)
65
+ return {}
66
+
67
+
68
+ utils_mod.create_json_response = _fake_create_json_response
69
+ utils_mod.get_api_key = _fake_get_api_key
70
+ utils_mod.verify_firebase_token = _fake_verify_firebase_token
71
+ sys.modules['utils'] = utils_mod
72
+
73
+ # Fake auth module with validate_auth_key
74
+ auth_mod = types.ModuleType('auth')
75
+
76
+
77
+ def _fake_validate_auth_key(val: str) -> bool:
78
+ return True
79
+
80
+
81
+ auth_mod.validate_auth_key = _fake_validate_auth_key
82
+ sys.modules['auth'] = auth_mod
83
+
84
+ # Fake openai module with APIError and a default OpenAI class
85
+ openai_mod = types.ModuleType('openai')
86
+
87
+ class APIError(Exception):
88
+ pass
89
+
90
+
91
+ class DummyOpenAI:
92
+ def __init__(self, base_url=None, api_key=None, timeout=None):
93
+ self.base_url = base_url
94
+ self.api_key = api_key
95
+ self.timeout = timeout
96
+ class Completions:
97
+ def create(self, **params):
98
+ raise NotImplementedError("create not implemented for dummy client")
99
+ def parse(self, **params):
100
+ raise NotImplementedError("parse not implemented for dummy client")
101
+ class Chat:
102
+ def __init__(self):
103
+ self.completions = Completions()
104
+ self.chat = Chat()
105
+
106
+ openai_mod.OpenAI = DummyOpenAI
107
+ openai_mod.APIError = APIError
108
+ sys.modules['openai'] = openai_mod
109
+
110
+ # Import the module under test after preparing the fake imports
111
+ repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
112
+ functions_root = os.path.join(repo_root, 'functions')
113
+ module_path = os.path.join(functions_root, 'api_v0_01', '_generic_proxy.py')
114
+ spec = importlib.util.spec_from_file_location('api_v0_01._generic_proxy', module_path)
115
+ _generic = importlib.util.module_from_spec(spec)
116
+ spec.loader.exec_module(_generic)
117
+
118
+ # Restore original sys.modules mappings to avoid side-effects for other tests
119
+ for name, orig in _orig_sys_modules.items():
120
+ if orig is None:
121
+ try:
122
+ del sys.modules[name]
123
+ except KeyError:
124
+ pass
125
+ else:
126
+ sys.modules[name] = orig
127
+
128
+
129
+ # Helper request stub used in tests
130
+ class DummyRequest:
131
+ def __init__(self, headers=None, method='POST', json_data=None, raise_on_get_json=False):
132
+ self.headers = headers or {}
133
+ self.method = method
134
+ self._json_data = json_data
135
+ self._raise = raise_on_get_json
136
+
137
+ def get_json(self, silent=False):
138
+ if self._raise:
139
+ raise Exception('Malformed JSON')
140
+ return self._json_data
141
+
142
+
143
+ def _parse_response(resp):
144
+ data = resp.get_data(as_text=True)
145
+ return json.loads(data)
146
+
147
+
148
+ # -----------------------------
149
+ # Tests
150
+ # -----------------------------
151
+
152
+
153
+ def test_verify_firebase_token_failure_returns_401():
154
+ req = DummyRequest(headers={_generic.AUTH_HEADER_NAME: 'ok'}, method='POST')
155
+ with patch.object(_generic, 'verify_firebase_token', side_effect=Exception('invalid token')):
156
+ resp = _generic._generic_proxy(req)
157
+
158
+ assert resp.status_code == 401
159
+ payload = _parse_response(resp)
160
+ assert payload['success'] is False
161
+ assert 'Unauthorized' in payload['payload']
162
+
163
+
164
+ def test_missing_auth_header_returns_401():
165
+ req = DummyRequest(headers={}, method='POST', json_data={'llm_family': 'openai', 'call_type': 'create', 'call_params': {}})
166
+ with patch.object(_generic, 'validate_auth_key', return_value=True), patch.object(_generic, 'verify_firebase_token', return_value={}):
167
+ resp = _generic._generic_proxy(req)
168
+
169
+ assert resp.status_code == 401
170
+ payload = _parse_response(resp)
171
+ assert payload['success'] is False
172
+ assert 'Missing app authentication key' in payload['payload']
173
+
174
+
175
+ def test_invalid_auth_key_returns_403():
176
+ headers = {_generic.AUTH_HEADER_NAME: 'bad'}
177
+ req = DummyRequest(headers=headers, method='POST', json_data={'llm_family': 'openai', 'call_type': 'create', 'call_params': {}})
178
+ with patch.object(_generic, 'validate_auth_key', return_value=False), patch.object(_generic, 'verify_firebase_token', return_value={}):
179
+ resp = _generic._generic_proxy(req)
180
+
181
+ assert resp.status_code == 403
182
+ payload = _parse_response(resp)
183
+ assert payload['success'] is False
184
+ assert 'Invalid app authentication key' in payload['payload']
185
+
186
+
187
+ def test_non_post_method_returns_405():
188
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
189
+ req = DummyRequest(headers=headers, method='GET')
190
+ with patch.object(_generic, 'validate_auth_key', return_value=True), patch.object(_generic, 'verify_firebase_token', return_value={}):
191
+ resp = _generic._generic_proxy(req)
192
+
193
+ assert resp.status_code == 405
194
+ payload = _parse_response(resp)
195
+ assert payload['success'] is False
196
+ assert 'POST method required' in payload['payload']
197
+
198
+
199
+ def test_malformed_json_returns_400():
200
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
201
+ req = DummyRequest(headers=headers, method='POST', raise_on_get_json=True)
202
+ with patch.object(_generic, 'validate_auth_key', return_value=True), patch.object(_generic, 'verify_firebase_token', return_value={}):
203
+ resp = _generic._generic_proxy(req)
204
+
205
+ assert resp.status_code == 400
206
+ payload = _parse_response(resp)
207
+ assert payload['success'] is False
208
+ assert 'Invalid JSON payload' in payload['payload']
209
+
210
+
211
+ def test_missing_llm_family_returns_400():
212
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
213
+ req = DummyRequest(headers=headers, method='POST', json_data={'call_type': 'create', 'call_params': {}})
214
+ with patch.object(_generic, 'validate_auth_key', return_value=True), patch.object(_generic, 'verify_firebase_token', return_value={}):
215
+ resp = _generic._generic_proxy(req)
216
+
217
+ assert resp.status_code == 400
218
+ payload = _parse_response(resp)
219
+ assert payload['success'] is False
220
+ assert "Missing 'llm_family'" in payload['payload']
221
+
222
+
223
+ def test_invalid_llm_family_returns_400():
224
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
225
+ req = DummyRequest(headers=headers, method='POST', json_data={'llm_family': 'notreal', 'call_type': 'create', 'call_params': {}})
226
+ with patch.object(_generic, 'validate_auth_key', return_value=True), patch.object(_generic, 'verify_firebase_token', return_value={}):
227
+ resp = _generic._generic_proxy(req)
228
+
229
+ assert resp.status_code == 400
230
+ payload = _parse_response(resp)
231
+ assert payload['success'] is False
232
+ assert 'Invalid LLM family' in payload['payload']
233
+
234
+
235
+ def test_missing_or_invalid_call_type_or_call_params_returns_400():
236
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
237
+ # Use an empty dict for call_params so the proxy can safely access call_params.get()
238
+ req = DummyRequest(headers=headers, method='POST', json_data={'llm_family': 'openai', 'call_type': None, 'call_params': {}})
239
+ with patch.object(_generic, 'validate_auth_key', return_value=True), patch.object(_generic, 'verify_firebase_token', return_value={}):
240
+ resp = _generic._generic_proxy(req)
241
+
242
+ assert resp.status_code == 400
243
+ payload = _parse_response(resp)
244
+ assert payload['success'] is False
245
+ assert 'Missing or invalid required fields' in payload['payload']
246
+
247
+
248
+ def test_invalid_call_type_returns_400():
249
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
250
+ req = DummyRequest(headers=headers, method='POST', json_data={'llm_family': 'openai', 'call_type': 'bad', 'call_params': {}})
251
+ with patch.object(_generic, 'validate_auth_key', return_value=True), patch.object(_generic, 'verify_firebase_token', return_value={}):
252
+ resp = _generic._generic_proxy(req)
253
+
254
+ assert resp.status_code == 400
255
+ payload = _parse_response(resp)
256
+ assert payload['success'] is False
257
+ assert "Invalid 'call_type'" in payload['payload']
258
+
259
+
260
+ def test_get_api_key_failure_returns_500():
261
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
262
+ req = DummyRequest(headers=headers, method='POST', json_data={'llm_family': 'openai', 'call_type': 'create', 'call_params': {'model': 'm'}})
263
+ with patch.object(_generic, 'validate_auth_key', return_value=True), \
264
+ patch.object(_generic, 'verify_firebase_token', return_value={}), \
265
+ patch.object(_generic, 'get_api_key', side_effect=Exception('boom')):
266
+ resp = _generic._generic_proxy(req)
267
+
268
+ assert resp.status_code == 500
269
+ payload = _parse_response(resp)
270
+ assert payload['success'] is False
271
+ assert 'Could not retrieve API key' in payload['payload']
272
+
273
+
274
+ def test_parse_without_response_format_returns_400():
275
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
276
+ req = DummyRequest(headers=headers, method='POST', json_data={'llm_family': 'openai', 'call_type': 'parse', 'call_params': {'model': 'm'}})
277
+
278
+ class NoopOpenAI:
279
+ def __init__(self, api_key=None, base_url=None, timeout=None):
280
+ class Completions:
281
+ def create(self, **p):
282
+ return None
283
+ def parse(self, **p):
284
+ return None
285
+ class Chat:
286
+ def __init__(self):
287
+ self.completions = Completions()
288
+ self.chat = Chat()
289
+
290
+ with patch.object(_generic, 'validate_auth_key', return_value=True), \
291
+ patch.object(_generic, 'verify_firebase_token', return_value={}), \
292
+ patch.object(_generic, 'get_api_key', return_value='KEY'), \
293
+ patch.object(_generic.openai, 'OpenAI', NoopOpenAI):
294
+ resp = _generic._generic_proxy(req)
295
+
296
+ assert resp.status_code == 400
297
+ payload = _parse_response(resp)
298
+ assert payload['success'] is False
299
+ assert "Missing 'response_format' for parse call" in payload['payload']
300
+
301
+
302
+ def test_successful_create_calls_openai_and_returns_payload():
303
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
304
+ req_payload = {'llm_family': 'openai', 'call_type': 'create', 'call_params': {'model': 'gem-model', 'input': 'hello'}}
305
+ req = DummyRequest(headers=headers, method='POST', json_data=req_payload)
306
+
307
+ class FakeClient:
308
+ def __init__(self, api_key, base_url=None, timeout=None):
309
+ self.api_key = api_key
310
+ class Completions:
311
+ def create(self_inner, **params):
312
+ class FakeResp:
313
+ def model_dump(self_inner, mode='json'):
314
+ return {'result': 'ok', 'received_model': params.get('model')}
315
+ return FakeResp()
316
+ class Chat:
317
+ def __init__(self):
318
+ self.completions = Completions()
319
+ self.chat = Chat()
320
+
321
+ with patch.object(_generic, 'validate_auth_key', return_value=True), \
322
+ patch.object(_generic, 'verify_firebase_token', return_value={}), \
323
+ patch.object(_generic, 'get_api_key', return_value='GEMINI-KEY'), \
324
+ patch.object(_generic.openai, 'OpenAI', FakeClient):
325
+ resp = _generic._generic_proxy(req)
326
+
327
+ assert resp.status_code == 200
328
+ payload = _parse_response(resp)
329
+ assert payload['success'] is True
330
+ assert isinstance(payload['payload'], dict)
331
+ assert payload['payload']['result'] == 'ok'
332
+ assert payload['payload']['received_model'] == 'gem-model'
333
+
334
+
335
+ def test_openai_api_error_with_status_code_is_returned_as_error_status():
336
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
337
+ req_payload = {'llm_family': 'openai', 'call_type': 'create', 'call_params': {'model': 'g', 'input': 'hey'}}
338
+ req = DummyRequest(headers=headers, method='POST', json_data=req_payload)
339
+
340
+ # Use the module's openai.APIError so the proxy's except block catches it
341
+ err = _generic.openai.APIError('rate limited')
342
+ err.status_code = 502
343
+
344
+ class ErrClient:
345
+ def __init__(self, api_key=None, base_url=None, timeout=None):
346
+ class Completions:
347
+ def create(self_inner, **params):
348
+ raise err
349
+ class Chat:
350
+ def __init__(self):
351
+ self.completions = Completions()
352
+ self.chat = Chat()
353
+
354
+ with patch.object(_generic, 'validate_auth_key', return_value=True), \
355
+ patch.object(_generic, 'verify_firebase_token', return_value={}), \
356
+ patch.object(_generic, 'get_api_key', return_value='GEMINI-KEY'), \
357
+ patch.object(_generic.openai, 'OpenAI', ErrClient):
358
+ resp = _generic._generic_proxy(req)
359
+
360
+ assert resp.status_code == 502
361
+ payload = _parse_response(resp)
362
+ assert payload['success'] is False
363
+ assert 'OpenAI API Error' in payload['payload']
364
+
365
+
366
+ def test_parse_replaces_response_format_name_with_model_and_calls_openai():
367
+ headers = {_generic.AUTH_HEADER_NAME: 'ok'}
368
+ req_payload = {'llm_family': 'openai', 'call_type': 'parse', 'call_params': {'model': 'gem-model', 'input': 'hello', 'response_format': 'FileSummaryModel'}}
369
+ req = DummyRequest(headers=headers, method='POST', json_data=req_payload)
370
+
371
+ class FakeClient2:
372
+ def __init__(self, api_key, base_url=None, timeout=None):
373
+ class Completions:
374
+ def parse(self_inner, **params):
375
+ class FakeResp:
376
+ def model_dump(self_inner, mode='json'):
377
+ return {
378
+ 'result': 'ok',
379
+ 'received_model': params.get('model'),
380
+ 'is_response_format_string': isinstance(params.get('response_format'), str)
381
+ }
382
+ return FakeResp()
383
+ class Chat:
384
+ def __init__(self):
385
+ self.completions = Completions()
386
+ self.chat = Chat()
387
+
388
+ with patch.object(_generic, 'validate_auth_key', return_value=True), \
389
+ patch.object(_generic, 'verify_firebase_token', return_value={}), \
390
+ patch.object(_generic, 'get_api_key', return_value='GEMINI-KEY'), \
391
+ patch.object(_generic.openai, 'OpenAI', FakeClient2):
392
+ resp = _generic._generic_proxy(req)
393
+
394
+ assert resp.status_code == 200
395
+ payload = _parse_response(resp)
396
+ assert payload['success'] is True
397
+ assert payload['payload']['result'] == 'ok'
398
+ # The proxy should have replaced the response_format string with the model (i.e., not a string)
399
+ assert payload['payload']['is_response_format_string'] is False