文章列表新增author_user_id/is_following与feed=following过滤;课程students_count改真实在学人数去重统计、新增total_minutes(不可解析不伪造);author_name走sync_to_async防ASGI同步阻塞。
92 lines
4.2 KiB
Python
92 lines
4.2 KiB
Python
"""C-06 签到幂等 + 学习聚合去重回归。"""
|
|
from django.test import TestCase
|
|
from rest_framework.test import APIClient
|
|
|
|
from learn.models import Chapter, ChapterRead, Course
|
|
from user.models import DailyCheckin, FUser
|
|
|
|
|
|
class CheckinIdempotentTest(TestCase):
|
|
def setUp(self):
|
|
self.client = APIClient()
|
|
self.u = FUser.objects.create_user(username='c06sa', password='x12345678')
|
|
|
|
def test_double_checkin_second_400_no_double_points(self):
|
|
self.client.force_authenticate(user=self.u)
|
|
r1 = self.client.post('/user/wallet/checkin/')
|
|
self.assertEqual(r1.status_code, 200)
|
|
earned = r1.json()['data']['earned']
|
|
self.u.refresh_from_db()
|
|
p1 = self.u.points
|
|
self.assertGreaterEqual(p1, earned)
|
|
r2 = self.client.post('/user/wallet/checkin/')
|
|
self.assertEqual(r2.status_code, 400)
|
|
self.u.refresh_from_db()
|
|
self.assertEqual(self.u.points, p1)
|
|
self.assertEqual(DailyCheckin.objects.filter(user=self.u).count(), 1)
|
|
|
|
def test_checkin_status_reflects_signed(self):
|
|
self.client.force_authenticate(user=self.u)
|
|
self.client.post('/user/wallet/checkin/')
|
|
r = self.client.get('/user/wallet/checkin/status/')
|
|
self.assertEqual(r.status_code, 200)
|
|
self.assertTrue(r.json()['data']['signed_today'])
|
|
|
|
def test_anonymous_checkin_401(self):
|
|
r = self.client.post('/user/wallet/checkin/')
|
|
self.assertEqual(r.status_code, 401)
|
|
|
|
|
|
class LearnersCountTest(TestCase):
|
|
def setUp(self):
|
|
self.client = APIClient()
|
|
self.u1 = FUser.objects.create_user(username='c06la', password='x12345678')
|
|
self.u2 = FUser.objects.create_user(username='c06lb', password='x12345678')
|
|
self.course = Course.objects.create(title='C06聚合课', author=self.u1, status='published')
|
|
self.ch1 = Chapter.objects.create(course=self.course, title='第一章')
|
|
self.ch2 = Chapter.objects.create(course=self.course, title='第二章')
|
|
|
|
def test_cross_read_dedup_two_users(self):
|
|
ChapterRead.objects.create(user=self.u1, chapter=self.ch1, course=self.course)
|
|
ChapterRead.objects.create(user=self.u1, chapter=self.ch2, course=self.course)
|
|
ChapterRead.objects.create(user=self.u2, chapter=self.ch1, course=self.course)
|
|
r = self.client.get('/learn/courses/')
|
|
self.assertEqual(r.status_code, 200)
|
|
items = {x['title']: x for x in r.json()['data']}
|
|
self.assertEqual(items['C06聚合课']['students_count'], 2)
|
|
|
|
def test_no_read_zero(self):
|
|
r = self.client.get('/learn/courses/')
|
|
self.assertEqual(r.status_code, 200)
|
|
items = {x['title']: x for x in r.json()['data']}
|
|
self.assertEqual(items['C06聚合课']['students_count'], 0)
|
|
|
|
def test_detail_students_count_matches(self):
|
|
ChapterRead.objects.create(user=self.u1, chapter=self.ch1, course=self.course)
|
|
r = self.client.get(f'/learn/courses/{self.course.id}/')
|
|
self.assertEqual(r.status_code, 200)
|
|
self.assertEqual(r.json()['data']['students_count'], 1)
|
|
|
|
|
|
class TotalMinutesTest(TestCase):
|
|
def setUp(self):
|
|
self.client = APIClient()
|
|
self.u = FUser.objects.create_user(username='c06ma', password='x12345678')
|
|
|
|
def test_sums_parsable_durations(self):
|
|
course = Course.objects.create(title='C06时长课', author=self.u, status='published')
|
|
Chapter.objects.create(course=course, title='C1', duration='30分钟')
|
|
Chapter.objects.create(course=course, title='C2', duration='45分钟')
|
|
r = self.client.get('/learn/courses/')
|
|
self.assertEqual(r.status_code, 200)
|
|
items = {x['title']: x for x in r.json()['data']}
|
|
self.assertEqual(items['C06时长课']['total_minutes'], 75)
|
|
|
|
def test_unparsable_returns_none(self):
|
|
course = Course.objects.create(title='C06无时长课', author=self.u, status='published')
|
|
Chapter.objects.create(course=course, title='C1', duration='')
|
|
Chapter.objects.create(course=course, title='C2', duration='未知')
|
|
r = self.client.get(f'/learn/courses/{course.id}/')
|
|
self.assertEqual(r.status_code, 200)
|
|
self.assertIsNone(r.json()['data']['total_minutes'])
|