Files
past-go/past-go/internal/wechat/subscribe_test.go
T
CodeBuddy 3ce6edc9a1 feat: 存储空间管理、虚拟支付接入与容量守卫
- 新增存储空间管理:会员套餐配置、用户容量/到期管理、后台支付记录查询
- 接入个人主体小程序虚拟支付:下单/发货推送/兜底查单(query_order)
- 修复 adminStorageUsers 等接口鉴权、out_trade_no 长度、前端 requestVirtualPayment 参数
- 前端上传前容量预检,超容引导升级无限空间
- 我的页会员卡片展示「无限空间」与已用容量/到期时间
- 后台「存储空间管理」新增支付记录标签页
2026-10-26 23:07:09 +08:00

143 lines
6.6 KiB
Go

package wechat
import (
"database/sql/driver"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"past/internal/store"
"past/internal/testdb"
"path/filepath"
"strings"
"testing"
)
type subscribeTransport func(*http.Request) (*http.Response, error)
func (f subscribeTransport) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
func TestSubscribeRecordsEveryOutcome(t *testing.T) {
for _, tt := range []struct {
name, openid, template, response, reason string
subscribed, success bool
code any
requests int
}{
{name: "success", openid: "recipient-openid", template: "template-id", subscribed: true, response: `{"errcode":0}`, success: true, code: int64(0), requests: 1},
{name: "quota exhausted", openid: "recipient-openid", template: "template-id", subscribed: true, response: `{"errcode":43101,"errmsg":"user refuse"}`, reason: "43101", code: int64(43101), requests: 1},
{name: "malformed response", openid: "recipient-openid", template: "template-id", subscribed: true, response: `not-json`, reason: "无效", requests: 1},
{name: "invalid errcode", openid: "recipient-openid", template: "template-id", subscribed: true, response: `{"errcode":"invalid"}`, reason: "无效", requests: 1},
{name: "missing errcode", openid: "recipient-openid", template: "template-id", subscribed: true, response: `{}`, reason: "errcode", requests: 1},
{name: "no authorization", openid: "recipient-openid", template: "template-id", reason: "未订阅"},
{name: "no openid", template: "template-id", reason: "openid"},
{name: "no template", openid: "recipient-openid", reason: "模板未配置"},
} {
t.Run(tt.name, func(t *testing.T) {
steps := []testdb.Step{{Match: "SELECT status FROM notify_log", Columns: []string{"status"}}, {Match: "SELECT openid FROM users", Args: []any{int64(2)}, Columns: []string{"openid"}, Rows: [][]driver.Value{{tt.openid}}}}
if tt.openid != "" && tt.template != "" {
auth := [][]driver.Value(nil)
if tt.subscribed {
auth = [][]driver.Value{{int64(1)}}
}
steps = append(steps, testdb.Step{Match: "FROM subscribe_templates", Args: []any{int64(2), "notify"}, Columns: []string{"id"}, Rows: auth})
}
meta := []testdb.Step{{Match: "SELECT couple_id FROM users", Columns: []string{"couple_id"}, Rows: [][]driver.Value{{int64(5)}}}, {Match: "SELECT action_type FROM activity_log", Columns: []string{"action_type"}, Rows: [][]driver.Value{{"diary_create"}}}}
steps = append(steps, meta...)
steps = append(steps, testdb.Step{Match: "INSERT INTO subscribe_message_logs", Check: func(q string, args []any) error {
if args[4] != tt.openid || args[5] != "diary_create" || args[6] != "new diary" {
t.Errorf("wrong recipient/event: %v", args)
return nil
}
if args[7] != strings.Repeat("爱", 20) {
t.Errorf("log must record normalized content: %v", args[7])
}
status := "failed"
if tt.success {
status = "success"
}
if args[11] != status || args[12] != tt.code || args[15] != int64(tt.requests) {
t.Errorf("wrong result: %v", args)
return nil
}
if !strings.Contains(fmt.Sprint(args[14]), tt.reason) {
t.Errorf("missing failure reason: %v", args[14])
return nil
}
if tt.success && args[17] == nil {
t.Error("missing sent_at")
return nil
}
if !tt.success && args[17] != nil {
t.Error("failed message has sent_at")
return nil
}
return nil
}})
steps = append(steps, meta...)
steps = append(steps, testdb.Step{Match: "SELECT COUNT(*) as cnt FROM notify_log", Columns: []string{"cnt"}, Rows: [][]driver.Value{{int64(0)}}}, testdb.Step{Match: "INSERT INTO notify_log"})
db, _ := testdb.Open(t, steps)
client := New("app", "secret", tt.template, t.TempDir(), &store.DB{DB: db})
os.MkdirAll(filepath.Dir(client.cacheFile()), 0755)
os.WriteFile(client.cacheFile(), []byte("cached-token"), 0600)
calls := 0
client.http.Transport = subscribeTransport(func(r *http.Request) (*http.Response, error) {
calls++
var payload map[string]any
json.NewDecoder(r.Body).Decode(&payload)
if payload["touser"] != tt.openid {
t.Fatal("wrong WeChat recipient")
}
if payload["data"].(map[string]any)["thing2"].(map[string]any)["value"] != strings.Repeat("爱", 20) {
t.Fatal("overlong content sent to WeChat")
}
return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(tt.response))}, nil
})
success := client.SendSubscribeMessage(2, "notify", map[string]string{"thing1": "new diary", "thing2": strings.Repeat("爱", 50)}, "", 10, 1)
if success != tt.success || calls != tt.requests {
t.Fatalf("success=%v requests=%d", success, calls)
}
})
}
}
func TestSubscribeWithoutActivityStillRecords(t *testing.T) {
db, _ := testdb.Open(t, []testdb.Step{
{Match: "SELECT openid FROM users", Columns: []string{"openid"}, Rows: [][]driver.Value{{"recipient"}}},
{Match: "SELECT id FROM subscribe_templates", Columns: []string{"id"}},
{Match: "SELECT couple_id FROM users", Columns: []string{"couple_id"}, Rows: [][]driver.Value{{nil}}},
{Match: "INSERT INTO subscribe_message_logs", Check: func(_ string, args []any) error {
if args[0] != int64(0) || args[4] != "recipient" || args[11] != "failed" {
t.Error("missing zero-activity log")
return nil
}
return nil
}},
})
client := New("app", "secret", "template", t.TempDir(), &store.DB{DB: db})
if client.SendSubscribeMessage(2, "notify", nil, "", 0, 0) {
t.Fatal("unsubscribed send succeeded")
}
}
func TestSuccessfulEventIsNotSentAgain(t *testing.T) {
db, _ := testdb.Open(t, []testdb.Step{{Match: "SELECT status FROM notify_log", Columns: []string{"status"}, Rows: [][]driver.Value{{"success"}}}})
client := New("app", "secret", "template", t.TempDir(), &store.DB{DB: db})
client.http.Transport = subscribeTransport(func(*http.Request) (*http.Response, error) { t.Fatal("duplicate sent to WeChat"); return nil, nil })
if !client.SendSubscribeMessage(2, "notify", nil, "", 10, 1) {
t.Fatal("completed message must be treated as success")
}
}
func TestThingNormalizationDoesNotMutateInput(t *testing.T) {
input := map[string]string{"thing1": strings.Repeat("爱💕", 30), "thing2": " ", "thing4": "行一\n行二", "date3": "2026-10-11"}
output := normalizeSubscribeData(input)
if len([]rune(output["thing1"])) != 20 || output["thing2"] == "" || output["thing4"] != "行一 行二" || output["date3"] != input["date3"] {
t.Fatalf("wrong normalized fields: %v", output)
}
if output["thing1"] == input["thing1"] || input["thing2"] != " " {
t.Fatal("input was mutated")
}
}