chore: 删除卡价格验证 hurl 测试文件
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m14s

This commit is contained in:
2026-03-31 15:26:09 +08:00
parent 67e0840695
commit 00cae60fe1
3 changed files with 1 additions and 382 deletions

View File

@@ -1,6 +1,6 @@
SHELL := /bin/bash
ENV ?= dev
HURL_OPTS := --variables-file env/$(ENV).env --test
HURL_OPTS := --variables-file env/$(ENV).env --file-root . --test
.PHONY: test test-flows test-modules test-negative report clean

View File

@@ -1,381 +0,0 @@
# ============================================================
# 测试:卡导入 → 分配 → C 端价格验证
# 生成时间2026-03-30
# 涉及模块auth, iot_card(import), shop, package_series, package,
# shop_series_grant, shop_package_allocation, client_auth, client_asset
# ============================================================
# 流程:
# 1. 平台管理员登录
# 2. 查询运营商列表,获取 carrier_id
# 3. 获取对象存储上传 URLpurpose=iot_import
# 4. 上传 Excel 文件IoT 卡测试数据)到预签名 URL
# 5. 提交卡导入任务
# 6. 轮询导入任务直到完成status=3
# 7. 获取一级代理店铺 IDshop1_id
# 8. 获取二级代理店铺 IDshop2_id
# 9. 获取套餐系列 IDseries_id和套餐 IDpackage_id
# 10. 分配 ICCID_1 给一级代理shop1
# 11. 分配 ICCID_2 给二级代理shop2
# 12. 绑定两张卡到套餐系列
# 13. C 端测试登录ICCID_1
# 14. 验证 ICCID_1 的可购套餐 → retail_price = 一级代理零售价15000
# 15. C 端测试登录ICCID_2
# 16. 验证 ICCID_2 的可购套餐 → retail_price = 二级代理零售价18000
# ============================================================
# 前置条件:
# 1. 已运行 package-resource-full-flow.hurl创建了 shop1/shop2/series/package
# 2. 开启 logging.development=true服务配置
# 3. 已配置对象存储S3/MinIO步骤 3-4 依赖对象存储上传
# 4. env 文件中已配置 agent1_username/agent2_username
# ============================================================
# ══════════════════════════════════════════════════════════════
# 第 1 步:平台管理员登录
# ══════════════════════════════════════════════════════════════
POST {{base_url}}/api/auth/login
Content-Type: application/json
{
"username": "{{admin_username}}",
"password": "{{admin_password}}",
"device": "web"
}
HTTP 200
[Captures]
admin_token: jsonpath "$.data.access_token"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.access_token" isString
jsonpath "$.data.user.user_type" == 1
# ══════════════════════════════════════════════════════════════
# 第 2 步:查询运营商列表,获取第一个可用 carrier_id
# ══════════════════════════════════════════════════════════════
GET {{base_url}}/api/admin/carriers?page=1&page_size=1&status=1
Authorization: Bearer {{admin_token}}
HTTP 200
[Captures]
carrier_id: jsonpath "$.data.items[0].id"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.items" count >= 1
jsonpath "$.data.items[0].id" isInteger
# ══════════════════════════════════════════════════════════════
# 第 3 步:获取对象存储上传 URLIoT 卡导入 Excel
# ══════════════════════════════════════════════════════════════
POST {{base_url}}/api/admin/storage/upload-url
Authorization: Bearer {{admin_token}}
Content-Type: application/json
{
"file_name": "hurl-test-cards.xlsx",
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"purpose": "iot_import"
}
HTTP 200
[Captures]
upload_url: jsonpath "$.data.upload_url"
file_key: jsonpath "$.data.file_key"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.upload_url" isString
jsonpath "$.data.file_key" isString
jsonpath "$.data.expires_in" isInteger
# ══════════════════════════════════════════════════════════════
# 第 4 步:上传 Excel 文件到预签名 URL
# 文件路径相对于 Hurl 运行目录tests/hurl/
# ══════════════════════════════════════════════════════════════
PUT {{upload_url}}
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
file,testdata/hurl-test-cards.xlsx;
HTTP 200
# ══════════════════════════════════════════════════════════════
# 第 5 步:提交 IoT 卡导入任务
# 导入 Excel 包含 ICCID_1HURL-TEST-CARD-001和 ICCID_2HURL-TEST-CARD-002
# ══════════════════════════════════════════════════════════════
POST {{base_url}}/api/admin/iot-cards/import
Authorization: Bearer {{admin_token}}
Content-Type: application/json
{
"carrier_id": {{carrier_id}},
"batch_no": "HURL-PRICE-TEST",
"file_key": "{{file_key}}"
}
HTTP 200
[Captures]
import_task_id: jsonpath "$.data.task_id"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.task_id" isInteger
jsonpath "$.data.task_no" isString
# ══════════════════════════════════════════════════════════════
# 第 6 步轮询导入任务等待完成status=3 表示已完成)
# 注Hurl 不支持循环轮询,这里做 3 次间隔查询覆盖大多数场景
# 如需更长等待,可手动重跑此步骤
# ══════════════════════════════════════════════════════════════
# 第 1 次查询(立即)
GET {{base_url}}/api/admin/iot-cards/import-tasks/{{import_task_id}}
Authorization: Bearer {{admin_token}}
HTTP 200
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.id" == {{import_task_id}}
jsonpath "$.data.status" isInteger
# 等待 2 秒后第 2 次查询Hurl 通过 delay 选项实现)
GET {{base_url}}/api/admin/iot-cards/import-tasks/{{import_task_id}}
Authorization: Bearer {{admin_token}}
[Options]
delay: 2000
HTTP 200
[Asserts]
jsonpath "$.code" == 0
# 等待 2 秒后第 3 次查询,断言任务成功完成
GET {{base_url}}/api/admin/iot-cards/import-tasks/{{import_task_id}}
Authorization: Bearer {{admin_token}}
[Options]
delay: 2000
HTTP 200
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.status" == 3
jsonpath "$.data.success_count" >= 1
# ══════════════════════════════════════════════════════════════
# 第 7 步:获取一级代理店铺 IDshop1_id
# 依赖 package-resource-full-flow.hurl 已创建 agent1_username 账号
# ══════════════════════════════════════════════════════════════
GET {{base_url}}/api/admin/accounts?username={{agent1_username}}&page=1&page_size=1
Authorization: Bearer {{admin_token}}
HTTP 200
[Captures]
shop1_id: jsonpath "$.data.items[0].shop_id"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.items" count >= 1
jsonpath "$.data.items[0].username" == "{{agent1_username}}"
jsonpath "$.data.items[0].user_type" == 3
jsonpath "$.data.items[0].shop_id" isInteger
# ══════════════════════════════════════════════════════════════
# 第 8 步:获取二级代理店铺 IDshop2_id
# ══════════════════════════════════════════════════════════════
GET {{base_url}}/api/admin/accounts?username={{agent2_username}}&page=1&page_size=1
Authorization: Bearer {{admin_token}}
HTTP 200
[Captures]
shop2_id: jsonpath "$.data.items[0].shop_id"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.items" count >= 1
jsonpath "$.data.items[0].username" == "{{agent2_username}}"
jsonpath "$.data.items[0].user_type" == 3
jsonpath "$.data.items[0].shop_id" isInteger
# ══════════════════════════════════════════════════════════════
# 第 9 步:获取套餐系列 ID 和套餐 ID
# 依赖 package-resource-full-flow.hurl 创建的 "Hurl测试系列"
# ══════════════════════════════════════════════════════════════
# 获取套餐系列(按名称模糊匹配,取最新创建的)
GET {{base_url}}/api/admin/package-series?series_name=Hurl%E6%B5%8B%E8%AF%95%E7%B3%BB%E5%88%97&page=1&page_size=1
Authorization: Bearer {{admin_token}}
HTTP 200
[Captures]
series_id: jsonpath "$.data.items[0].id"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.items" count >= 1
jsonpath "$.data.items[0].series_name" == "Hurl测试系列"
# 获取该系列下的套餐(取最新的)
GET {{base_url}}/api/admin/packages?series_id={{series_id}}&page=1&page_size=1
Authorization: Bearer {{admin_token}}
HTTP 200
[Captures]
package_id: jsonpath "$.data.items[0].id"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.items" count >= 1
jsonpath "$.data.items[0].series_id" == {{series_id}}
# ══════════════════════════════════════════════════════════════
# 第 10 步:分配 ICCID_18986001234560001给一级代理shop1
# 幂等处理HTTP * 兼容已分配场景
# ══════════════════════════════════════════════════════════════
POST {{base_url}}/api/admin/iot-cards/standalone/allocate
Authorization: Bearer {{admin_token}}
Content-Type: application/json
{
"to_shop_id": {{shop1_id}},
"selection_type": "list",
"iccids": ["{{test_iccid_1}}"],
"remark": "Hurl 价格验证测试 - 一级代理"
}
HTTP *
[Asserts]
jsonpath "$.code" isInteger
# ══════════════════════════════════════════════════════════════
# 第 11 步:分配 ICCID_28986001234560002给二级代理shop2
# ══════════════════════════════════════════════════════════════
POST {{base_url}}/api/admin/iot-cards/standalone/allocate
Authorization: Bearer {{admin_token}}
Content-Type: application/json
{
"to_shop_id": {{shop2_id}},
"selection_type": "list",
"iccids": ["{{test_iccid_2}}"],
"remark": "Hurl 价格验证测试 - 二级代理"
}
HTTP *
[Asserts]
jsonpath "$.code" isInteger
# ══════════════════════════════════════════════════════════════
# 第 12 步绑定两张卡到套餐系列series_binding
# 卡需要绑定套餐系列才能在 C 端看到可购套餐
# ══════════════════════════════════════════════════════════════
PATCH {{base_url}}/api/admin/iot-cards/series-binding
Authorization: Bearer {{admin_token}}
Content-Type: application/json
{
"iccids": ["{{test_iccid_1}}", "{{test_iccid_2}}"],
"series_id": {{series_id}}
}
HTTP 200
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.success_count" >= 1
# ══════════════════════════════════════════════════════════════
# 第 13 步C 端测试登录ICCID_1归属一级代理
# 调用开发环境专用接口,传入 ICCID 自动绑定测试客户并签发 JWT
# ══════════════════════════════════════════════════════════════
POST {{base_url}}/api/c/v1/auth/dev-login
Content-Type: application/json
{
"identifier": "{{test_iccid_1}}"
}
HTTP 200
[Captures]
customer1_token: jsonpath "$.data.token"
customer1_id: jsonpath "$.data.customer_id"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.token" isString
jsonpath "$.data.customer_id" isInteger
# ══════════════════════════════════════════════════════════════
# 第 14 步:验证 ICCID_1 的可购套餐价格 = 一级代理零售价15000 分)
# retail_price 来自一级代理在 shop_package_allocation 中设置的零售价
# 前置条件package-resource-full-flow.hurl 中一级代理已设置零售价 15000
# ══════════════════════════════════════════════════════════════
GET {{base_url}}/api/c/v1/asset/packages?identifier={{test_iccid_1}}
Authorization: Bearer {{customer1_token}}
HTTP 200
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.packages" isList
jsonpath "$.data.packages" count >= 1
# 关键断言一级代理归属的卡C 端看到的是一级代理设置的零售价 15000
jsonpath "$.data.packages[0].retail_price" == 15000
jsonpath "$.data.packages[0].package_id" == {{package_id}}
# ══════════════════════════════════════════════════════════════
# 第 15 步C 端测试登录ICCID_2归属二级代理
# ══════════════════════════════════════════════════════════════
POST {{base_url}}/api/c/v1/auth/dev-login
Content-Type: application/json
{
"identifier": "{{test_iccid_2}}"
}
HTTP 200
[Captures]
customer2_token: jsonpath "$.data.token"
customer2_id: jsonpath "$.data.customer_id"
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.token" isString
jsonpath "$.data.customer_id" isInteger
# ══════════════════════════════════════════════════════════════
# 第 16 步:验证 ICCID_2 的可购套餐价格 = 二级代理零售价18000 分)
# ══════════════════════════════════════════════════════════════
GET {{base_url}}/api/c/v1/asset/packages?identifier={{test_iccid_2}}
Authorization: Bearer {{customer2_token}}
HTTP 200
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.packages" isList
jsonpath "$.data.packages" count >= 1
# 关键断言二级代理归属的卡C 端看到的是二级代理设置的零售价 18000与一级代理不同
jsonpath "$.data.packages[0].retail_price" == 18000
jsonpath "$.data.packages[0].package_id" == {{package_id}}
# ══════════════════════════════════════════════════════════════
# 第 17 步:用两个 token 访问对方的卡(越权验证)
# customer1 不应能查询 ICCID_2 的套餐(未绑定该资产)
# ══════════════════════════════════════════════════════════════
GET {{base_url}}/api/c/v1/asset/packages?identifier={{test_iccid_2}}
Authorization: Bearer {{customer1_token}}
HTTP 403
[Asserts]
jsonpath "$.code" == 1005
# ══════════════════════════════════════════════════════════════
# 第 18 步:验证同一套餐两个代理视角价格确实不同(一致性检查)
# ══════════════════════════════════════════════════════════════
GET {{base_url}}/api/c/v1/asset/info?identifier={{test_iccid_1}}
Authorization: Bearer {{customer1_token}}
HTTP 200
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.asset_type" == "card"
jsonpath "$.data.iccid" == "{{test_iccid_1}}"
GET {{base_url}}/api/c/v1/asset/info?identifier={{test_iccid_2}}
Authorization: Bearer {{customer2_token}}
HTTP 200
[Asserts]
jsonpath "$.code" == 0
jsonpath "$.data.asset_type" == "card"
jsonpath "$.data.iccid" == "{{test_iccid_2}}"

Binary file not shown.