移除所有测试代码和测试要求
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m33s

**变更说明**:
- 删除所有 *_test.go 文件(单元测试、集成测试、验收测试、流程测试)
- 删除整个 tests/ 目录
- 更新 CLAUDE.md:用"测试禁令"章节替换所有测试要求
- 删除测试生成 Skill (openspec-generate-acceptance-tests)
- 删除测试生成命令 (opsx:gen-tests)
- 更新 tasks.md:删除所有测试相关任务

**新规范**:
-  禁止编写任何形式的自动化测试
-  禁止创建 *_test.go 文件
-  禁止在任务中包含测试相关工作
-  仅当用户明确要求时才编写测试

**原因**:
业务系统的正确性通过人工验证和生产环境监控保证,测试代码维护成本高于价值。

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 17:13:42 +08:00
parent 804145332b
commit 353621d923
218 changed files with 11787 additions and 41983 deletions

View File

@@ -1,323 +0,0 @@
package gateway
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func TestNewClient(t *testing.T) {
client := NewClient("https://test.example.com", "testAppID", "testSecret")
if client.baseURL != "https://test.example.com" {
t.Errorf("baseURL = %s, want https://test.example.com", client.baseURL)
}
if client.appID != "testAppID" {
t.Errorf("appID = %s, want testAppID", client.appID)
}
if client.appSecret != "testSecret" {
t.Errorf("appSecret = %s, want testSecret", client.appSecret)
}
if client.timeout != 30*time.Second {
t.Errorf("timeout = %v, want 30s", client.timeout)
}
if client.httpClient == nil {
t.Error("httpClient should not be nil")
}
}
func TestWithTimeout(t *testing.T) {
client := NewClient("https://test.example.com", "testAppID", "testSecret").
WithTimeout(60 * time.Second)
if client.timeout != 60*time.Second {
t.Errorf("timeout = %v, want 60s", client.timeout)
}
}
func TestWithTimeout_Chain(t *testing.T) {
// 验证链式调用返回同一个 Client 实例
client := NewClient("https://test.example.com", "testAppID", "testSecret")
returned := client.WithTimeout(45 * time.Second)
if returned != client {
t.Error("WithTimeout should return the same Client instance for chaining")
}
}
func TestDoRequest_Success(t *testing.T) {
// 创建 mock HTTP 服务器
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 验证请求方法
if r.Method != http.MethodPost {
t.Errorf("Method = %s, want POST", r.Method)
}
// 验证 Content-Type
if r.Header.Get("Content-Type") != "application/json;charset=utf-8" {
t.Errorf("Content-Type = %s, want application/json;charset=utf-8", r.Header.Get("Content-Type"))
}
// 验证请求体格式
var reqBody map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
t.Fatalf("解析请求体失败: %v", err)
}
// 验证必需字段
if _, ok := reqBody["appId"]; !ok {
t.Error("请求体缺少 appId 字段")
}
if _, ok := reqBody["data"]; !ok {
t.Error("请求体缺少 data 字段")
}
if _, ok := reqBody["sign"]; !ok {
t.Error("请求体缺少 sign 字段")
}
if _, ok := reqBody["timestamp"]; !ok {
t.Error("请求体缺少 timestamp 字段")
}
// 返回 mock 响应
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{"test":"data"}`),
TraceID: "test-trace-id",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
businessData := map[string]interface{}{
"params": map[string]string{
"cardNo": "898608070422D0010269",
},
}
data, err := client.doRequest(ctx, "/test", businessData)
if err != nil {
t.Fatalf("doRequest() error = %v", err)
}
if string(data) != `{"test":"data"}` {
t.Errorf("data = %s, want {\"test\":\"data\"}", string(data))
}
}
func TestDoRequest_BusinessError(t *testing.T) {
// 创建返回业务错误的 mock 服务器
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 500,
Msg: "业务处理失败",
Data: nil,
TraceID: "error-trace-id",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
_, err := client.doRequest(ctx, "/test", map[string]interface{}{})
if err == nil {
t.Fatal("doRequest() expected business error")
}
// 验证错误信息包含业务错误内容
if !strings.Contains(err.Error(), "业务错误") {
t.Errorf("error should contain '业务错误', got: %v", err)
}
}
func TestDoRequest_Timeout(t *testing.T) {
// 创建延迟响应的服务器
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(500 * time.Millisecond) // 延迟 500ms
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret").
WithTimeout(100 * time.Millisecond) // 设置 100ms 超时
ctx := context.Background()
_, err := client.doRequest(ctx, "/test", map[string]interface{}{})
if err == nil {
t.Fatal("doRequest() expected timeout error")
}
// 验证是超时错误
if !strings.Contains(err.Error(), "超时") {
t.Errorf("error should contain '超时', got: %v", err)
}
}
func TestDoRequest_HTTPStatusError(t *testing.T) {
// 创建返回 500 状态码的服务器
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal Server Error"))
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
_, err := client.doRequest(ctx, "/test", map[string]interface{}{})
if err == nil {
t.Fatal("doRequest() expected HTTP status error")
}
// 验证错误信息包含 HTTP 状态码
if !strings.Contains(err.Error(), "500") {
t.Errorf("error should contain '500', got: %v", err)
}
}
func TestDoRequest_InvalidResponse(t *testing.T) {
// 创建返回无效 JSON 的服务器
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte("invalid json"))
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
_, err := client.doRequest(ctx, "/test", map[string]interface{}{})
if err == nil {
t.Fatal("doRequest() expected JSON parse error")
}
// 验证错误信息包含解析失败提示
if !strings.Contains(err.Error(), "解析") {
t.Errorf("error should contain '解析', got: %v", err)
}
}
func TestDoRequest_ContextCanceled(t *testing.T) {
// 创建正常响应的服务器(但会延迟)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(500 * time.Millisecond)
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
// 创建已取消的 context
ctx, cancel := context.WithCancel(context.Background())
cancel() // 立即取消
_, err := client.doRequest(ctx, "/test", map[string]interface{}{})
if err == nil {
t.Fatal("doRequest() expected context canceled error")
}
}
func TestDoRequest_NetworkError(t *testing.T) {
// 使用无效的服务器地址
client := NewClient("http://127.0.0.1:1", "testAppID", "testSecret").
WithTimeout(1 * time.Second)
ctx := context.Background()
_, err := client.doRequest(ctx, "/test", map[string]interface{}{})
if err == nil {
t.Fatal("doRequest() expected network error")
}
}
func TestDoRequest_EmptyBusinessData(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{}`),
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
data, err := client.doRequest(ctx, "/test", map[string]interface{}{})
if err != nil {
t.Fatalf("doRequest() error = %v", err)
}
if string(data) != `{}` {
t.Errorf("data = %s, want {}", string(data))
}
}
func TestIntegration_QueryCardStatus(t *testing.T) {
if testing.Short() {
t.Skip("跳过集成测试")
}
baseURL := "https://lplan.whjhft.com/openapi"
appID := "60bgt1X8i7AvXqkd"
appSecret := "BZeQttaZQt0i73moF"
client := NewClient(baseURL, appID, appSecret).WithTimeout(30 * time.Second)
ctx := context.Background()
resp, err := client.QueryCardStatus(ctx, &CardStatusReq{
CardNo: "898608070422D0010269",
})
if err != nil {
t.Fatalf("QueryCardStatus() error = %v", err)
}
if resp.ICCID == "" {
t.Error("ICCID should not be empty")
}
if resp.CardStatus == "" {
t.Error("CardStatus should not be empty")
}
t.Logf("Integration test passed: ICCID=%s, Status=%s", resp.ICCID, resp.CardStatus)
}
func TestIntegration_QueryFlow(t *testing.T) {
if testing.Short() {
t.Skip("跳过集成测试")
}
baseURL := "https://lplan.whjhft.com/openapi"
appID := "60bgt1X8i7AvXqkd"
appSecret := "BZeQttaZQt0i73moF"
client := NewClient(baseURL, appID, appSecret).WithTimeout(30 * time.Second)
ctx := context.Background()
resp, err := client.QueryFlow(ctx, &FlowQueryReq{
CardNo: "898608070422D0010269",
})
if err != nil {
t.Fatalf("QueryFlow() error = %v", err)
}
if resp.UsedFlow < 0 {
t.Error("UsedFlow should not be negative")
}
t.Logf("Integration test passed: UsedFlow=%d %s", resp.UsedFlow, resp.Unit)
}

