cover the new paths, especially the ones that fail silently
خطاهای این پروژه خاموشاند: نامِ اشتباهِ collection یا فیلترِ نخورده نتیجهی خالی میدهد نه استثنا، و دستیار فقط بدتر جواب میدهد. این تستها نگهبانِ همانجاهایند. TextbookLayoutTests بررسی میکند رشتهی هر فصل بینِ ingest و retrieval یکی باشد و بازهی صفحهها پیوسته بماند. IngestBookResumeTests مطمئن میشود صفحهی بیمسئله دوبار پول نمیگیرد، صفحهی خطادار در اجرای بعد دوباره سراغش میروند، و upsertِ شکستخورده rollback میشود. LessonContextResolutionTests نگهبانِ نرمالسازیِ «پایه نهم» → «ریاضی نهم» است و اینکه مسیرِ gradeId و مسیرِ lessonId به یک collection برسند. GradeAwarePromptTests جلوی برگشتنِ لحنِ دبستانی برای پایههای بالا را میگیرد. FollowUpStaysGroundedTests پیامِ دومِ یک گفتگو را هم مستند نگه میدارد. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1493,3 +1493,70 @@ def _orig_loader():
|
||||
except _U.DoesNotExist:
|
||||
raise AuthenticationFailed('کاربر پیدا نشد.')
|
||||
return _load
|
||||
|
||||
|
||||
class UpdateUserProfileTests(TestCase):
|
||||
"""
|
||||
تستهای PATCH /api/user/profile.
|
||||
|
||||
صفحهی «بیا باهم آشنا بشیم» در اپ اسم میگیرد و تا پیش از این هیچ مسیری
|
||||
برای نوشتنش نبود — پروفایل فقط خواندنی بود.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
cache.clear()
|
||||
self.user = User.objects.create(device_id='device-patch-1', is_guest=True, name='')
|
||||
self.token = issue_access_token(self.user.id)
|
||||
|
||||
def tearDown(self):
|
||||
cache.clear()
|
||||
|
||||
def _patch(self, body, token=None):
|
||||
kwargs = {'format': 'json'}
|
||||
token = self.token if token is None else token
|
||||
if token:
|
||||
kwargs['HTTP_AUTHORIZATION'] = f'Bearer {token}'
|
||||
return self.client.patch('/api/user/profile', body, **kwargs)
|
||||
|
||||
def test_requires_authentication(self):
|
||||
self.assertEqual(self._patch({'name': 'سارا'}, token='').status_code, 401)
|
||||
|
||||
def test_sets_name_and_returns_the_updated_profile(self):
|
||||
response = self._patch({'name': 'سارا'})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data['name'], 'سارا')
|
||||
self.user.refresh_from_db()
|
||||
self.assertEqual(self.user.name, 'سارا')
|
||||
|
||||
def test_partial_update_leaves_other_fields_alone(self):
|
||||
self._patch({'name': 'علی', 'bio': 'عاشقِ ریاضی'})
|
||||
response = self._patch({'bio': 'کتابخوان'})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data['name'], 'علی')
|
||||
self.assertEqual(response.data['bio'], 'کتابخوان')
|
||||
|
||||
def test_creates_the_profile_row_lazily(self):
|
||||
self.assertFalse(Profile.objects.filter(user_id=self.user.id).exists())
|
||||
|
||||
response = self._patch({'birthYear': 1391})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data['birthYear'], 1391)
|
||||
self.assertTrue(Profile.objects.filter(user_id=self.user.id).exists())
|
||||
|
||||
def test_empty_body_is_rejected(self):
|
||||
self.assertEqual(self._patch({}).status_code, 400)
|
||||
|
||||
def test_phone_and_guest_flag_cannot_be_changed(self):
|
||||
response = self._patch({'name': 'رضا', 'phone': '09120000000', 'isGuest': False})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.user.refresh_from_db()
|
||||
self.assertIsNone(self.user.phone)
|
||||
self.assertTrue(self.user.is_guest)
|
||||
|
||||
def test_out_of_range_birth_year_is_rejected(self):
|
||||
self.assertEqual(self._patch({'birthYear': 2026}).status_code, 400)
|
||||
|
||||
Reference in New Issue
Block a user