roksta 0.2.7__cp311-cp311-win_amd64.whl → 0.3.2__cp311-cp311-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 (112) hide show
  1. roksta/__init__.cp311-win_amd64.pyd +0 -0
  2. roksta/ai/__init__.cp311-win_amd64.pyd +0 -0
  3. roksta/ai/call_ai.cp311-win_amd64.pyd +0 -0
  4. roksta/ai/gemini.cp311-win_amd64.pyd +0 -0
  5. roksta/ai/generic.cp311-win_amd64.pyd +0 -0
  6. roksta/ai/llm.cp311-win_amd64.pyd +0 -0
  7. roksta/ai/openai.cp311-win_amd64.pyd +0 -0
  8. roksta/ai/tools/__init__.cp311-win_amd64.pyd +0 -0
  9. roksta/ai/tools/delete_file.cp311-win_amd64.pyd +0 -0
  10. roksta/ai/tools/edit_file.cp311-win_amd64.pyd +0 -0
  11. roksta/ai/tools/final_response.cp311-win_amd64.pyd +0 -0
  12. roksta/ai/tools/get_file_summaries.cp311-win_amd64.pyd +0 -0
  13. roksta/ai/tools/read_file.cp311-win_amd64.pyd +0 -0
  14. roksta/ai/tools/regex_replace.cp311-win_amd64.pyd +0 -0
  15. roksta/ai/tools/shell_any.cp311-win_amd64.pyd +0 -0
  16. roksta/ai/tools/shell_limited.cp311-win_amd64.pyd +0 -0
  17. roksta/ai/tools/tool_defs.cp311-win_amd64.pyd +0 -0
  18. roksta/ai/tools/tool_utils.cp311-win_amd64.pyd +0 -0
  19. roksta/ai/tools/write_file.cp311-win_amd64.pyd +0 -0
  20. roksta/analytics.cp311-win_amd64.pyd +0 -0
  21. roksta/balance.cp311-win_amd64.pyd +0 -0
  22. roksta/build_project.cp311-win_amd64.pyd +0 -0
  23. roksta/chat_workflow.cp311-win_amd64.pyd +0 -0
  24. roksta/check_for_updates.cp311-win_amd64.pyd +0 -0
  25. roksta/checkpoints.cp311-win_amd64.pyd +0 -0
  26. roksta/clarify_goal.cp311-win_amd64.pyd +0 -0
  27. roksta/codebase_listing.cp311-win_amd64.pyd +0 -0
  28. roksta/command_handlers/__init__.cp311-win_amd64.pyd +0 -0
  29. roksta/command_handlers/handle_activate_command.cp311-win_amd64.pyd +0 -0
  30. roksta/command_handlers/handle_add_funds_command.cp311-win_amd64.pyd +0 -0
  31. roksta/command_handlers/handle_auto_charge_command.cp311-win_amd64.pyd +0 -0
  32. roksta/command_handlers/handle_auto_commit_command.cp311-win_amd64.pyd +0 -0
  33. roksta/command_handlers/handle_building_command.cp311-win_amd64.pyd +0 -0
  34. roksta/command_handlers/handle_chat_command.cp311-win_amd64.pyd +0 -0
  35. roksta/command_handlers/handle_dev_rate_command.cp311-win_amd64.pyd +0 -0
  36. roksta/command_handlers/handle_feedback_command.cp311-win_amd64.pyd +0 -0
  37. roksta/command_handlers/handle_goal_command.cp311-win_amd64.pyd +0 -0
  38. roksta/command_handlers/handle_help_command.cp311-win_amd64.pyd +0 -0
  39. roksta/command_handlers/handle_init_command.cp311-win_amd64.pyd +0 -0
  40. roksta/command_handlers/handle_linting_command.cp311-win_amd64.pyd +0 -0
  41. roksta/command_handlers/handle_login_command.cp311-win_amd64.pyd +0 -0
  42. roksta/command_handlers/handle_logout_command.cp311-win_amd64.pyd +0 -0
  43. roksta/command_handlers/handle_payment_details_command.cp311-win_amd64.pyd +0 -0
  44. roksta/command_handlers/handle_quit_command.cp311-win_amd64.pyd +0 -0
  45. roksta/command_handlers/handle_redeem_command.cp311-win_amd64.pyd +0 -0
  46. roksta/command_handlers/handle_request_activation_command.cp311-win_amd64.pyd +0 -0
  47. roksta/command_handlers/handle_testing_command.cp311-win_amd64.pyd +0 -0
  48. roksta/command_handlers/handle_usage_command.cp311-win_amd64.pyd +0 -0
  49. roksta/create_default_config.cp311-win_amd64.pyd +0 -0
  50. roksta/default_config.cp311-win_amd64.pyd +0 -0
  51. roksta/enums.cp311-win_amd64.pyd +0 -0
  52. roksta/env.cp311-win_amd64.pyd +0 -0
  53. roksta/extended_text_area.cp311-win_amd64.pyd +0 -0
  54. roksta/firebase.cp311-win_amd64.pyd +0 -0
  55. roksta/firebase_auth_web.cp311-win_amd64.pyd +0 -0
  56. roksta/firebase_config.cp311-win_amd64.pyd +0 -0
  57. roksta/fix_tests.cp311-win_amd64.pyd +0 -0
  58. roksta/gen_codebase_summaries.cp311-win_amd64.pyd +0 -0
  59. roksta/gen_one_line_goal.cp311-win_amd64.pyd +0 -0
  60. roksta/get_codebase_structure.cp311-win_amd64.pyd +0 -0
  61. roksta/get_failing_tests.cp311-win_amd64.pyd +0 -0
  62. roksta/goal_workflow.cp311-win_amd64.pyd +0 -0
  63. roksta/init_codebase.cp311-win_amd64.pyd +0 -0
  64. roksta/lint_code.cp311-win_amd64.pyd +0 -0
  65. roksta/logger.cp311-win_amd64.pyd +0 -0
  66. roksta/main.cp311-win_amd64.pyd +0 -0
  67. roksta/make_issue.cp311-win_amd64.pyd +0 -0
  68. roksta/new_features.cp311-win_amd64.pyd +0 -0
  69. roksta/parse_readme.cp311-win_amd64.pyd +0 -0
  70. roksta/propose_solution.cp311-win_amd64.pyd +0 -0
  71. roksta/response_formats.cp311-win_amd64.pyd +0 -0
  72. roksta/rewrite_goal.cp311-win_amd64.pyd +0 -0
  73. roksta/roksta.cp311-win_amd64.pyd +0 -0
  74. roksta/run_cli_goal.cp311-win_amd64.pyd +0 -0
  75. roksta/select_files.cp311-win_amd64.pyd +0 -0
  76. roksta/tips.cp311-win_amd64.pyd +0 -0
  77. roksta/utils.cp311-win_amd64.pyd +0 -0
  78. roksta/write_code.cp311-win_amd64.pyd +0 -0
  79. {roksta-0.2.7.dist-info → roksta-0.3.2.dist-info}/METADATA +2 -1
  80. roksta-0.3.2.dist-info/RECORD +121 -0
  81. tests/conftest.py +42 -0
  82. tests/functions/{api_v0_01 → api_v1_01}/__init__.py +1 -1
  83. tests/functions/{api_v0_01 → api_v1_01}/test__analytics.py +2 -3
  84. tests/functions/{api_v0_01 → api_v1_01}/test__gemini_proxy.py +51 -6
  85. tests/functions/{api_v0_01 → api_v1_01}/test__generic_proxy.py +31 -2
  86. tests/functions/{api_v0_01 → api_v1_01}/test__get_payment_details.py +2 -2
  87. tests/functions/{api_v0_01 → api_v1_01}/test__openai_proxy.py +50 -14
  88. tests/functions/{api_v0_01 → api_v1_01}/test__redeem_credit_code.py +2 -2
  89. tests/functions/{api_v0_01 → api_v1_01}/test__sync_emails.py +3 -2
  90. tests/functions/{api_v0_01 → api_v1_01}/test__take_payment.py +2 -2
  91. tests/functions/{api_v0_01 → api_v1_01}/test__use_activation_code.py +3 -2
  92. tests/functions/api_v1_02/__init__.py +2 -0
  93. tests/functions/api_v1_02/test__analytics.py +416 -0
  94. tests/functions/api_v1_02/test__gemini_proxy.py +352 -0
  95. tests/functions/api_v1_02/test__generic_proxy.py +428 -0
  96. tests/functions/api_v1_02/test__get_payment_details.py +356 -0
  97. tests/functions/api_v1_02/test__openai_proxy.py +449 -0
  98. tests/functions/api_v1_02/test__redeem_credit_code.py +167 -0
  99. tests/functions/api_v1_02/test__sync_emails.py +325 -0
  100. tests/functions/api_v1_02/test__take_payment.py +491 -0
  101. tests/functions/api_v1_02/test__use_activation_code.py +438 -0
  102. tests/functions/api_v1_02/test_proxy_keyword_replacement.py +557 -0
  103. tests/functions/api_v1_02/test_replace_keywords.py +74 -0
  104. tests/functions/test_utils.py +484 -0
  105. roksta/ai/tools.cp311-win_amd64.pyd +0 -0
  106. roksta/command_handlers.cp311-win_amd64.pyd +0 -0
  107. roksta-0.2.7.dist-info/RECORD +0 -78
  108. tests/functions/test_utils_functions.py +0 -222
  109. {roksta-0.2.7.dist-info → roksta-0.3.2.dist-info}/WHEEL +0 -0
  110. {roksta-0.2.7.dist-info → roksta-0.3.2.dist-info}/entry_points.txt +0 -0
  111. {roksta-0.2.7.dist-info → roksta-0.3.2.dist-info}/top_level.txt +0 -0
  112. /tests/functions/{test_main_functions.py → test_main.py} +0 -0