View File

@@ -1,103 +0,0 @@
package gateway
import (
"crypto/aes"
"encoding/base64"
"strings"
"testing"
)
func TestAESEncrypt(t *testing.T) {
tests := []struct {
name string
data []byte
appSecret string
wantErr bool
}{
{
name: "正常加密",
data: []byte(`{"params":{"cardNo":"898608070422D0010269"}}`),
appSecret: "BZeQttaZQt0i73moF",
wantErr: false,
},
{
name: "空数据加密",
data: []byte(""),
appSecret: "BZeQttaZQt0i73moF",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
encrypted, err := aesEncrypt(tt.data, tt.appSecret)
if (err != nil) != tt.wantErr {
t.Errorf("aesEncrypt() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && encrypted == "" {
t.Error("aesEncrypt() 返回空字符串")
}
// 验证 Base64 格式
if !tt.wantErr {
_, err := base64.StdEncoding.DecodeString(encrypted)
if err != nil {
t.Errorf("aesEncrypt() 返回的不是有效的 Base64: %v", err)
}
}
})
}
}
func TestGenerateSign(t *testing.T) {
appID := "60bgt1X8i7AvXqkd"
encryptedData := "test_encrypted_data"
timestamp := int64(1704067200)
appSecret := "BZeQttaZQt0i73moF"
sign := generateSign(appID, encryptedData, timestamp, appSecret)
// 验证签名格式32 位大写十六进制)
if len(sign) != 32 {
t.Errorf("签名长度错误: got %d, want 32", len(sign))
}
if sign != strings.ToUpper(sign) {
t.Error("签名应为大写")
}
// 验证签名可重现
sign2 := generateSign(appID, encryptedData, timestamp, appSecret)
if sign != sign2 {
t.Error("相同参数应生成相同签名")
}
}
func TestNewECBEncrypterPanic(t *testing.T) {
defer func() {
if recover() == nil {
t.Fatal("newECBEncrypter 期望触发 panic但未触发")
}
}()
newECBEncrypter(nil)
}
func TestECBEncrypterCryptBlocksPanic(t *testing.T) {
block, err := aes.NewCipher(make([]byte, aesBlockSize))
if err != nil {
t.Fatalf("创建 AES cipher 失败: %v", err)
}
encrypter := newECBEncrypter(block)
defer func() {
if recover() == nil {
t.Fatal("CryptBlocks 期望触发 panic但未触发")
}
}()
// 传入非完整块长度,触发 panic
src := []byte("short")
dst := make([]byte, len(src))
encrypter.CryptBlocks(dst, src)
}

