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,438 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import json
|
|
4
|
-
import types
|
|
5
|
-
import importlib
|
|
6
|
-
from unittest.mock import patch
|
|
7
|
-
from datetime import datetime, timezone, timedelta
|
|
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
|
-
# -----------------------------
|
|
16
|
-
# Provide lightweight fake modules to satisfy imports inside _use_activation_code
|
|
17
|
-
# These are minimal and will be patched in individual tests as needed.
|
|
18
|
-
# -----------------------------
|
|
19
|
-
# Save original sys.modules entries so we can restore them after importing the module under test
|
|
20
|
-
_orig_sys_modules = {}
|
|
21
|
-
_names_to_fake = [
|
|
22
|
-
'firebase_functions',
|
|
23
|
-
'utils',
|
|
24
|
-
'auth',
|
|
25
|
-
'firebase_admin',
|
|
26
|
-
'firebase_admin.firestore',
|
|
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 (create_json_response, verify_firebase_token)
|
|
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=response_body, status=status_code, headers={'Content-Type': 'application/json'})
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def _fake_verify_firebase_token(req):
|
|
62
|
-
# Default: successful verification returning a uid
|
|
63
|
-
return {'uid': 'user_1'}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
utils_mod.create_json_response = _fake_create_json_response
|
|
67
|
-
utils_mod.verify_firebase_token = _fake_verify_firebase_token
|
|
68
|
-
sys.modules['utils'] = utils_mod
|
|
69
|
-
|
|
70
|
-
# Fake top-level auth module (validate_auth_key will be patched per-test as needed)
|
|
71
|
-
auth_mod = types.ModuleType('auth')
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def _simple_validate_auth_key(val: str) -> bool:
|
|
75
|
-
return bool(val)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
auth_mod.validate_auth_key = _simple_validate_auth_key
|
|
79
|
-
sys.modules['auth'] = auth_mod
|
|
80
|
-
|
|
81
|
-
# Fake firebase_admin.firestore using an in-memory dict
|
|
82
|
-
firebase_admin_mod = types.ModuleType('firebase_admin')
|
|
83
|
-
|
|
84
|
-
# In-memory fake DB used by the fake Firestore implementation
|
|
85
|
-
fake_db_data = {}
|
|
86
|
-
|
|
87
|
-
class DocumentSnapshot:
|
|
88
|
-
def __init__(self, exists, data):
|
|
89
|
-
self.exists = exists
|
|
90
|
-
self._data = data
|
|
91
|
-
|
|
92
|
-
def to_dict(self):
|
|
93
|
-
return self._data
|
|
94
|
-
|
|
95
|
-
def get(self, key):
|
|
96
|
-
if not self._data:
|
|
97
|
-
return None
|
|
98
|
-
return self._data.get(key)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class DocumentRef:
|
|
102
|
-
def __init__(self, collection_name, doc_id):
|
|
103
|
-
self.collection = collection_name
|
|
104
|
-
self.doc_id = doc_id
|
|
105
|
-
|
|
106
|
-
def get(self, transaction=None):
|
|
107
|
-
coll = fake_db_data.get(self.collection, {})
|
|
108
|
-
if self.doc_id in coll:
|
|
109
|
-
return DocumentSnapshot(True, coll[self.doc_id])
|
|
110
|
-
return DocumentSnapshot(False, None)
|
|
111
|
-
|
|
112
|
-
def update(self, update_dict):
|
|
113
|
-
coll = fake_db_data.setdefault(self.collection, {})
|
|
114
|
-
doc = coll.setdefault(self.doc_id, {})
|
|
115
|
-
if isinstance(doc, dict):
|
|
116
|
-
doc.update(update_dict)
|
|
117
|
-
else:
|
|
118
|
-
coll[self.doc_id] = update_dict
|
|
119
|
-
|
|
120
|
-
def set(self, data):
|
|
121
|
-
coll = fake_db_data.setdefault(self.collection, {})
|
|
122
|
-
coll[self.doc_id] = data
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
class CollectionRef:
|
|
126
|
-
def __init__(self, name):
|
|
127
|
-
self.name = name
|
|
128
|
-
|
|
129
|
-
def document(self, doc_id):
|
|
130
|
-
return DocumentRef(self.name, doc_id)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
class FakeTransaction:
|
|
134
|
-
def __init__(self):
|
|
135
|
-
pass
|
|
136
|
-
|
|
137
|
-
def update(self, doc_ref, update_dict):
|
|
138
|
-
coll = fake_db_data.setdefault(doc_ref.collection, {})
|
|
139
|
-
doc = coll.setdefault(doc_ref.doc_id, {})
|
|
140
|
-
if isinstance(doc, dict):
|
|
141
|
-
doc.update(update_dict)
|
|
142
|
-
else:
|
|
143
|
-
coll[doc_ref.doc_id] = update_dict
|
|
144
|
-
|
|
145
|
-
def set(self, doc_ref, data):
|
|
146
|
-
coll = fake_db_data.setdefault(doc_ref.collection, {})
|
|
147
|
-
coll[doc_ref.doc_id] = data
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
class FakeFirestoreClient:
|
|
151
|
-
def __init__(self):
|
|
152
|
-
pass
|
|
153
|
-
|
|
154
|
-
def collection(self, name):
|
|
155
|
-
return CollectionRef(name)
|
|
156
|
-
|
|
157
|
-
def transaction(self):
|
|
158
|
-
return FakeTransaction()
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
fake_firestore_mod = types.ModuleType('firebase_admin.firestore')
|
|
162
|
-
fake_firestore_mod.client = lambda: FakeFirestoreClient()
|
|
163
|
-
fake_firestore_mod.transactional = lambda f: f
|
|
164
|
-
fake_firestore_mod.SERVER_TIMESTAMP = object()
|
|
165
|
-
sys.modules['firebase_admin.firestore'] = fake_firestore_mod
|
|
166
|
-
firebase_admin_mod.firestore = fake_firestore_mod
|
|
167
|
-
sys.modules['firebase_admin'] = firebase_admin_mod
|
|
168
|
-
|
|
169
|
-
# -----------------------------
|
|
170
|
-
# Import the module under test after preparing the fake imports
|
|
171
|
-
# -----------------------------
|
|
172
|
-
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
|
173
|
-
functions_root = os.path.join(repo_root, 'functions')
|
|
174
|
-
module_path = os.path.join(functions_root, 'api_v1_00', '_use_activation_code.py')
|
|
175
|
-
spec = importlib.util.spec_from_file_location('api_v1_00._use_activation_code', module_path)
|
|
176
|
-
_use_activation = importlib.util.module_from_spec(spec)
|
|
177
|
-
spec.loader.exec_module(_use_activation)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
# Restore original sys.modules mappings to avoid side-effects for other tests
|
|
181
|
-
for name, orig in _orig_sys_modules.items():
|
|
182
|
-
if orig is None:
|
|
183
|
-
try:
|
|
184
|
-
del sys.modules[name]
|
|
185
|
-
except KeyError:
|
|
186
|
-
pass
|
|
187
|
-
else:
|
|
188
|
-
sys.modules[name] = orig
|
|
189
|
-
|
|
190
|
-
# Import env constants for auth header usage in tests
|
|
191
|
-
import env as functions_env # noqa: E402
|
|
192
|
-
AUTH_HEADER_NAME = functions_env.AUTH_HEADER_NAME
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
# Simple helper request stub used in the tests
|
|
196
|
-
class DummyRequest:
|
|
197
|
-
def __init__(self, headers=None, method='POST', json_data=None, raise_on_get_json=False):
|
|
198
|
-
self.headers = headers or {}
|
|
199
|
-
self.method = method
|
|
200
|
-
self._json_data = json_data
|
|
201
|
-
self._raise = raise_on_get_json
|
|
202
|
-
|
|
203
|
-
def get_json(self, silent=True):
|
|
204
|
-
if self._raise:
|
|
205
|
-
raise Exception('Malformed JSON')
|
|
206
|
-
return self._json_data
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def _parse_response(resp):
|
|
210
|
-
data = resp.get_data(as_text=True)
|
|
211
|
-
return json.loads(data)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
# -----------------------------
|
|
215
|
-
# Tests
|
|
216
|
-
# -----------------------------
|
|
217
|
-
|
|
218
|
-
def test_missing_auth_header_returns_401():
|
|
219
|
-
fake_db_data.clear()
|
|
220
|
-
req = DummyRequest(headers={}, method='POST')
|
|
221
|
-
# Ensure token verification isn't called when header missing
|
|
222
|
-
with patch.object(_use_activation, 'verify_firebase_token', side_effect=Exception('Should not be called')):
|
|
223
|
-
resp = _use_activation._use_activation_code(req)
|
|
224
|
-
|
|
225
|
-
assert resp.status_code == 401
|
|
226
|
-
payload = _parse_response(resp)
|
|
227
|
-
assert payload['success'] is False
|
|
228
|
-
assert 'Missing app authentication key' in payload['payload']
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
def test_invalid_auth_key_returns_403():
|
|
232
|
-
fake_db_data.clear()
|
|
233
|
-
headers = {AUTH_HEADER_NAME: 'bad-token'}
|
|
234
|
-
req = DummyRequest(headers=headers, method='POST')
|
|
235
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=False):
|
|
236
|
-
resp = _use_activation._use_activation_code(req)
|
|
237
|
-
|
|
238
|
-
assert resp.status_code == 403
|
|
239
|
-
payload = _parse_response(resp)
|
|
240
|
-
assert payload['success'] is False
|
|
241
|
-
assert 'Invalid app authentication key' in payload['payload']
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
def test_verify_firebase_token_failure_returns_403():
|
|
245
|
-
fake_db_data.clear()
|
|
246
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
247
|
-
req = DummyRequest(headers=headers, method='POST')
|
|
248
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', side_effect=Exception('boom')):
|
|
249
|
-
resp = _use_activation._use_activation_code(req)
|
|
250
|
-
|
|
251
|
-
assert resp.status_code == 403
|
|
252
|
-
payload = _parse_response(resp)
|
|
253
|
-
assert payload['success'] is False
|
|
254
|
-
assert 'Authentication failed' in payload['payload']
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
def test_missing_json_payload_returns_400():
|
|
258
|
-
fake_db_data.clear()
|
|
259
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
260
|
-
req = DummyRequest(headers=headers, method='POST', json_data=None)
|
|
261
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
262
|
-
resp = _use_activation._use_activation_code(req)
|
|
263
|
-
|
|
264
|
-
assert resp.status_code == 400
|
|
265
|
-
payload = _parse_response(resp)
|
|
266
|
-
assert payload['success'] is False
|
|
267
|
-
assert 'Invalid or missing JSON payload' in payload['payload']
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
def test_missing_activation_code_returns_400():
|
|
271
|
-
fake_db_data.clear()
|
|
272
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
273
|
-
# Provide a non-empty JSON payload without 'activation_code' to trigger the missing key branch
|
|
274
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'foo': 'bar'})
|
|
275
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
276
|
-
resp = _use_activation._use_activation_code(req)
|
|
277
|
-
|
|
278
|
-
assert resp.status_code == 400
|
|
279
|
-
payload = _parse_response(resp)
|
|
280
|
-
assert payload['success'] is False
|
|
281
|
-
assert "Missing 'activation_code'" in payload['payload']
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
def test_user_does_not_exist_returns_404():
|
|
285
|
-
fake_db_data.clear()
|
|
286
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
287
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'activation_code': 'code1'})
|
|
288
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
289
|
-
resp = _use_activation._use_activation_code(req)
|
|
290
|
-
|
|
291
|
-
assert resp.status_code == 404
|
|
292
|
-
payload = _parse_response(resp)
|
|
293
|
-
assert payload['success'] is False
|
|
294
|
-
assert 'User does not exist' in payload['payload']
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
def test_user_already_active_returns_200():
|
|
298
|
-
fake_db_data.clear()
|
|
299
|
-
fake_db_data.setdefault('users', {})['user_1'] = {'active': True}
|
|
300
|
-
|
|
301
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
302
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'activation_code': 'code1'})
|
|
303
|
-
|
|
304
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
305
|
-
resp = _use_activation._use_activation_code(req)
|
|
306
|
-
|
|
307
|
-
assert resp.status_code == 200
|
|
308
|
-
payload = _parse_response(resp)
|
|
309
|
-
assert payload['success'] is True
|
|
310
|
-
assert 'Account is already active' in payload['payload']
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
def test_activation_code_not_found_returns_404():
|
|
314
|
-
fake_db_data.clear()
|
|
315
|
-
# user exists and inactive
|
|
316
|
-
fake_db_data.setdefault('users', {})['user_1'] = {'active': False}
|
|
317
|
-
|
|
318
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
319
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'activation_code': 'CodeX'})
|
|
320
|
-
|
|
321
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
322
|
-
resp = _use_activation._use_activation_code(req)
|
|
323
|
-
|
|
324
|
-
assert resp.status_code == 404
|
|
325
|
-
payload = _parse_response(resp)
|
|
326
|
-
assert payload['success'] is False
|
|
327
|
-
assert 'Invalid activation code' in payload['payload']
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
def test_activation_code_missing_expiry_returns_500():
|
|
331
|
-
fake_db_data.clear()
|
|
332
|
-
# user exists and inactive
|
|
333
|
-
fake_db_data.setdefault('users', {})['user_1'] = {'active': False}
|
|
334
|
-
# activation code exists but missing expiry_date
|
|
335
|
-
fake_db_data.setdefault('activation_codes', {})['codea'] = {'use_count': 0, 'max_uses': 1}
|
|
336
|
-
|
|
337
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
338
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'activation_code': 'codeA'})
|
|
339
|
-
|
|
340
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
341
|
-
resp = _use_activation._use_activation_code(req)
|
|
342
|
-
|
|
343
|
-
assert resp.status_code == 500
|
|
344
|
-
payload = _parse_response(resp)
|
|
345
|
-
assert payload['success'] is False
|
|
346
|
-
assert 'Activation code expiry information is missing' in payload['payload']
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
def test_activation_code_expired_returns_400():
|
|
350
|
-
fake_db_data.clear()
|
|
351
|
-
fake_db_data.setdefault('users', {})['user_1'] = {'active': False}
|
|
352
|
-
past = datetime.now(timezone.utc) - timedelta(days=1)
|
|
353
|
-
fake_db_data.setdefault('activation_codes', {})['expiredcode'] = {'expiry_date': past, 'use_count': 0, 'max_uses': 5}
|
|
354
|
-
|
|
355
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
356
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'activation_code': 'expiredCode'})
|
|
357
|
-
|
|
358
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
359
|
-
resp = _use_activation._use_activation_code(req)
|
|
360
|
-
|
|
361
|
-
assert resp.status_code == 400
|
|
362
|
-
payload = _parse_response(resp)
|
|
363
|
-
assert payload['success'] is False
|
|
364
|
-
assert 'Activation code has expired' in payload['payload']
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
def test_activation_code_usage_limit_reached_returns_400():
|
|
368
|
-
fake_db_data.clear()
|
|
369
|
-
fake_db_data.setdefault('users', {})['user_1'] = {'active': False}
|
|
370
|
-
fake_db_data.setdefault('activation_codes', {})['limitcode'] = {'expiry_date': datetime.now(timezone.utc) + timedelta(days=1), 'use_count': 5, 'max_uses': 5}
|
|
371
|
-
|
|
372
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
373
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'activation_code': 'limitCode'})
|
|
374
|
-
|
|
375
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
376
|
-
resp = _use_activation._use_activation_code(req)
|
|
377
|
-
|
|
378
|
-
assert resp.status_code == 400
|
|
379
|
-
payload = _parse_response(resp)
|
|
380
|
-
assert payload['success'] is False
|
|
381
|
-
assert 'Usage limit has been hit' in payload['payload']
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
def test_user_already_used_code_returns_400():
|
|
385
|
-
fake_db_data.clear()
|
|
386
|
-
fake_db_data.setdefault('users', {})['user_1'] = {'active': False}
|
|
387
|
-
code = 'multi'
|
|
388
|
-
fake_db_data.setdefault('activation_codes', {})[code] = {'expiry_date': datetime.now(timezone.utc) + timedelta(days=1), 'use_count': 0, 'max_uses': 5}
|
|
389
|
-
# Create an activation record indicating user already used the code
|
|
390
|
-
fake_db_data.setdefault('activations', {})[f"{code}_user_1"] = {'activation_code': code, 'user_id': 'user_1', 'timestamp': fake_firestore_mod.SERVER_TIMESTAMP}
|
|
391
|
-
|
|
392
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
393
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'activation_code': 'MULTI'})
|
|
394
|
-
|
|
395
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
396
|
-
resp = _use_activation._use_activation_code(req)
|
|
397
|
-
|
|
398
|
-
assert resp.status_code == 400
|
|
399
|
-
payload = _parse_response(resp)
|
|
400
|
-
assert payload['success'] is False
|
|
401
|
-
assert 'User has already used this activation code' in payload['payload']
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
def test_successful_activation_returns_200_and_updates_db():
|
|
405
|
-
fake_db_data.clear()
|
|
406
|
-
fake_db_data.setdefault('users', {})['user_1'] = {'active': False}
|
|
407
|
-
code = 'successcode'
|
|
408
|
-
fake_db_data.setdefault('activation_codes', {})[code] = {'expiry_date': datetime.now(timezone.utc) + timedelta(days=1), 'use_count': 2, 'max_uses': 5}
|
|
409
|
-
|
|
410
|
-
headers = {AUTH_HEADER_NAME: 'ok'}
|
|
411
|
-
req = DummyRequest(headers=headers, method='POST', json_data={'activation_code': 'SuccessCode'})
|
|
412
|
-
|
|
413
|
-
with patch.object(_use_activation, 'validate_auth_key', return_value=True), patch.object(_use_activation, 'verify_firebase_token', return_value={'uid': 'user_1'}):
|
|
414
|
-
resp = _use_activation._use_activation_code(req)
|
|
415
|
-
|
|
416
|
-
assert resp.status_code == 200
|
|
417
|
-
payload = _parse_response(resp)
|
|
418
|
-
assert payload['success'] is True
|
|
419
|
-
assert 'Activation code used successfully' in payload['payload']
|
|
420
|
-
|
|
421
|
-
# Verify database updates
|
|
422
|
-
user_doc = fake_db_data.get('users', {}).get('user_1')
|
|
423
|
-
assert user_doc is not None
|
|
424
|
-
assert user_doc.get('active') is True
|
|
425
|
-
|
|
426
|
-
code_doc = fake_db_data.get('activation_codes', {}).get(code)
|
|
427
|
-
assert code_doc is not None
|
|
428
|
-
assert code_doc.get('use_count') == 3
|
|
429
|
-
|
|
430
|
-
activation_key = f"{code}_user_1"
|
|
431
|
-
activations = fake_db_data.get('activations', {})
|
|
432
|
-
assert activation_key in activations
|
|
433
|
-
act = activations[activation_key]
|
|
434
|
-
assert act['user_id'] == 'user_1'
|
|
435
|
-
assert act['activation_code'] == code
|
|
436
|
-
assert 'timestamp' in act
|
|
437
|
-
# timestamp should be the firestore.SERVER_TIMESTAMP sentinel
|
|
438
|
-
assert act['timestamp'] is fake_firestore_mod.SERVER_TIMESTAMP
|