@@ -0,0 +1,484 @@
1
+ import importlib.util
2
+ import os
3
+ import sys
4
+ import types
5
+ import json
6
+ import base64
7
+ import uuid
8
+ import pytest
9
+
10
+
11
+ # Helper to create simple module stubs
12
+ def _make_fake_firebase_functions():
13
+ fake_https = types.ModuleType('firebase_functions.https_fn')
14
+
15
+ class FakeResponse:
16
+ def __init__(self, response=None, mimetype=None, status=200, **kwargs):
17
+ # Mirror the small subset of the real Response API used by the code/tests
18
+ self.status_code = status
19
+ if isinstance(response, bytes):
20
+ self._text = response.decode('utf-8')
21
+ else:
22
+ # If a non-string is passed (unlikely), coerce to JSON text for assertions
23
+ self._text = response if isinstance(response, str) else (json.dumps(response) if response is not None else '')
24
+ self.mimetype = mimetype
25
+ self.headers = {'Content-Type': mimetype} if mimetype else {}
26
+
27
+ def get_data(self, as_text=False):
28
+ return self._text if as_text else self._text.encode('utf-8')
29
+
30
+ fake_https.Response = FakeResponse
31
+ # Provide a Request attribute used in type annotations in the module under test
32
+ fake_https.Request = types.SimpleNamespace
33
+
34
+ fake_root = types.ModuleType('firebase_functions')
35
+ fake_root.https_fn = fake_https
36
+ return fake_root, fake_https
37
+
38
+
39
+ def _make_fake_firebase_admin():
40
+ fake = types.ModuleType('firebase_admin')
41
+ # Minimal auth object so import succeeds; tests here don't rely on actual auth behaviour
42
+ fake.auth = types.SimpleNamespace(verify_id_token=lambda token: {'uid': 'test'})
43
+ return fake
44
+
45
+
46
+ def _make_fake_google_secretmanager():
47
+ google = types.ModuleType('google')
48
+ google_cloud = types.ModuleType('google.cloud')
49
+ secretmanager = types.ModuleType('google.cloud.secretmanager')
50
+
51
+ class FakeClient:
52
+ def access_secret_version(self, request):
53
+ raise RuntimeError("SecretManager should not be called in these tests")
54
+
55
+ secretmanager.SecretManagerServiceClient = FakeClient
56
+ google_cloud.secretmanager = secretmanager
57
+ google.cloud = google_cloud
58
+ return google, google_cloud, secretmanager
59
+
60
+
61
+ def _load_utils_module(monkeypatch):
62
+ """Load a fresh copy of functions/utils.py as an isolated module.
63
+
64
+ This helper inserts necessary fake modules into sys.modules via the
65
+ provided monkeypatch fixture before loading the module so its import-time
66
+ dependencies succeed and are controllable by the tests.
67
+ """
68
+ repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
69
+ utils_path = os.path.join(repo_root, 'functions', 'utils.py')
70
+ assert os.path.exists(utils_path), f"Expected functions/utils.py to exist at {utils_path}"
71
+
72
+ # Prepare and register lightweight stubs needed at import time
73
+ fake_firebase_root, fake_https = _make_fake_firebase_functions()
74
+ monkeypatch.setitem(sys.modules, 'firebase_functions', fake_firebase_root)
75
+ monkeypatch.setitem(sys.modules, 'firebase_functions.https_fn', fake_https)
76
+
77
+ fake_firebase_admin = _make_fake_firebase_admin()
78
+ monkeypatch.setitem(sys.modules, 'firebase_admin', fake_firebase_admin)
79
+
80
+ google, google_cloud, secretmanager = _make_fake_google_secretmanager()
81
+ monkeypatch.setitem(sys.modules, 'google', google)
82
+ monkeypatch.setitem(sys.modules, 'google.cloud', google_cloud)
83
+ monkeypatch.setitem(sys.modules, 'google.cloud.secretmanager', secretmanager)
84
+
85
+ # Create a unique module name so each test gets a fresh module object
86
+ module_name = f"tests.utils_isolated_{uuid.uuid4().hex}"
87
+ spec = importlib.util.spec_from_file_location(module_name, utils_path)
88
+ module = importlib.util.module_from_spec(spec)
89
+ # Ensure the new module is discoverable under its name during execution
90
+ monkeypatch.setitem(sys.modules, module_name, module)
91
+ spec.loader.exec_module(module)
92
+ return module
93
+
94
+
95
+ def test_create_json_response_returns_expected_json_and_status(monkeypatch):
96
+ mod = _load_utils_module(monkeypatch)
97
+
98
+ resp = mod.create_json_response(True, {"alpha": 1}, 201)
99
+
100
+ assert hasattr(resp, 'status_code')
101
+ assert resp.status_code == 201
102
+
103
+ body_text = resp.get_data(as_text=True)
104
+ parsed = json.loads(body_text)
105
+ assert parsed == {"success": True, "payload": {"alpha": 1}}
106
+
107
+
108
+ def test_get_sealedbox_private_key_raises_when_pynacl_privatekey_missing(monkeypatch):
109
+ # Ensure a nacl.public module exists but does NOT expose PrivateKey so the
110
+ # import inside get_sealedbox_private_key raises and the function reports
111
+ # that encryption support is unavailable.
112
+ nacl_mod = types.ModuleType('nacl')
113
+ nacl_public = types.ModuleType('nacl.public')
114
+
115
+ # Provide PublicKey/SealedBox but omit PrivateKey to trigger the import error
116
+ class PublicKeyStub:
117
+ def __init__(self, data):
118
+ self._data = bytes(data)
119
+
120
+ class SealedBoxStub:
121
+ def __init__(self, key):
122
+ pass
123
+
124
+ nacl_public.PublicKey = PublicKeyStub
125
+ nacl_public.SealedBox = SealedBoxStub
126
+
127
+ monkeypatch.setitem(sys.modules, 'nacl', nacl_mod)
128
+ monkeypatch.setitem(sys.modules, 'nacl.public', nacl_public)
129
+
130
+ mod = _load_utils_module(monkeypatch)
131
+
132
+ with pytest.raises(Exception) as excinfo:
133
+ mod.get_sealedbox_private_key()
134
+
135
+ assert 'Encryption support is unavailable' in str(excinfo.value)
136
+
137
+
138
+ def test_get_sealedbox_private_key_initializes_and_caches_private_key(monkeypatch):
139
+ # Provide a PrivateKey implementation and stub get_secret_key to return
140
+ # a valid 32-byte base64-encoded secret. Verify the value is cached and
141
+ # the secret fetch is only performed once.
142
+ key_bytes = b"\x02" * 32
143
+
144
+ nacl_mod = types.ModuleType('nacl')
145
+ nacl_public = types.ModuleType('nacl.public')
146
+
147
+ class PrivateKeyStub:
148
+ def __init__(self, data):
149
+ # Accept bytes-like input
150
+ self._data = bytes(data)
151
+
152
+ def __repr__(self):
153
+ return f"PrivateKeyStub(len={len(self._data)})"
154
+
155
+ nacl_public.PrivateKey = PrivateKeyStub
156
+ monkeypatch.setitem(sys.modules, 'nacl', nacl_mod)
157
+ monkeypatch.setitem(sys.modules, 'nacl.public', nacl_public)
158
+
159
+ mod = _load_utils_module(monkeypatch)
160
+
161
+ calls = {'count': 0}
162
+
163
+ def fake_get_secret_key(name):
164
+ calls['count'] += 1
165
+ return base64.b64encode(key_bytes).decode('ascii')
166
+
167
+ # Replace the module-level get_secret_key with our fake
168
+ mod.get_secret_key = fake_get_secret_key
169
+
170
+ # Ensure fresh start
171
+ mod._SEALEDBOX_PRIVATE_KEY = None
172
+
173
+ first = mod.get_sealedbox_private_key()
174
+ second = mod.get_sealedbox_private_key()
175
+
176
+ assert first is second
177
+ assert calls['count'] == 1
178
+ assert hasattr(first, '_data') and first._data == key_bytes
179
+
180
+
181
+ def test_get_sealedbox_private_key_invalid_length_does_not_cache_and_raises(monkeypatch):
182
+ # Provide PrivateKey but have the secret manager return an invalid-length key
183
+ invalid_key = b"\x03" * 16
184
+
185
+ nacl_mod = types.ModuleType('nacl')
186
+ nacl_public = types.ModuleType('nacl.public')
187
+
188
+ class PrivateKeyStub:
189
+ def __init__(self, data):
190
+ self._data = bytes(data)
191
+
192
+ nacl_public.PrivateKey = PrivateKeyStub
193
+ monkeypatch.setitem(sys.modules, 'nacl', nacl_mod)
194
+ monkeypatch.setitem(sys.modules, 'nacl.public', nacl_public)
195
+
196
+ mod = _load_utils_module(monkeypatch)
197
+
198
+ def fake_get_secret_key(name):
199
+ return base64.b64encode(invalid_key).decode('ascii')
200
+
201
+ mod.get_secret_key = fake_get_secret_key
202
+
203
+ # Ensure fresh start
204
+ mod._SEALEDBOX_PRIVATE_KEY = None
205
+
206
+ with pytest.raises(Exception) as excinfo:
207
+ mod.get_sealedbox_private_key()
208
+
209
+ assert 'Failed to initialize sealed-box private key' in str(excinfo.value)
210
+ # Failure should not be cached
211
+ assert mod._SEALEDBOX_PRIVATE_KEY is None
212
+
213
+
214
+ # ------------------------- Additional tests to improve coverage -------------------------
215
+
216
+ def test_get_secret_key_returns_secret_on_success(monkeypatch):
217
+ mod = _load_utils_module(monkeypatch)
218
+
219
+ fake_payload = types.SimpleNamespace(data=b'secret-value')
220
+ fake_response = types.SimpleNamespace(payload=fake_payload)
221
+
222
+ class FakeClient:
223
+ def access_secret_version(self, request):
224
+ return fake_response
225
+
226
+ # Patch the SecretManager client on the imported module
227
+ monkeypatch.setattr(mod.secretmanager, 'SecretManagerServiceClient', FakeClient, raising=True)
228
+
229
+ result = mod.get_secret_key('my-secret')
230
+ assert result == 'secret-value'
231
+
232
+
233
+ def test_get_secret_key_raises_on_backend_error(monkeypatch):
234
+ mod = _load_utils_module(monkeypatch)
235
+
236
+ class FakeClient:
237
+ def access_secret_version(self, request):
238
+ raise RuntimeError('boom')
239
+
240
+ monkeypatch.setattr(mod.secretmanager, 'SecretManagerServiceClient', FakeClient, raising=True)
241
+
242
+ with pytest.raises(Exception) as excinfo:
243
+ mod.get_secret_key('does-not-matter')
244
+ assert 'Failed to retrieve secret key from Secret Manager' in str(excinfo.value)
245
+
246
+
247
+ def test_verify_firebase_token_success(monkeypatch):
248
+ mod = _load_utils_module(monkeypatch)
249
+ req = types.SimpleNamespace(headers={'Authorization': 'Bearer SOME_TOKEN'})
250
+
251
+ res = mod.verify_firebase_token(req)
252
+ assert res == {'uid': 'test'}
253
+
254
+
255
+ def test_verify_firebase_token_missing_or_invalid_header_raises(monkeypatch):
256
+ mod = _load_utils_module(monkeypatch)
257
+ req = types.SimpleNamespace(headers={})
258
+
259
+ with pytest.raises(Exception) as excinfo:
260
+ mod.verify_firebase_token(req)
261
+ # The implementation wraps the underlying message, but the cause should be present
262
+ assert 'Missing or invalid Authorization' in str(excinfo.value)
263
+
264
+
265
+ def test_verify_firebase_token_verify_id_token_failure_raises(monkeypatch):
266
+ mod = _load_utils_module(monkeypatch)
267
+
268
+ def fake_verify(token):
269
+ raise ValueError('invalid token')
270
+
271
+ # Replace the verifier with one that raises
272
+ mod.auth.verify_id_token = fake_verify
273
+
274
+ req = types.SimpleNamespace(headers={'Authorization': 'Bearer x'})
275
+ with pytest.raises(Exception) as excinfo:
276
+ mod.verify_firebase_token(req)
277
+ assert 'invalid token' in str(excinfo.value)
278
+
279
+
280
+ def test_get_api_key_delegates_to_get_secret_key(monkeypatch):
281
+ mod = _load_utils_module(monkeypatch)
282
+
283
+ called = {}
284
+
285
+ def fake_get_secret_key(name):
286
+ called['name'] = name
287
+ return 'X'
288
+
289
+ mod.get_secret_key = fake_get_secret_key
290
+
291
+ result = mod.get_api_key(mod.LlmFamily.OPENAI)
292
+ assert result == 'X'
293
+ assert called['name'] == 'openai-api-key'
294
+
295
+
296
+ def test_get_request_json_plaintext_returns_parsed_json(monkeypatch):
297
+ mod = _load_utils_module(monkeypatch)
298
+
299
+ def get_json(silent=False):
300
+ return {'a': 1}
301
+
302
+ req = types.SimpleNamespace(headers={}, get_json=get_json)
303
+ result = mod.get_request_json(req, strict=False)
304
+ assert result == {'a': 1}
305
+
306
+
307
+ def test_get_request_json_plaintext_strict_raises_on_get_json_error(monkeypatch):
308
+ mod = _load_utils_module(monkeypatch)
309
+
310
+ def get_json(silent=False):
311
+ raise ValueError('bad json')
312
+
313
+ req = types.SimpleNamespace(headers={}, get_json=get_json)
314
+ with pytest.raises(Exception):
315
+ mod.get_request_json(req, strict=True)
316
+
317
+
318
+ def test_get_request_json_plaintext_non_strict_returns_none_on_get_json_error(monkeypatch):
319
+ mod = _load_utils_module(monkeypatch)
320
+
321
+ def get_json(silent=False):
322
+ raise ValueError('bad json')
323
+
324
+ req = types.SimpleNamespace(headers={}, get_json=get_json)
325
+ result = mod.get_request_json(req, strict=False)
326
+ assert result is None
327
+
328
+
329
+ def _install_sealedbox_stub(monkeypatch, decrypt_behavior):
330
+ """Helper: install a nacl.public.SealedBox stub whose decrypt returns or throws.
331
+
332
+ decrypt_behavior may be either:
333
+ - a bytes object to return from decrypt(), or
334
+ - a callable that accepts ciphertext and returns bytes / raises.
335
+ """
336
+ nacl_mod = types.ModuleType('nacl')
337
+ nacl_public = types.ModuleType('nacl.public')
338
+
339
+ class SealedBox:
340
+ def __init__(self, key):
341
+ self._key = key
342
+
343
+ def decrypt(self, ciphertext):
344
+ if callable(decrypt_behavior):
345
+ return decrypt_behavior(ciphertext)
346
+ return decrypt_behavior
347
+
348
+ nacl_public.SealedBox = SealedBox
349
+ nacl_mod.public = nacl_public
350
+
351
+ # Ensure both module paths exist in sys.modules so `from nacl.public import SealedBox` works
352
+ monkeypatch.setitem(sys.modules, 'nacl', nacl_mod)
353
+ monkeypatch.setitem(sys.modules, 'nacl.public', nacl_public)
354
+
355
+
356
+ def test_get_request_json_encrypted_success_using_get_data(monkeypatch):
357
+ mod = _load_utils_module(monkeypatch)
358
+
359
+ plaintext = b'{"ok": true}'
360
+ ciphertext = b'FAKECIPHERTEXT'
361
+ body_b64 = base64.b64encode(ciphertext)
362
+
363
+ # Request exposes get_data(as_text=False)
364
+ def get_data(as_text=False):
365
+ return body_b64
366
+
367
+ req = types.SimpleNamespace(headers={mod.ENCRYPTION_HEADER_NAME: 'true'}, get_data=get_data)
368
+
369
+ # SealedBox.decrypt will return the plaintext regardless of ciphertext
370
+ _install_sealedbox_stub(monkeypatch, plaintext)
371
+ # Stub the private key loader so it doesn't call Secret Manager
372
+ monkeypatch.setattr(mod, 'get_sealedbox_private_key', lambda: 'DUMMY', raising=False)
373
+
374
+ res = mod.get_request_json(req, strict=True)
375
+ assert res == {"ok": True}
376
+
377
+
378
+ def test_get_request_json_encrypted_fallback_when_get_data_rejects_as_text_arg(monkeypatch):
379
+ mod = _load_utils_module(monkeypatch)
380
+
381
+ plaintext = b'{"ok": 1}'
382
+ ciphertext = b'CT'
383
+ body_b64 = base64.b64encode(ciphertext)
384
+
385
+ def get_data(*args, **kwargs):
386
+ # Simulate a stub that raises when given the as_text kwarg
387
+ if 'as_text' in kwargs:
388
+ raise TypeError('no as_text')
389
+ return body_b64
390
+
391
+ req = types.SimpleNamespace(headers={mod.ENCRYPTION_HEADER_NAME: 'true'}, get_data=get_data)
392
+
393
+ _install_sealedbox_stub(monkeypatch, plaintext)
394
+ monkeypatch.setattr(mod, 'get_sealedbox_private_key', lambda: 'DUMMY', raising=False)
395
+
396
+ res = mod.get_request_json(req, strict=True)
397
+ assert res == {"ok": 1}
398
+
399
+
400
+ def test_get_request_json_encrypted_uses_data_attribute_if_no_get_data(monkeypatch):
401
+ mod = _load_utils_module(monkeypatch)
402
+
403
+ plaintext = b'{"v": 42}'
404
+ ciphertext = b'CT2'
405
+ body_b64 = base64.b64encode(ciphertext)
406
+
407
+ req = types.SimpleNamespace(headers={mod.ENCRYPTION_HEADER_NAME: 'true'}, data=body_b64)
408
+
409
+ _install_sealedbox_stub(monkeypatch, plaintext)
410
+ monkeypatch.setattr(mod, 'get_sealedbox_private_key', lambda: 'DUMMY', raising=False)
411
+
412
+ res = mod.get_request_json(req, strict=True)
413
+ assert res == {"v": 42}
414
+
415
+
416
+ def test_get_request_json_encrypted_invalid_base64(monkeypatch):
417
+ mod = _load_utils_module(monkeypatch)
418
+
419
+ # Make b64 decoder throw to exercise that error path deterministically
420
+ def fake_b64decode(_):
421
+ raise Exception('bad base64')
422
+
423
+ monkeypatch.setattr(mod.base64, 'b64decode', fake_b64decode, raising=True)
424
+
425
+ def get_data(as_text=False):
426
+ return b'NOT-BASE64'
427
+
428
+ req = types.SimpleNamespace(headers={mod.ENCRYPTION_HEADER_NAME: 'true'}, get_data=get_data)
429
+
430
+ # Non-strict should return None
431
+ res = mod.get_request_json(req, strict=False)
432
+ assert res is None
433
+
434
+ # Strict should raise
435
+ with pytest.raises(Exception):
436
+ mod.get_request_json(req, strict=True)
437
+
438
+
439
+ def test_get_request_json_encrypted_decrypt_failure(monkeypatch):
440
+ mod = _load_utils_module(monkeypatch)
441
+
442
+ ciphertext = b'CT'
443
+ body_b64 = base64.b64encode(ciphertext)
444
+
445
+ # SealedBox.decrypt will raise to simulate decryption failure
446
+ def raise_on_decrypt(_):
447
+ raise RuntimeError('bad decrypt')
448
+
449
+ _install_sealedbox_stub(monkeypatch, raise_on_decrypt)
450
+ monkeypatch.setattr(mod, 'get_sealedbox_private_key', lambda: 'DUMMY', raising=False)
451
+
452
+ def get_data(as_text=False):
453
+ return body_b64
454
+
455
+ req = types.SimpleNamespace(headers={mod.ENCRYPTION_HEADER_NAME: 'true'}, get_data=get_data)
456
+
457
+ # Non-strict returns None
458
+ assert mod.get_request_json(req, strict=False) is None
459
+
460
+ # Strict raises
461
+ with pytest.raises(Exception):
462
+ mod.get_request_json(req, strict=True)
463
+
464
+
465
+ def test_get_request_json_encrypted_invalid_json_after_decrypt(monkeypatch):
466
+ mod = _load_utils_module(monkeypatch)
467
+
468
+ # Decrypt returns bytes that are not valid JSON
469
+ _install_sealedbox_stub(monkeypatch, b'not-json')
470
+ monkeypatch.setattr(mod, 'get_sealedbox_private_key', lambda: 'DUMMY', raising=False)
471
+
472
+ body_b64 = base64.b64encode(b'CT')
473
+
474
+ def get_data(as_text=False):
475
+ return body_b64
476
+
477
+ req = types.SimpleNamespace(headers={mod.ENCRYPTION_HEADER_NAME: 'true'}, get_data=get_data)
478
+
479
+ assert mod.get_request_json(req, strict=False) is None
480
+ with pytest.raises(Exception):
481
+ mod.get_request_json(req, strict=True)
482
+
483
+
484
+ # End of additional tests
Binary file
Binary file
@@ -1,78 +0,0 @@
1
- roksta/__init__.cp311-win_amd64.pyd,sha256=kVtuv-PimTcT9vr5kce0a4sQHSyWzVVIk42ONQ9N5v4,15872
2
- roksta/analytics.cp311-win_amd64.pyd,sha256=2XHICx3rn9iES7mTqAJyXZhzi7pLU8f-L9l8CL5CTXY,94208
3
- roksta/balance.cp311-win_amd64.pyd,sha256=Z2UcWIfBHDFtxjkYpDjFGJv7GvlgH_mktRytzHcSzBU,117760
4
- roksta/build_project.cp311-win_amd64.pyd,sha256=z7NYvpSpH2QF4wrdLNfKAzjEcY7Kqbhn70-4FcwZIBo,59904
5
- roksta/chat_workflow.cp311-win_amd64.pyd,sha256=X19X2pWTyiCFfcL6NpPf4lpVlrqWxAceLMf8xbuIXrc,89088
6
- roksta/check_for_updates.cp311-win_amd64.pyd,sha256=2axLsEHwPJYjJQgpuMrRfxLUsIYlfuOtxKFxoBq2jIQ,98304
7
- roksta/checkpoints.cp311-win_amd64.pyd,sha256=6koOtWXwyrb5G3oJEgIBNWOoLfeRgi0UjgmtRChcDwU,81408
8
- roksta/clarify_goal.cp311-win_amd64.pyd,sha256=cUqeOGWnxrspubfzdKmoMOrFBVqxlJq-NO23w__CxLI,64000
9
- roksta/codebase_listing.cp311-win_amd64.pyd,sha256=ynCpvL-zd4O2fw3qyKAB8iu9uKlMWk30dz9YYz06jGI,46592
10
- roksta/command_handlers.cp311-win_amd64.pyd,sha256=kKPBP_5eDEPA2jBpItGUs1qFGaS8VfOtO-_wEpxIAKM,371712
11
- roksta/create_default_config.cp311-win_amd64.pyd,sha256=v-SMG_c2f_g12fQ9ZZgVn8a__W5LC3v4vc2MRdMsGGg,31744
12
- roksta/default_config.cp311-win_amd64.pyd,sha256=Bg0FTTNQt6sAngtqstC3U_rLDZ7W2s68bQZcUA7nFO4,30720
13
- roksta/enums.cp311-win_amd64.pyd,sha256=dCSJFLTD84hz3IJe7YQSW-yrg1K1V5kHdbs9WeoVO8o,135168
14
- roksta/env.cp311-win_amd64.pyd,sha256=HPJCHScUJCyQOrH-QyuRVziU6NG9wT4ShgUmqu3v4d8,27136
15
- roksta/extended_text_area.cp311-win_amd64.pyd,sha256=lAmxRNGVF9h3y-mG1F4a9QjadUmjbipvfMb7McpIL54,107520
16
- roksta/firebase.cp311-win_amd64.pyd,sha256=078BGjj0iHa95XELZWytI4BOLIl9m6ittzltlwW_5VQ,167936
17
- roksta/firebase_auth_web.cp311-win_amd64.pyd,sha256=CIhBCcxx36uyxlNdrPEcHRQWsZ3yhJ23XDvq-_82LTc,170496
18
- roksta/firebase_config.cp311-win_amd64.pyd,sha256=hHOvz3BmakHGBliexJNzBw-byuwM38IUhO4u3lEIkOY,21504
19
- roksta/fix_tests.cp311-win_amd64.pyd,sha256=CAC80fQe_6_YuKfzzhKvotq2q9P9EFFzxJpsRz2FD2c,73728
20
- roksta/gen_codebase_summaries.cp311-win_amd64.pyd,sha256=XzQrevEOTTFk_HJAwqMiZjSKDvRnIU89-1d2wGUhmP8,230400
21
- roksta/gen_one_line_goal.cp311-win_amd64.pyd,sha256=9CocGZ0tTtWb6l55qxzhMHQ5gDjr7eNA6EbyChFrNVs,63488
22
- roksta/get_codebase_structure.cp311-win_amd64.pyd,sha256=SLvOIr4SOr2-KZYIC5xg9Fsrt6rRo8sstVMw6T4qXFI,66048
23
- roksta/get_failing_tests.cp311-win_amd64.pyd,sha256=aGW-VctMCU1bX1k5kYdYaIfzgudROegtt0Dd7zouKxI,69120
24
- roksta/goal_workflow.cp311-win_amd64.pyd,sha256=dhj1Ls513lO0m9NrITl_fxLw18gDHrfjEOE38_wE0o8,189952
25
- roksta/init_codebase.cp311-win_amd64.pyd,sha256=Oi5zfTn5Acde8_QIfTbbJSq9Z6VBV0WOP5VDvNzcXh4,95744
26
- roksta/lint_code.cp311-win_amd64.pyd,sha256=ry6JtopCzFif6ocjAlJs-7wtILoHPuHyMDBfmzKRLh8,69632
27
- roksta/logger.cp311-win_amd64.pyd,sha256=gpKI2DjQpSnFg1EkeZ36fBLS2BtU4WmosYQQ0YrR1RA,38400
28
- roksta/main.cp311-win_amd64.pyd,sha256=d_q43_dTcAzGLXlU8Bj-MLMlLnLBe1Dc-bBqfG66dQU,38912
29
- roksta/make_issue.cp311-win_amd64.pyd,sha256=5jUZHdcw0NGTPCL0O58VkKHO0qBpvivKXkT9k-xUwto,69632
30
- roksta/new_features.cp311-win_amd64.pyd,sha256=nWGmfU_RbX6bkb2rURiLP0ytLhIlsC0bv4Kh8uYtTpc,16384
31
- roksta/parse_readme.cp311-win_amd64.pyd,sha256=qOO2SK9Zg_l88Gy5jU4WBY-ndUp_BobIhybRKZo9O1E,61952
32
- roksta/propose_solution.cp311-win_amd64.pyd,sha256=hMni5zUGAoWgbW5AEsnXPcZ7ZY4_f4wFXewisXLU98k,61440
33
- roksta/response_formats.cp311-win_amd64.pyd,sha256=hQivV6gt6s6X2lo-uzyjojEzBzD0Z3IAzeQfuP1LZYQ,41984
34
- roksta/rewrite_goal.cp311-win_amd64.pyd,sha256=EVGA8xj3NhBE6QaPAwZ77keaiblDxhtzJGj1Jy53L2M,55808
35
- roksta/roksta.cp311-win_amd64.pyd,sha256=hRwYVSkwrKElbkFG7yy-DbrbH_xwJQ1hOaQlWQy7DOI,229376
36
- roksta/run_cli_goal.cp311-win_amd64.pyd,sha256=8HFka6AWgw-7s16UKKDNrOmIyv71QesEzY5cZcb5ui0,69120
37
- roksta/select_files.cp311-win_amd64.pyd,sha256=C6iY011nQT9ix_itXnBFqB2fdvkKEjXWr97mTfI5qfM,66048
38
- roksta/tips.cp311-win_amd64.pyd,sha256=r9aphvYXXqwyfAFlbAzRvamjOOW5jEfrFcx2gGCjP1U,20992
39
- roksta/utils.cp311-win_amd64.pyd,sha256=xQHjBcggSNyuxboxhcadqUH3hNGt52zFWGRDX6kLExM,221184
40
- roksta/write_code.cp311-win_amd64.pyd,sha256=8WWdjkVhGyIL4jyKMvsNvvR8z7-ZJPr5EhiCCisdMDQ,58880
41
- roksta/ai/__init__.cp311-win_amd64.pyd,sha256=2Bfwj4T5oOMgbXo-SE4fkp2t8qXvrUtcjihnZgIImQE,15872
42
- roksta/ai/call_ai.cp311-win_amd64.pyd,sha256=yl4gtbx9Qjm8KusVdO1786UKK1xcRlyyHf3KEz75Ha0,72192
43
- roksta/ai/gemini.cp311-win_amd64.pyd,sha256=gB25h3r7Akjg5kahw5MHLy0JQGdz07JTEVnSr_8AEJw,143872
44
- roksta/ai/generic.cp311-win_amd64.pyd,sha256=DvL70EWE2alGSCxSzlnuEjFqsi8tVv_ZsLH0k4R4WZg,105984
45
- roksta/ai/llm.cp311-win_amd64.pyd,sha256=-LFfN8JbjDz5HtSF0z_eLi67k34rBft0Ljt9YEFBmjI,92160
46
- roksta/ai/openai.cp311-win_amd64.pyd,sha256=MpnTcmY51RZpNaEwtUuroJIreZprW9g_wlYpxl0ZrBk,103424
47
- roksta/ai/tools.cp311-win_amd64.pyd,sha256=vRxuWnZkiZMEYYzcSUpQOdcdwqi-tX4dNAQaBZX5x2E,231936
48
- tests/__init__.py,sha256=meQjVtD2zimtDw3tIP6nNWHGDQVC42as-c_y038cYnk,126
49
- tests/conftest.py,sha256=auOTmcnUGIgvlQsVEOhchjLgUtQmnzMtCc5N3Sr79Cw,6464
50
- tests/functions/__init__.py,sha256=msAiuVOA8jfqVxichPYYyfBFCVEFB_xRvj4lrSpKK1w,97
51
- tests/functions/test_auth.py,sha256=SNzmpkoxknrxx6cTE91MM2kN3F08GB1pRuupP5IrQs0,1016
52
- tests/functions/test_main_functions.py,sha256=LgWLAOtSfDXNfHhAAr-j9Peb5JBLFtQ0eKAWqTxLNDU,2648
53
- tests/functions/test_utils_functions.py,sha256=2x4W2JGnnKabTSTNKQ_DdcZl7K9HVF0N4kudTL05b3Y,7590
54
- tests/functions/api_v0_01/__init__.py,sha256=6EY0sGI3Jkmk4BGjNzSgyLGxFIGy06vwD76KDKAHh4g,126
55
- tests/functions/api_v0_01/test__analytics.py,sha256=d0zfl_ydtFc8cxlaL5GV76Rz2P7XsHoqEp2rYdN1zk0,16224
56
- tests/functions/api_v0_01/test__gemini_proxy.py,sha256=BuT2IWS0NHCa1FzEN8YPf6t00TOjWbpF-SYSOZMWPCg,11459
57
- tests/functions/api_v0_01/test__generic_proxy.py,sha256=2CSQ8OYUM0Ertq9PYueWDBWgPmz5bpRO8YICAwA_ZUc,16589
58
- tests/functions/api_v0_01/test__get_payment_details.py,sha256=wvhH5xliVIkU6GvvWFKF-lAV8mSZfj0HCT284BjDYCw,14091
59
- tests/functions/api_v0_01/test__openai_proxy.py,sha256=tXAz-Ukl3FD1Zdv083xczoQzG-dOS53uL2gBjB-Wcx8,16174
60
- tests/functions/api_v0_01/test__redeem_credit_code.py,sha256=xDzTWQxcIztksCWpTNibNgZ2ZPT2gqgdtljZiE8wMlU,6514
61
- tests/functions/api_v0_01/test__sync_emails.py,sha256=5nH4wxcqHqowc4C-pNShF4eWS52Pw6hZxsaOTn7f66I,13022
62
- tests/functions/api_v0_01/test__take_payment.py,sha256=NFoIw4RVMT63c7diZ7gt6N03dp26vVlognZBchpWRLY,18513
63
- tests/functions/api_v0_01/test__use_activation_code.py,sha256=3XS-N-fL7UIOev2kzphlDy-2ukykQLzb_0kmlkCr1gQ,17396
64
- tests/functions/api_v1_00/__init__.py,sha256=VdV5Oy6FRVT4S1xK34YodylJ7FY6jqvjujkS2Qvbyt0,126
65
- tests/functions/api_v1_00/test__analytics.py,sha256=s_pK4ySr86PC4ZBr7Y7bCHOEuFj36amaWrianBZ_QbU,16222
66
- tests/functions/api_v1_00/test__gemini_proxy.py,sha256=ZNYBQ9agPyNYGAVYlWPY6IF8wP_5IQ4nbHOpY8TnKEw,13696
67
- tests/functions/api_v1_00/test__generic_proxy.py,sha256=f2KjO8v_mh4nBwdkFXJLYfFUBpJS_RDtQNfn0ltE2oM,18101
68
- tests/functions/api_v1_00/test__get_payment_details.py,sha256=GRMAn8WR-07EBZ0IkeVE4MARFUbDdDhEHO2dITRxnVA,14091
69
- tests/functions/api_v1_00/test__openai_proxy.py,sha256=i1f7S8IFgWiG6WGG-YYptjTCOjSudujEMyhP16BVlW0,18175
70
- tests/functions/api_v1_00/test__redeem_credit_code.py,sha256=A5WwuSXBX5TiZPdYrDRkUmpppBXG00lcP0LmRz0gNr0,6514
71
- tests/functions/api_v1_00/test__sync_emails.py,sha256=e0rb637Ois25t__1G8Yp7Pj8WgC-R1I9SUgS6bK1dsU,13024
72
- tests/functions/api_v1_00/test__take_payment.py,sha256=RQBUHcgN3-XOoPtAjca_72q4BaZxr_L3G_Nss5-gb2g,18513
73
- tests/functions/api_v1_00/test__use_activation_code.py,sha256=ZOCwXHzenH0l1WhmZP8s8vaXIk58lHpKJ0igx0VEDwY,17398
74
- roksta-0.2.7.dist-info/METADATA,sha256=tqdrCdR2ZSh1KSc_8dkuQ8tXOVkASKraZpUqAQgptjU,1466
75
- roksta-0.2.7.dist-info/WHEEL,sha256=JLOMsP7F5qtkAkINx5UnzbFguf8CqZeraV8o04b0I8I,101
76
- roksta-0.2.7.dist-info/entry_points.txt,sha256=mzRdYg_DlzZRwjxYUt9-gyoRCkM1QBTeTbwETgiTdGw,44
77
- roksta-0.2.7.dist-info/top_level.txt,sha256=lvciNZQ1dPGXpiCLdWVXK03n9fKHjbQdwjqQbnUjeYM,13
78
- roksta-0.2.7.dist-info/RECORD,,