Files
chunyu 24874aae23 feat(C-02):工具热榜周榜+最近使用
新增ToolUsageRecord使用日志(tool+created_at索引);/tool/top/总榜按累计排序、周榜按近7天聚合(无日志回退总榜,兼容period旧参);/tool/recent/登录去重倒序;seed新增AI图片放大/代码运行两个工具。
2026-09-15 15:20:27 +08:00

93 lines
4.2 KiB
Python

"""C-02 工具热榜聚合用例:总榜/周榜窗口/匿名归因/recent 权限/兼容参数。"""
from datetime import timedelta
from django.test import TestCase
from django.utils import timezone
from rest_framework.test import APIClient
from tool.models import Tool, ToolUsageRecord
from user.models import FUser
class ToolTopTest(TestCase):
def setUp(self):
self.client = APIClient()
self.a = Tool.objects.create(name='A', url_path='/utility/a-tool', usage_count=10)
self.b = Tool.objects.create(name='B', url_path='/utility/b-tool', usage_count=5)
self.c = Tool.objects.create(name='C', url_path='/utility/c-tool', usage_count=1)
self.u = FUser.objects.create_user(username='c02u', password='x12345678')
def test_total_rank_order(self):
r = self.client.get('/tool/top/', {'range': 'all', 'limit': 3})
self.assertEqual(r.status_code, 200)
data = r.json()['data']
self.assertEqual([x['id'] for x in data], [self.a.id, self.b.id, self.c.id])
self.assertEqual([x['rank'] for x in data], [1, 2, 3])
for x in data:
self.assertIn('usage_count', x)
self.assertIn('weekly_count', x)
def test_period_alias_total(self):
r = self.client.get('/tool/top/', {'period': 'total', 'limit': 3})
self.assertEqual(r.status_code, 200)
self.assertEqual([x['rank'] for x in r.json()['data']], [1, 2, 3])
def test_weekly_window_aggregation(self):
now = timezone.now()
for _ in range(3):
ToolUsageRecord.objects.create(tool=self.c, user=self.u)
old = ToolUsageRecord.objects.create(tool=self.a, user=self.u)
old.created_at = now - timedelta(days=8)
old.save(update_fields=['created_at'])
r = self.client.get('/tool/top/', {'range': 'week', 'limit': 3})
self.assertEqual(r.status_code, 200)
data = r.json()['data']
# C 近7天3次应排第一
self.assertEqual(data[0]['id'], self.c.id)
self.assertEqual(data[0]['weekly_count'], 3)
def test_weekly_boundary_7days(self):
now = timezone.now()
edge = ToolUsageRecord.objects.create(tool=self.b)
edge.created_at = now - timedelta(days=7) + timedelta(seconds=60)
edge.save(update_fields=['created_at'])
out = ToolUsageRecord.objects.create(tool=self.b)
out.created_at = now - timedelta(days=7, seconds=60)
out.save(update_fields=['created_at'])
r = self.client.get('/tool/top/', {'range': 'week', 'limit': 10})
data = {x['id']: x['weekly_count'] for x in r.json()['data']}
self.assertEqual(data[self.b.id], 1)
def test_weekly_empty_fallback_to_total(self):
r = self.client.get('/tool/top/', {'range': 'week', 'limit': 3})
self.assertEqual(r.status_code, 200)
self.assertEqual([x['id'] for x in r.json()['data']], [self.a.id, self.b.id, self.c.id])
def test_increment_writes_record_anonymous(self):
r = self.client.post(f'/tool/usage/{self.a.id}/increment/')
self.assertEqual(r.status_code, 200)
self.assertEqual(ToolUsageRecord.objects.filter(tool=self.a).count(), 1)
self.a.refresh_from_db()
self.assertEqual(self.a.usage_count, 11)
def test_increment_writes_record_login(self):
self.client.force_authenticate(user=self.u)
r = self.client.post(f'/tool/usage/{self.b.id}/increment/')
self.assertEqual(r.status_code, 200)
rec = ToolUsageRecord.objects.filter(tool=self.b).order_by('-id').first()
self.assertEqual(rec.user_id, self.u.id)
def test_recent_requires_login(self):
r = self.client.get('/tool/recent/')
self.assertIn(r.status_code, (401, 403))
def test_recent_dedup_order(self):
ToolUsageRecord.objects.create(tool=self.a, user=self.u)
ToolUsageRecord.objects.create(tool=self.b, user=self.u)
ToolUsageRecord.objects.create(tool=self.a, user=self.u)
self.client.force_authenticate(user=self.u)
r = self.client.get('/tool/recent/', {'limit': 10})
self.assertEqual(r.status_code, 200)
ids = [x['id'] for x in r.json()['data']]
self.assertEqual(ids, [self.a.id, self.b.id])