View File

@@ -1,404 +0,0 @@
package gateway
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestGetDeviceInfo_ByCardNo_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{"imei":"123456789012345","onlineStatus":1,"signalLevel":25,"wifiSsid":"TestWiFi","wifiEnabled":1,"uploadSpeed":100,"downloadSpeed":500}`),
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
result, err := client.GetDeviceInfo(ctx, &DeviceInfoReq{
CardNo: "898608070422D0010269",
})
if err != nil {
t.Fatalf("GetDeviceInfo() error = %v", err)
}
if result.IMEI != "123456789012345" {
t.Errorf("IMEI = %s, want 123456789012345", result.IMEI)
}
if result.OnlineStatus != 1 {
t.Errorf("OnlineStatus = %d, want 1", result.OnlineStatus)
}
if result.SignalLevel != 25 {
t.Errorf("SignalLevel = %d, want 25", result.SignalLevel)
}
if result.WiFiSSID != "TestWiFi" {
t.Errorf("WiFiSSID = %s, want TestWiFi", result.WiFiSSID)
}
}
func TestGetDeviceInfo_ByDeviceID_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{"imei":"123456789012345","onlineStatus":0,"signalLevel":0}`),
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
result, err := client.GetDeviceInfo(ctx, &DeviceInfoReq{
DeviceID: "123456789012345",
})
if err != nil {
t.Fatalf("GetDeviceInfo() error = %v", err)
}
if result.IMEI != "123456789012345" {
t.Errorf("IMEI = %s, want 123456789012345", result.IMEI)
}
if result.OnlineStatus != 0 {
t.Errorf("OnlineStatus = %d, want 0", result.OnlineStatus)
}
}
func TestGetDeviceInfo_MissingParams(t *testing.T) {
client := NewClient("https://test.example.com", "testAppID", "testSecret")
ctx := context.Background()
_, err := client.GetDeviceInfo(ctx, &DeviceInfoReq{})
if err == nil {
t.Fatal("GetDeviceInfo() expected validation error")
}
if !strings.Contains(err.Error(), "至少需要一个") {
t.Errorf("error should contain '至少需要一个', got: %v", err)
}
}
func TestGetDeviceInfo_InvalidResponse(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`invalid json`),
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
_, err := client.GetDeviceInfo(ctx, &DeviceInfoReq{CardNo: "test"})
if err == nil {
t.Fatal("GetDeviceInfo() expected JSON parse error")
}
if !strings.Contains(err.Error(), "解析") {
t.Errorf("error should contain '解析', got: %v", err)
}
}
func TestGetSlotInfo_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{"imei":"123456789012345","slots":[{"slotNo":1,"iccid":"898608070422D0010269","cardStatus":"正常","isActive":1},{"slotNo":2,"iccid":"898608070422D0010270","cardStatus":"停机","isActive":0}]}`),
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
result, err := client.GetSlotInfo(ctx, &DeviceInfoReq{
DeviceID: "123456789012345",
})
if err != nil {
t.Fatalf("GetSlotInfo() error = %v", err)
}
if result.IMEI != "123456789012345" {
t.Errorf("IMEI = %s, want 123456789012345", result.IMEI)
}
if len(result.Slots) != 2 {
t.Errorf("len(Slots) = %d, want 2", len(result.Slots))
}
if result.Slots[0].SlotNo != 1 {
t.Errorf("Slots[0].SlotNo = %d, want 1", result.Slots[0].SlotNo)
}
if result.Slots[0].ICCID != "898608070422D0010269" {
t.Errorf("Slots[0].ICCID = %s, want 898608070422D0010269", result.Slots[0].ICCID)
}
if result.Slots[0].IsActive != 1 {
t.Errorf("Slots[0].IsActive = %d, want 1", result.Slots[0].IsActive)
}
}
func TestGetSlotInfo_MissingParams(t *testing.T) {
client := NewClient("https://test.example.com", "testAppID", "testSecret")
ctx := context.Background()
_, err := client.GetSlotInfo(ctx, &DeviceInfoReq{})
if err == nil {
t.Fatal("GetSlotInfo() expected validation error")
}
if !strings.Contains(err.Error(), "至少需要一个") {
t.Errorf("error should contain '至少需要一个', got: %v", err)
}
}
func TestSetSpeedLimit_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.SetSpeedLimit(ctx, &SpeedLimitReq{
DeviceID: "123456789012345",
UploadSpeed: 100,
DownloadSpeed: 500,
})
if err != nil {
t.Fatalf("SetSpeedLimit() error = %v", err)
}
}
func TestSetSpeedLimit_WithExtend(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.SetSpeedLimit(ctx, &SpeedLimitReq{
DeviceID: "123456789012345",
UploadSpeed: 100,
DownloadSpeed: 500,
Extend: "test-extend",
})
if err != nil {
t.Fatalf("SetSpeedLimit() error = %v", err)
}
}
func TestSetSpeedLimit_BusinessError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 500, Msg: "设置失败"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.SetSpeedLimit(ctx, &SpeedLimitReq{
DeviceID: "123456789012345",
UploadSpeed: 100,
DownloadSpeed: 500,
})
if err == nil {
t.Fatal("SetSpeedLimit() expected business error")
}
if !strings.Contains(err.Error(), "业务错误") {
t.Errorf("error should contain '业务错误', got: %v", err)
}
}
func TestSetWiFi_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.SetWiFi(ctx, &WiFiReq{
DeviceID: "123456789012345",
SSID: "TestWiFi",
Password: "password123",
Enabled: 1,
})
if err != nil {
t.Fatalf("SetWiFi() error = %v", err)
}
}
func TestSetWiFi_WithExtend(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.SetWiFi(ctx, &WiFiReq{
DeviceID: "123456789012345",
SSID: "TestWiFi",
Password: "password123",
Enabled: 0,
Extend: "test-extend",
})
if err != nil {
t.Fatalf("SetWiFi() error = %v", err)
}
}
func TestSwitchCard_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.SwitchCard(ctx, &SwitchCardReq{
DeviceID: "123456789012345",
TargetICCID: "898608070422D0010270",
})
if err != nil {
t.Fatalf("SwitchCard() error = %v", err)
}
}
func TestSwitchCard_BusinessError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 404, Msg: "目标卡不存在"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.SwitchCard(ctx, &SwitchCardReq{
DeviceID: "123456789012345",
TargetICCID: "invalid",
})
if err == nil {
t.Fatal("SwitchCard() expected business error")
}
}
func TestResetDevice_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.ResetDevice(ctx, &DeviceOperationReq{
DeviceID: "123456789012345",
})
if err != nil {
t.Fatalf("ResetDevice() error = %v", err)
}
}
func TestResetDevice_WithExtend(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.ResetDevice(ctx, &DeviceOperationReq{
DeviceID: "123456789012345",
Extend: "test-extend",
})
if err != nil {
t.Fatalf("ResetDevice() error = %v", err)
}
}
func TestRebootDevice_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.RebootDevice(ctx, &DeviceOperationReq{
DeviceID: "123456789012345",
})
if err != nil {
t.Fatalf("RebootDevice() error = %v", err)
}
}
func TestRebootDevice_BusinessError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 500, Msg: "设备离线"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.RebootDevice(ctx, &DeviceOperationReq{
DeviceID: "123456789012345",
})
if err == nil {
t.Fatal("RebootDevice() expected business error")
}
if !strings.Contains(err.Error(), "业务错误") {
t.Errorf("error should contain '业务错误', got: %v", err)
}
}

View File

@@ -1,292 +0,0 @@
package gateway
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestQueryCardStatus_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{"iccid":"898608070422D0010269","cardStatus":"正常"}`),
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
result, err := client.QueryCardStatus(ctx, &CardStatusReq{
CardNo: "898608070422D0010269",
})
if err != nil {
t.Fatalf("QueryCardStatus() error = %v", err)
}
if result.ICCID != "898608070422D0010269" {
t.Errorf("ICCID = %s, want 898608070422D0010269", result.ICCID)
}
if result.CardStatus != "正常" {
t.Errorf("CardStatus = %s, want 正常", result.CardStatus)
}
}
func TestQueryCardStatus_InvalidResponse(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`invalid json`),
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
_, err := client.QueryCardStatus(ctx, &CardStatusReq{CardNo: "test"})
if err == nil {
t.Fatal("QueryCardStatus() expected JSON parse error")
}
if !strings.Contains(err.Error(), "解析") {
t.Errorf("error should contain '解析', got: %v", err)
}
}
func TestQueryFlow_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{"usedFlow":1024,"unit":"MB"}`),
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
result, err := client.QueryFlow(ctx, &FlowQueryReq{
CardNo: "898608070422D0010269",
})
if err != nil {
t.Fatalf("QueryFlow() error = %v", err)
}
if result.UsedFlow != 1024 {
t.Errorf("UsedFlow = %d, want 1024", result.UsedFlow)
}
if result.Unit != "MB" {
t.Errorf("Unit = %s, want MB", result.Unit)
}
}
func TestQueryFlow_BusinessError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 404,
Msg: "卡号不存在",
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
_, err := client.QueryFlow(ctx, &FlowQueryReq{CardNo: "invalid"})
if err == nil {
t.Fatal("QueryFlow() expected business error")
}
if !strings.Contains(err.Error(), "业务错误") {
t.Errorf("error should contain '业务错误', got: %v", err)
}
}
func TestQueryRealnameStatus_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{"status":"已实名"}`),
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
result, err := client.QueryRealnameStatus(ctx, &CardStatusReq{
CardNo: "898608070422D0010269",
})
if err != nil {
t.Fatalf("QueryRealnameStatus() error = %v", err)
}
if result.Status != "已实名" {
t.Errorf("Status = %s, want 已实名", result.Status)
}
}
func TestStopCard_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.StopCard(ctx, &CardOperationReq{
CardNo: "898608070422D0010269",
})
if err != nil {
t.Fatalf("StopCard() error = %v", err)
}
}
func TestStopCard_WithExtend(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.StopCard(ctx, &CardOperationReq{
CardNo: "898608070422D0010269",
Extend: "test-extend",
})
if err != nil {
t.Fatalf("StopCard() error = %v", err)
}
}
func TestStartCard_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 200, Msg: "成功"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.StartCard(ctx, &CardOperationReq{
CardNo: "898608070422D0010269",
})
if err != nil {
t.Fatalf("StartCard() error = %v", err)
}
}
func TestStartCard_BusinessError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{Code: 500, Msg: "操作失败"}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
err := client.StartCard(ctx, &CardOperationReq{CardNo: "test"})
if err == nil {
t.Fatal("StartCard() expected business error")
}
}
func TestGetRealnameLink_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{"link":"https://realname.example.com/verify?token=abc123"}`),
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
result, err := client.GetRealnameLink(ctx, &CardStatusReq{
CardNo: "898608070422D0010269",
})
if err != nil {
t.Fatalf("GetRealnameLink() error = %v", err)
}
if result.Link != "https://realname.example.com/verify?token=abc123" {
t.Errorf("Link = %s, want https://realname.example.com/verify?token=abc123", result.Link)
}
}
func TestGetRealnameLink_InvalidResponse(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := GatewayResponse{
Code: 200,
Msg: "成功",
Data: json.RawMessage(`{"invalid": "structure"}`),
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewClient(server.URL, "testAppID", "testSecret")
ctx := context.Background()
result, err := client.GetRealnameLink(ctx, &CardStatusReq{CardNo: "test"})
if err != nil {
t.Fatalf("GetRealnameLink() unexpected error = %v", err)
}
if result.Link != "" {
t.Errorf("Link = %s, want empty string", result.Link)
}
}
func TestBatchQuery_NotImplemented(t *testing.T) {
client := NewClient("https://test.example.com", "testAppID", "testSecret")
ctx := context.Background()
_, err := client.BatchQuery(ctx, &BatchQueryReq{
CardNos: []string{"test1", "test2"},
})
if err == nil {
t.Fatal("BatchQuery() expected not implemented error")
}
if !strings.Contains(err.Error(), "暂未实现") {
t.Errorf("error should contain '暂未实现', got: %v", err)
}
}