Files
junhong_cmp_fiber/pkg/idempotency/postgres_integration_test.go
break 17782d5f8e
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
实现七月迭代公共技术基础
2026-07-23 17:52:48 +09:00

87 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package idempotency_test
import (
"context"
"sync"
"testing"
"github.com/break/junhong_cmp_fiber/internal/testutil"
"github.com/break/junhong_cmp_fiber/pkg/idempotency"
)
func TestPostgresUniqueConstraintArbitratesConcurrentFirstSubmission(t *testing.T) {
db := testutil.NewPostgresTransaction(t)
if err := db.Exec(`
CREATE TEMP TABLE test_command_idempotency (
subject varchar(100) NOT NULL,
operation varchar(100) NOT NULL,
request_id varchar(100) NOT NULL,
fingerprint varchar(128) NOT NULL,
result_id bigint NOT NULL,
UNIQUE (subject, operation, request_id)
) ON COMMIT DROP`).Error; err != nil {
t.Fatalf("创建幂等契约测试表失败:%v", err)
}
fingerprint, err := idempotency.Fingerprint(map[string]any{"amount": 100})
if err != nil {
t.Fatalf("计算指纹失败:%v", err)
}
start := make(chan struct{})
results := make(chan int64, 2)
errs := make(chan error, 2)
var wait sync.WaitGroup
for _, resultID := range []int64{101, 102} {
wait.Add(1)
go func(id int64) {
defer wait.Done()
<-start
result := db.WithContext(context.Background()).Exec(`
INSERT INTO test_command_idempotency
(subject, operation, request_id, fingerprint, result_id)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT (subject, operation, request_id) DO NOTHING`,
"account:7", "agent-recharge.create", "request-1", fingerprint.Value, id,
)
results <- result.RowsAffected
errs <- result.Error
}(resultID)
}
close(start)
wait.Wait()
close(results)
close(errs)
var inserted int64
for resultErr := range errs {
if resultErr != nil {
t.Fatalf("并发写入失败:%v", resultErr)
}
}
for affected := range results {
inserted += affected
}
if inserted != 1 {
t.Fatalf("并发首写必须只有一个数据库事实,实际插入:%d", inserted)
}
var count int64
if err := db.Table("test_command_idempotency").Count(&count).Error; err != nil {
t.Fatalf("统计幂等事实失败:%v", err)
}
if count != 1 {
t.Fatalf("幂等事实数量错误:%d", count)
}
}
func TestIdempotencyScopeDoesNotDependOnRedis(t *testing.T) {
t.Parallel()
scope := idempotency.Scope{Subject: "account:7", Operation: "example.create"}
if !idempotency.ValidateScope(scope, "request-1") {
t.Fatal("完整作用域和请求 ID 应通过校验")
}
// 公共幂等契约没有 Redis 参数Redis 仅可由业务作为削峰优化,不能参与最终裁决。
}