Files
affiliate_dash/backend/internal/pkg/lewan/signer_test.go
T
yml2213andCursor f7f48d0feb 功能:接入乐玩开放平台双渠道路由,并修复渠道锁定与 Webhook 可靠性
在保持 DLC 默认零破坏的前提下,支持 priority/仅乐玩/仅 DLC 履约策略与后台 GUI 配置;同步补齐 Prepare 渠道落库、乐玩订单取消、Webhook 验签与失败重试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-11 15:48:03 +08:00

125 lines
5.1 KiB
Go

package lewan
import (
"testing"
"time"
)
func TestOfficialVectors(t *testing.T) {
// 固定密钥与时间戳
accessKey := "ak_test_fixed_20260905"
secretKey := "sk_test_fixed_20260905"
timestamp := "1788566400"
_ = accessKey
// Vector 1: GET 查询
t.Run("Vector1_GET_Query", func(t *testing.T) {
method := "GET"
path := "/openapi/v1/products"
rawQuery := "pageSize=20&page=1"
nonce := "nonce_get_20260905_fixed"
bodySHA256 := EmptyBodySHA256
expectedSig := "54ea478585e61230be670d664745c81ffb4907b87c8592bf082c9cc894232adf"
signingStr := BuildSigningString(method, path, rawQuery, bodySHA256, timestamp, nonce)
expectedSigningStr := "GET\n/openapi/v1/products\npage=1&pageSize=20\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n1788566400\nnonce_get_20260905_fixed"
if signingStr != expectedSigningStr {
t.Fatalf("signing string mismatch:\ngot:\n%s\nwant:\n%s", signingStr, expectedSigningStr)
}
sig := SignRequest(secretKey, signingStr)
if sig != expectedSig {
t.Fatalf("signature mismatch:\ngot: %s\nwant: %s", sig, expectedSig)
}
})
// Vector 2: POST JSON
t.Run("Vector2_POST_JSON", func(t *testing.T) {
method := "POST"
path := "/openapi/v1/orders"
rawQuery := ""
body := []byte(`{"callerOrderId":"DOC-20260905-001","roleId":"1148711973","items":[{"productId":"00000000-0000-4000-8000-000000000101","quantity":2}]}`)
nonce := "nonce_post_20260905_fixed"
expectedBodySHA := "146e687459b70c7fdf2781f72593306a8041aa0d5212207459dc71cd76e59032"
expectedSig := "1e1d3df46b885e76b1e992585215e488c29315e7207e62df4aa26b0124d04968"
bodySHA := SHA256Hex(body)
if bodySHA != expectedBodySHA {
t.Fatalf("body SHA256 mismatch:\ngot: %s\nwant: %s", bodySHA, expectedBodySHA)
}
signingStr := BuildSigningString(method, path, rawQuery, bodySHA, timestamp, nonce)
expectedSigningStr := "POST\n/openapi/v1/orders\n\n146e687459b70c7fdf2781f72593306a8041aa0d5212207459dc71cd76e59032\n1788566400\nnonce_post_20260905_fixed"
if signingStr != expectedSigningStr {
t.Fatalf("signing string mismatch:\ngot:\n%s\nwant:\n%s", signingStr, expectedSigningStr)
}
sig := SignRequest(secretKey, signingStr)
if sig != expectedSig {
t.Fatalf("signature mismatch:\ngot: %s\nwant: %s", sig, expectedSig)
}
})
// Vector 3: 重复查询参数
t.Run("Vector3_Duplicate_Query", func(t *testing.T) {
method := "GET"
path := "/openapi/v1/orders"
rawQuery := "status=SUCCEEDED&keyword=%E4%BA%91%20%E8%A7%A6%E8%A7%92&status=FAILED"
nonce := "nonce_repeat_20260905_fixed"
expectedSig := "c1c2be18372e3ac89e8042f3477fff8287126e31777a18a593c0cce7028626d8"
normQuery := NormalizeQuery(rawQuery)
expectedNormQuery := "keyword=%E4%BA%91%20%E8%A7%A6%E8%A7%92&status=FAILED&status=SUCCEEDED"
if normQuery != expectedNormQuery {
t.Fatalf("normalized query mismatch:\ngot: %s\nwant: %s", normQuery, expectedNormQuery)
}
signingStr := BuildSigningString(method, path, rawQuery, EmptyBodySHA256, timestamp, nonce)
sig := SignRequest(secretKey, signingStr)
if sig != expectedSig {
t.Fatalf("signature mismatch:\ngot: %s\nwant: %s", sig, expectedSig)
}
})
// Vector 4: POST 空请求体
t.Run("Vector4_POST_Empty_Body", func(t *testing.T) {
method := "POST"
path := "/openapi/v1/orders/00000000-0000-4000-8000-000000000201/account/release"
rawQuery := ""
nonce := "nonce_empty_20260905_fixed"
expectedSig := "167f6d7c116fc7e51b0d2418e65a0705f08076940025f2c013adbd8f27648908"
signingStr := BuildSigningString(method, path, rawQuery, EmptyBodySHA256, timestamp, nonce)
sig := SignRequest(secretKey, signingStr)
if sig != expectedSig {
t.Fatalf("signature mismatch:\ngot: %s\nwant: %s", sig, expectedSig)
}
})
// Vector 5: Webhook 固定向量
t.Run("Vector5_Webhook", func(t *testing.T) {
webhookSecret := "whsec_test_fixed_20260905"
eventID := "00000000-0000-4000-8000-000000000302"
rawBody := []byte(`{"applicationId":"00000000-0000-4000-8000-000000000301","data":{"callerOrderId":"DOC-20260905-001","failedQuantity":1,"failureCodes":["SUPPLIER_REJECTED"],"orderId":"00000000-0000-4000-8000-000000000303","pendingQuantity":0,"releasedQuantity":0,"status":"PARTIAL_SUCCESS","succeededQuantity":1},"eventId":"00000000-0000-4000-8000-000000000302","occurredAt":"2026-09-05T00:00:00.000Z","orderVersion":7,"schemaVersion":"1.0","type":"ORDER_COMPLETED"}`)
expectedSig := "v1=ee3968ef467a4f9c19c0a33e7af1bb3118fab8d02c5ebf9cbd8001cc34ebe6be"
sig := SignWebhook(webhookSecret, timestamp, eventID, rawBody)
if sig != expectedSig {
t.Fatalf("webhook signature mismatch:\ngot: %s\nwant: %s", sig, expectedSig)
}
// 测试验证函数(不限时限)
if !VerifyWebhook(webhookSecret, sig, timestamp, eventID, rawBody, 0) {
t.Fatalf("VerifyWebhook should succeed")
}
// 测试错误 secret
if VerifyWebhook("wrong_secret", sig, timestamp, eventID, rawBody, 0) {
t.Fatalf("VerifyWebhook should fail with wrong secret")
}
// 测试时间窗口超限
if VerifyWebhook(webhookSecret, sig, timestamp, eventID, rawBody, 5*time.Minute) {
t.Fatalf("VerifyWebhook should fail for expired timestamp")
}
})
}