调整部分前端布局和后端增删改逻辑

This commit is contained in:
焦龙言
2026-09-26 23:11:05 +08:00
parent 977e7f996e
commit 17d991a74a
117 changed files with 311994 additions and 3192 deletions
+8 -4
View File
@@ -58,8 +58,6 @@ cd shierfeng-fastapi-test
```bash
# MySQL版本
docker compose -f docker-compose.test.my.yml up -d --build
# PostgreSQL版本
docker compose -f docker-compose.test.pg.yml up -d --build
```
#### 运行测试
@@ -105,11 +103,17 @@ python -m pytest -v
## 配置说明
使用 `docker-compose.test.my.yml`或`docker-compose.test.pg.yml`启动服务,默认前端端口为 `80`,后端端口为 `9099`。测试环境已禁用验证码功能。
使用 `docker-compose.test.my.yml` 启动服务,默认前端端口为 `80`,后端端口为 `9099`。测试环境已禁用验证码功能。
测试账号密码不写入代码、文档或日志。运行测试前,请在受控环境注入以下环境变量:
- `SHIERFENG_TEST_ADMIN_PASSWORD`:管理员账号 `admin` 的测试密码
- `SHIERFENG_TEST_NIANGAO_PASSWORD`:`niangao` 账号的测试密码
- `SHIERFENG_TEST_USER_PASSWORD`:用户管理测试中新建账号的初始密码
## 注意事项
1. 确保系统已安装 Docker 和 Docker Compose
2. 确保端口 `80` 和 `9099` 未被占用
3. 首次运行时 Docker 镜像构建可能需要几分钟时间
4. 测试使用默认管理员账户:用户名 `admin`,密码 `admin123`
4. 测试使用管理员账户 `admin`;密码必须通过 `SHIERFENG_TEST_ADMIN_PASSWORD` 注入,不在仓库中记录
@@ -15,7 +15,7 @@ class BasePageTest:
"""初始化浏览器和登录"""
# 首先登录获取token
helper = LoginHelper()
self.token = helper.login(username='admin', password='admin123')
self.token = helper.login(username='admin')
assert self.token is not None, '登录应该成功'
# 启动浏览器
+15 -1
View File
@@ -1,3 +1,5 @@
import os
import requests
from common.config import Config
@@ -12,10 +14,13 @@ class LoginHelper:
self.base_url = base_url
self.session = requests.Session()
def login(self, username: str = 'admin', password: str = 'admin123', max_retries: int = 3) -> str | None:
def login(self, username: str = 'admin', password: str | None = None, max_retries: int = 3) -> str | None:
"""
执行登录操作(在测试环境中,验证码已禁用)
"""
if password is None:
password = get_test_password(username)
for _attempt in range(max_retries):
# 在测试环境中,验证码已禁用,所以直接登录
login_data = {'username': username, 'password': password}
@@ -47,6 +52,15 @@ class LoginHelper:
return None
def get_test_password(username: str) -> str:
"""读取测试账号密码,避免把凭据写入测试代码或日志。"""
env_name = f'SHIERFENG_TEST_{username.upper()}_PASSWORD'
password = os.getenv(env_name)
if not password:
raise RuntimeError(f'未设置测试账号密码环境变量: {env_name}')
return password
# 使用示例
if __name__ == '__main__':
helper = LoginHelper()
@@ -1,77 +0,0 @@
services:
# 前端服务
shierfeng-frontend:
build:
context: ../shierfeng-fastapi-frontend
dockerfile: Dockerfile
image: shierfeng-frontend:latest
container_name: shierfeng-frontend-test
ports:
- "80:80"
volumes:
- ../shierfeng-fastapi-frontend/bin/nginx.dockerpg.conf:/etc/nginx/conf.d/default.conf
depends_on:
- shierfeng-backend-pg
networks:
- shierfeng-network-test
# 后端服务(PostgreSQL版本)
shierfeng-backend-pg:
build:
context: ../shierfeng-fastapi-backend
dockerfile: Dockerfile.pg
image: shierfeng-backend-pg:latest
container_name: shierfeng-backend-pg-test
ports:
- "9099:9099"
environment:
TRANSPORT_CRYPTO_ENABLED: "false"
TRANSPORT_CRYPTO_MODE: "off"
depends_on:
shierfeng-pg:
condition: service_healthy
shierfeng-redis:
condition: service_healthy
networks:
- shierfeng-network-test
# PostgreSQL服务
shierfeng-pg:
image: postgres:14
container_name: shierfeng-pg-test
environment:
POSTGRES_PASSWORD: root
POSTGRES_DB: shierfeng-fastapi
POSTGRES_INITDB_ARGS: --encoding=UTF8 --lc-collate=C --lc-ctype=C
ports:
- "15433:5432"
volumes:
- ../shierfeng-fastapi-backend/sql/shierfeng-fastapi-pg.sql:/docker-entrypoint-initdb.d/01-shierfeng-fastapi-pg.sql
- ./disable_captcha.sql:/docker-entrypoint-initdb.d/02-disable-captcha.sql
networks:
- shierfeng-network-test
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 5s
timeout: 10s
retries: 30
# Redis服务
shierfeng-redis:
image: redis:latest
container_name: shierfeng-redis-test
ports:
- "16381:6379"
networks:
- shierfeng-network-test
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 10s
retries: 30
# 网络配置
networks:
shierfeng-network-test:
name: shierfeng-network-test
driver: bridge
@@ -3,6 +3,7 @@ from playwright.async_api import async_playwright
from common.base_page_test import BasePageTest
from common.config import Config
from common.login_helper import get_test_password
class OnlineUserTest(BasePageTest):
@@ -16,21 +17,11 @@ class OnlineUserTest(BasePageTest):
niangao_page = await niangao_context.new_page()
# 登录 niangao
try:
await niangao_page.goto(Config.frontend_url + '/login')
await niangao_page.locator('div.el-form-item', has_text='账号').locator('input').fill('niangao')
await niangao_page.locator('div.el-form-item', has_text='密码').locator('input').fill('admin123')
await niangao_page.get_by_role('button', name='登 录').click()
# 等待登录成功
await niangao_page.wait_for_url('**/index')
except Exception as e:
print(f'Niangao login failed: {e}. Trying admin123')
try:
await niangao_page.locator('div.el-form-item', has_text='密码').locator('input').fill('123456')
await niangao_page.get_by_role('button', name='登 录').click()
await niangao_page.wait_for_url('**/index')
except Exception as e2:
print(f'Niangao login failed again: {e2}')
await niangao_page.goto(Config.frontend_url + '/login')
await niangao_page.locator('div.el-form-item', has_text='账号').locator('input').fill('niangao')
await niangao_page.locator('div.el-form-item', has_text='密码').locator('input').fill(get_test_password('niangao'))
await niangao_page.get_by_role('button', name='登 录').click()
await niangao_page.wait_for_url('**/index')
# 2. 切换回 admin (self.page) 进行操作
await self.page.goto(Config.frontend_url + '/monitor/online')
@@ -5,6 +5,7 @@ from playwright.async_api import async_playwright
from common.base_page_test import BasePageTest
from common.config import Config
from common.login_helper import get_test_password
class UserManagementTest(BasePageTest):
@@ -44,7 +45,7 @@ class UserManagementTest(BasePageTest):
# 这里使用 get_by_placeholder 或者 filter 来定位更稳妥,或者直接 scope 到 dialog
await dialog.locator('div.el-form-item').filter(has_text='手机号码').get_by_role('textbox').fill(phone)
await dialog.locator('div.el-form-item').filter(has_text='邮箱').get_by_role('textbox').fill('test@example.com')
await dialog.locator('div.el-form-item').filter(has_text='用户密码').get_by_role('textbox').fill('123456')
await dialog.locator('div.el-form-item').filter(has_text='用户密码').get_by_role('textbox').fill(get_test_password('user'))
# 用户性别
# 定位 label 为 "用户性别" 的父级 form-item,然后找里面的 "请选择" 文本
+4 -4
View File
@@ -2,7 +2,7 @@ import pytest
from playwright.async_api import async_playwright
from common.config import Config
from common.login_helper import LoginHelper
from common.login_helper import LoginHelper, get_test_password
@pytest.mark.asyncio
@@ -51,7 +51,7 @@ def test_login_without_captcha() -> None:
helper = LoginHelper()
# 尝试登录(测试环境中验证码已禁用)
token = helper.login(username='admin', password='admin123')
token = helper.login(username='admin')
# 验证登录结果
assert token is not None, '登录应该成功'
@@ -73,7 +73,7 @@ async def test_login_flow_with_playwright() -> None:
# 输入用户名和密码
await page.fill('input[placeholder="账号"]', 'admin')
await page.fill('input[placeholder="密码"]', 'admin123')
await page.fill('input[placeholder="密码"]', get_test_password('admin'))
# 在测试环境中验证码已禁用,所以不需要处理验证码
# 直接点击登录按钮
@@ -151,7 +151,7 @@ async def test_authenticated_access() -> None:
"""测试认证后的访问"""
# 首先通过API登录获取token
helper = LoginHelper()
token = helper.login(username='admin', password='admin123')
token = helper.login(username='admin')
assert token is not None, '登录应该成功'
+3 -3
View File
@@ -10,7 +10,7 @@ async def test_dashboard_page() -> None:
"""测试仪表盘页面"""
# 首先登录获取token
helper = LoginHelper()
token = helper.login(username='admin', password='admin123')
token = helper.login(username='admin')
assert token is not None, '登录应该成功'
async with async_playwright() as p:
@@ -48,7 +48,7 @@ async def test_druid_page() -> None:
"""测试数据监控页面"""
# 首先登录获取token
helper = LoginHelper()
token = helper.login(username='admin', password='admin123')
token = helper.login(username='admin')
assert token is not None, '登录应该成功'
async with async_playwright() as p:
@@ -86,7 +86,7 @@ async def test_build_page() -> None:
"""测试表单构建页面"""
# 首先登录获取token
helper = LoginHelper()
token = helper.login(username='admin', password='admin123')
token = helper.login(username='admin')
assert token is not None, '登录应该成功'
async with async_playwright() as p:
@@ -0,0 +1,41 @@
import pytest
from common.login_helper import LoginHelper
class _Response:
status_code = 401
def json(self) -> dict[str, object]:
return {'code': 401, 'msg': '测试响应'}
class _RecordingSession:
def __init__(self) -> None:
self.requests: list[dict[str, str]] = []
def post(self, url: str, data: dict[str, str], headers: dict[str, str]) -> _Response:
self.requests.append(data)
return _Response()
def test_login_uses_admin_password_from_environment_when_password_is_omitted(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv('SHIERFENG_TEST_ADMIN_PASSWORD', 'test-only-admin-secret')
helper = LoginHelper()
session = _RecordingSession()
helper.session = session
assert helper.login(username='admin', max_retries=1) is None
assert session.requests == [
{'username': 'admin', 'password': 'test-only-admin-secret'},
]
def test_login_fails_fast_when_account_password_environment_is_missing(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv('SHIERFENG_TEST_ADMIN_PASSWORD', raising=False)
helper = LoginHelper()
with pytest.raises(RuntimeError, match='SHIERFENG_TEST_ADMIN_PASSWORD'):
helper.login(username='admin', max_retries=1)