Files
links/scripts/e2e_action_links.py
T

114 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 应该有一条 @小黑 你好墨尔本')