mirror of
https://github.com/wahyd4/links.git
synced 2026-08-09 05:06:16 +10:00
114 lines
4.6 KiB
Python
114 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
||
"""端到端测试:模拟用户在 UI 创建 ACTION 链接 → 访问链接 → 验证 Discord 发送 + 结果页。"""
|
||
import os
|
||
import sys
|
||
import re
|
||
import django
|
||
|
||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
|
||
os.environ['DISCORD_WEBHOOK_URL'] = os.environ.get('DISCORD_WEBHOOK_URL', '')
|
||
if not os.environ['DISCORD_WEBHOOK_URL']:
|
||
print('❌ 需要 DISCORD_WEBHOOK_URL 环境变量')
|
||
sys.exit(1)
|
||
|
||
django.setup()
|
||
|
||
from django.test import Client
|
||
from django.contrib.auth import get_user_model
|
||
from unittest.mock import patch
|
||
from links.models import Link
|
||
|
||
# 创建/获取测试用户并登录(links 管理页需要认证)
|
||
User = get_user_model()
|
||
if not User.objects.filter(username='e2e_tester').exists():
|
||
User.objects.create_user(username='e2e_tester', password='e2e_pass_123')
|
||
client = Client()
|
||
client.force_login(User.objects.get(username='e2e_tester'))
|
||
|
||
ALIAS = 'e2eask'
|
||
|
||
# 1. 打开创建页(links 的创建页在 /create/)
|
||
resp = client.get('/create/')
|
||
assert resp.status_code == 200, f'创建页 {resp.status_code}'
|
||
html = resp.content.decode()
|
||
assert '⚡ Action' in html, '缺少 Action tab'
|
||
assert 'id="action-tab"' in html, '缺少 action-tab 元素'
|
||
print('✅ 1. 创建页加载,Action tab 存在')
|
||
|
||
# 2. 提取 csrf 并模拟表单提交(Action 类型)
|
||
m = re.search(r'name="csrfmiddlewaretoken" value="([^"]+)"', html)
|
||
assert m, '找不到 csrf token'
|
||
csrf = m.group(1)
|
||
|
||
resp = client.post('/create/', {
|
||
'csrfmiddlewaretoken': csrf,
|
||
'alias': ALIAS,
|
||
'link_type': 'ACTION',
|
||
'action_type': 'discord_send',
|
||
'action_message_template': '@小黑 {query}',
|
||
'description': '端到端测试动作链接',
|
||
}, follow=True)
|
||
assert resp.status_code == 200, f'创建提交 {resp.status_code}'
|
||
|
||
link = Link.objects.get(alias=ALIAS)
|
||
assert link.link_type == 'ACTION', f'link_type={link.link_type}'
|
||
assert link.action_config == {'action_type': 'discord_send', 'message_template': '@小黑 {query}'}, link.action_config
|
||
print('✅ 2. 表单创建成功,action_config 正确保存:', link.action_config)
|
||
|
||
# 3. 编辑页预填验证
|
||
resp = client.get(f'/link/{link.pk}/edit/')
|
||
html = resp.content.decode()
|
||
assert 'value="discord_send"' in html or 'discord_send' in html, '编辑页未预填 action_type'
|
||
assert '@小黑 {query}' in html, '编辑页未预填 message template'
|
||
print('✅ 3. 编辑页正确预填动作配置')
|
||
|
||
# 4. 访问动作链接 → 发 Discord + 302 跳转(不渲染页面)
|
||
with patch('links.actions.send_discord_message') as mock_send:
|
||
resp = client.get(f'/{ALIAS}/你好墨尔本/')
|
||
assert resp.status_code == 302, f'应 302 跳转: {resp.status_code}'
|
||
assert resp['Location'].startswith('https://discord.com/channels/'), resp['Location']
|
||
mock_send.assert_called_once_with('@小黑 你好墨尔本')
|
||
print('✅ 4. 访问链接 302 跳转 Discord,消息已发送(不发页面)')
|
||
|
||
# 4b. 防重:10 分钟内同一问题刷新不重复发送
|
||
with patch('links.actions.send_discord_message') as mock_send:
|
||
resp = client.get(f'/{ALIAS}/你好墨尔本/') # 刷新/重复访问
|
||
assert resp.status_code == 302
|
||
mock_send.assert_not_called(), '防重窗口内不应再次发送'
|
||
print('✅ 4b. 10 分钟防重生效:刷新不重复发送')
|
||
|
||
# 5. 无参数访问 → 提示缺参数,不发送空消息
|
||
with patch('links.actions.send_discord_message') as mock_send:
|
||
resp = client.get(f'/{ALIAS}/')
|
||
assert resp.status_code == 200
|
||
assert '链接缺少参数' in resp.content.decode(), '缺少参数提示未出现'
|
||
mock_send.assert_not_called(), '空参数不应发送消息'
|
||
print('✅ 5. 无参数访问提示缺参数,不发送空消息')
|
||
|
||
# 6. 表单校验:ACTION 缺模板 → 报错
|
||
resp = client.post('/create/', {
|
||
'csrfmiddlewaretoken': csrf,
|
||
'alias': 'badaction',
|
||
'link_type': 'ACTION',
|
||
'action_type': 'discord_send',
|
||
'action_message_template': '',
|
||
}, follow=True)
|
||
assert 'Message template is required' in resp.content.decode(), '缺少模板未报错'
|
||
print('✅ 6. ACTION 缺模板正确报错')
|
||
|
||
# 7. 普通链接不受影响
|
||
resp = client.post('/create/', {
|
||
'csrfmiddlewaretoken': csrf,
|
||
'alias': 'e2eplain',
|
||
'link_type': 'LINK',
|
||
'original_url': 'https://example.com',
|
||
}, follow=True)
|
||
assert resp.status_code == 200
|
||
plain = Link.objects.get(alias='e2eplain')
|
||
assert plain.action_config == {}, f'普通链接 action_config 应为空: {plain.action_config}'
|
||
print('✅ 7. 普通链接创建正常,action_config 清空')
|
||
|
||
# 清理测试数据
|
||
Link.objects.filter(alias__in=['e2eask', 'e2eplain']).delete()
|
||
print('\n🎉 端到端测试全部通过!检查 Discord #general 应该有一条 @小黑 你好墨尔本')
|