From 3668b10be33f3ad93b63d96172cca67fe20951e4 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 4 Mar 2026 18:37:45 +0800 Subject: [PATCH] =?UTF-8?q?test(expiry):=20=E5=AE=8C=E6=88=90=20#32=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95=E8=B5=84?= =?UTF-8?q?=E4=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reports/api_integration_2026-03-04.md | 33 ++++ .../postman/expiry.postman_collection.json | 170 ++++++++++++++++++ .../postman/expiry.postman_environment.json | 14 ++ scripts/expiry/run_integration_tests.sh | 97 ++++++++++ 4 files changed, 314 insertions(+) create mode 100644 docs/expiry/reports/api_integration_2026-03-04.md create mode 100644 docs/expiry/tests/postman/expiry.postman_collection.json create mode 100644 docs/expiry/tests/postman/expiry.postman_environment.json create mode 100755 scripts/expiry/run_integration_tests.sh diff --git a/docs/expiry/reports/api_integration_2026-03-04.md b/docs/expiry/reports/api_integration_2026-03-04.md new file mode 100644 index 0000000..139a0a7 --- /dev/null +++ b/docs/expiry/reports/api_integration_2026-03-04.md @@ -0,0 +1,33 @@ +# 保质期接口集成测试报告(2026-03-04) + +## 范围 +- 登录获取 Token(`/api/v1/auth/login`) +- 物品接口:创建、列表、汇总、更新、状态标记、删除 +- 设置接口:获取与更新 + +## 测试资产 +- Postman Collection: `docs/expiry/tests/postman/expiry.postman_collection.json` +- Postman Environment: `docs/expiry/tests/postman/expiry.postman_environment.json` +- 一键脚本: `scripts/expiry/run_integration_tests.sh` + +## 执行方式 +```bash +# 方式一:已有 token +BASE_URL=http://127.0.0.1:8080 TOKEN= scripts/expiry/run_integration_tests.sh + +# 方式二:通过 wx.login 的 code 登录 +BASE_URL=http://127.0.0.1:8080 MINI_PROGRAM_ID=1 WX_CODE= scripts/expiry/run_integration_tests.sh +``` + +## 断言规则 +- 登录接口:`HTTP 200 && code == 200` +- 业务接口:`HTTP 200 && code == 0` +- 错误接口:预期返回 4xx/5xx 与对应 message + +## 性能测试建议 +脚本内置 `hey` 调用(若系统已安装): +```bash +hey -n 100 -c 20 -H "Authorization: Bearer " http://127.0.0.1:8080/api/expiry/summary +``` + +目标:平均响应时间 < 200ms(开发环境仅作参考,生产环境需复测)。 diff --git a/docs/expiry/tests/postman/expiry.postman_collection.json b/docs/expiry/tests/postman/expiry.postman_collection.json new file mode 100644 index 0000000..caebd50 --- /dev/null +++ b/docs/expiry/tests/postman/expiry.postman_collection.json @@ -0,0 +1,170 @@ +{ + "info": { + "_postman_id": "3f8f9f2a-8b2f-4cc0-91df-0c8d7a03b101", + "name": "Expiry API Integration", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "description": "保质期提醒小程序后端接口集成测试集合" + }, + "item": [ + { + "name": "Auth - Login", + "request": { + "method": "POST", + "header": [ + { "key": "Content-Type", "value": "application/json" } + ], + "body": { + "mode": "raw", + "raw": "{\n \"mini_program_id\": {{mini_program_id}},\n \"code\": \"{{wx_code}}\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/auth/login", + "host": ["{{base_url}}"], + "path": ["api", "v1", "auth", "login"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test('login status 200', function () {", + " pm.response.to.have.status(200);", + "});", + "const json = pm.response.json();", + "pm.test('login business code 200', function () {", + " pm.expect(json.code).to.eql(200);", + "});", + "if (json.data && json.data.session_key) {", + " pm.environment.set('token', json.data.session_key);", + "}" + ], + "type": "text/javascript" + } + } + ] + }, + { + "name": "Expiry - Create Item", + "request": { + "method": "POST", + "header": [ + { "key": "Content-Type", "value": "application/json" }, + { "key": "Authorization", "value": "Bearer {{token}}" } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"自动化牛奶\",\n \"category\": \"food\",\n \"expiry_date\": \"2030-12-31\",\n \"quantity\": 2,\n \"location\": \"冰箱\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/expiry/items", + "host": ["{{base_url}}"], + "path": ["api", "expiry", "items"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const json = pm.response.json();", + "pm.test('create success', function () {", + " pm.expect(pm.response.code).to.eql(200);", + " pm.expect(json.code).to.eql(0);", + "});", + "if (json.data && json.data.id) {", + " pm.environment.set('item_id', json.data.id);", + "}" + ], + "type": "text/javascript" + } + } + ] + }, + { + "name": "Expiry - Get Items", + "request": { + "method": "GET", + "header": [ + { "key": "Authorization", "value": "Bearer {{token}}" } + ], + "url": { + "raw": "{{base_url}}/api/expiry/items?status=all&page=1&page_size=20", + "host": ["{{base_url}}"], + "path": ["api", "expiry", "items"], + "query": [ + { "key": "status", "value": "all" }, + { "key": "page", "value": "1" }, + { "key": "page_size", "value": "20" } + ] + } + } + }, + { + "name": "Expiry - Get Summary", + "request": { + "method": "GET", + "header": [ + { "key": "Authorization", "value": "Bearer {{token}}" } + ], + "url": { + "raw": "{{base_url}}/api/expiry/summary", + "host": ["{{base_url}}"], + "path": ["api", "expiry", "summary"] + } + } + }, + { + "name": "Expiry - Update Status Used", + "request": { + "method": "POST", + "header": [ + { "key": "Content-Type", "value": "application/json" }, + { "key": "Authorization", "value": "Bearer {{token}}" } + ], + "body": { + "mode": "raw", + "raw": "{\n \"status\": \"used\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/expiry/items/{{item_id}}/status", + "host": ["{{base_url}}"], + "path": ["api", "expiry", "items", "{{item_id}}", "status"] + } + } + }, + { + "name": "Expiry - Settings Get", + "request": { + "method": "GET", + "header": [ + { "key": "Authorization", "value": "Bearer {{token}}" } + ], + "url": { + "raw": "{{base_url}}/api/expiry/settings", + "host": ["{{base_url}}"], + "path": ["api", "expiry", "settings"] + } + } + }, + { + "name": "Expiry - Settings Update", + "request": { + "method": "POST", + "header": [ + { "key": "Content-Type", "value": "application/json" }, + { "key": "Authorization", "value": "Bearer {{token}}" } + ], + "body": { + "mode": "raw", + "raw": "{\n \"remind_days\": [10, 5, 1]\n}" + }, + "url": { + "raw": "{{base_url}}/api/expiry/settings", + "host": ["{{base_url}}"], + "path": ["api", "expiry", "settings"] + } + } + } + ] +} diff --git a/docs/expiry/tests/postman/expiry.postman_environment.json b/docs/expiry/tests/postman/expiry.postman_environment.json new file mode 100644 index 0000000..7b1ac36 --- /dev/null +++ b/docs/expiry/tests/postman/expiry.postman_environment.json @@ -0,0 +1,14 @@ +{ + "id": "4f72d514-2fe4-4125-9ef0-2b5240ca07c4", + "name": "Expiry Local Env", + "values": [ + { "key": "base_url", "value": "http://127.0.0.1:8080", "enabled": true }, + { "key": "mini_program_id", "value": "1", "enabled": true }, + { "key": "wx_code", "value": "请替换为wx.login()返回code", "enabled": true }, + { "key": "token", "value": "", "enabled": true }, + { "key": "item_id", "value": "", "enabled": true } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2026-03-04T18:40:00.000Z", + "_postman_exported_using": "postman" +} diff --git a/scripts/expiry/run_integration_tests.sh b/scripts/expiry/run_integration_tests.sh new file mode 100755 index 0000000..607f3da --- /dev/null +++ b/scripts/expiry/run_integration_tests.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +set -euo pipefail + +BASE_URL="${BASE_URL:-http://127.0.0.1:8080}" +MINI_PROGRAM_ID="${MINI_PROGRAM_ID:-1}" +WX_CODE="${WX_CODE:-}" +TOKEN="${TOKEN:-}" + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || { + echo "缺少命令: $1" >&2 + exit 1 + } +} + +need_cmd curl +need_cmd jq + +echo "[1/8] 准备 token" +if [[ -z "$TOKEN" ]]; then + if [[ -z "$WX_CODE" ]]; then + echo "请提供 TOKEN 或 WX_CODE" >&2 + exit 1 + fi + + TOKEN="$(curl -sS -X POST "${BASE_URL}/api/v1/auth/login" \ + -H 'Content-Type: application/json' \ + -d "{\"mini_program_id\":${MINI_PROGRAM_ID},\"code\":\"${WX_CODE}\"}" \ + | jq -r '.data.session_key')" +fi + +if [[ -z "$TOKEN" || "$TOKEN" == "null" ]]; then + echo "获取 token 失败" >&2 + exit 1 +fi + +auth_header=( -H "Authorization: Bearer ${TOKEN}" ) + +assert_code_zero() { + local payload="$1" + local api_code + api_code="$(echo "$payload" | jq -r '.code')" + if [[ "$api_code" != "0" ]]; then + echo "接口业务码异常: ${api_code}, payload=${payload}" >&2 + exit 1 + fi +} + +echo "[2/8] 创建物品" +create_payload="$(curl -sS -X POST "${BASE_URL}/api/expiry/items" \ + "${auth_header[@]}" \ + -H 'Content-Type: application/json' \ + -d '{"name":"集成测试牛奶","category":"food","expiry_date":"2030-12-31","quantity":2,"location":"冰箱"}')" +assert_code_zero "$create_payload" +item_id="$(echo "$create_payload" | jq -r '.data.id')" + +echo "[3/8] 查询列表" +list_payload="$(curl -sS "${BASE_URL}/api/expiry/items?status=all&page=1&page_size=20" "${auth_header[@]}")" +assert_code_zero "$list_payload" + +echo "[4/8] 查询汇总" +summary_payload="$(curl -sS "${BASE_URL}/api/expiry/summary" "${auth_header[@]}")" +assert_code_zero "$summary_payload" + +echo "[5/8] 更新物品" +update_payload="$(curl -sS -X PUT "${BASE_URL}/api/expiry/items/${item_id}" \ + "${auth_header[@]}" \ + -H 'Content-Type: application/json' \ + -d '{"name":"集成测试牛奶(更新)","category":"food","expiry_date":"2030-12-30","quantity":1}')" +assert_code_zero "$update_payload" + +echo "[6/8] 标记已使用" +status_payload="$(curl -sS -X POST "${BASE_URL}/api/expiry/items/${item_id}/status" \ + "${auth_header[@]}" \ + -H 'Content-Type: application/json' \ + -d '{"status":"used"}')" +assert_code_zero "$status_payload" + +echo "[7/8] 设置提醒" +settings_payload="$(curl -sS -X POST "${BASE_URL}/api/expiry/settings" \ + "${auth_header[@]}" \ + -H 'Content-Type: application/json' \ + -d '{"remind_days":[10,5,1]}')" +assert_code_zero "$settings_payload" + +echo "[8/8] 删除物品" +delete_payload="$(curl -sS -X DELETE "${BASE_URL}/api/expiry/items/${item_id}" "${auth_header[@]}")" +assert_code_zero "$delete_payload" + +echo "全部接口集成测试通过" + +if command -v hey >/dev/null 2>&1; then + echo "开始性能测试: /api/expiry/summary 并发 20,总请求 100" + hey -n 100 -c 20 -H "Authorization: Bearer ${TOKEN}" "${BASE_URL}/api/expiry/summary" +else + echo "未安装 hey,跳过性能测试(建议安装后执行:hey -n 100 -c 20 ${BASE_URL}/api/expiry/summary)" +fi