Add repository integration probes, repository policy tests, the compose-based integration runner, and the matching usage documentation. Align the runner environment with both repository and middleware integration test expectations, and verify with fresh repository tests, integration-tag test runs, bash -n, and docker-compose config before commit.
243 lines
6.1 KiB
Go
243 lines
6.1 KiB
Go
//go:build integration
|
||
// +build integration
|
||
|
||
package repository
|
||
|
||
import (
|
||
"context"
|
||
"os"
|
||
"testing"
|
||
|
||
"github.com/jackc/pgx/v5/pgxpool"
|
||
)
|
||
|
||
// getPackageTestDB 获取测试数据库连接
|
||
func getPackageTestDB(t *testing.T) *pgxpool.Pool {
|
||
t.Helper()
|
||
|
||
host := os.Getenv("SUPPLY_API_DB_HOST")
|
||
if host == "" {
|
||
host = "/var/run/postgresql"
|
||
}
|
||
port := os.Getenv("SUPPLY_API_DB_PORT")
|
||
if port == "" {
|
||
port = "5432"
|
||
}
|
||
user := os.Getenv("SUPPLY_API_DB_USER")
|
||
if user == "" {
|
||
user = "long"
|
||
}
|
||
password := os.Getenv("SUPPLY_API_DB_PASSWORD")
|
||
dbName := os.Getenv("SUPPLY_API_DB_NAME")
|
||
if dbName == "" {
|
||
dbName = "supply_test"
|
||
}
|
||
|
||
// 构建 DSN - 如果 host 是路径(Unix socket),使用 host= 参数
|
||
var dsn string
|
||
if host[0] == '/' {
|
||
dsn = "postgres://" + user + ":" + password + "@/" + dbName + "?host=" + host + "&sslmode=disable"
|
||
} else {
|
||
dsn = "postgres://" + user + ":" + password + "@" + host + ":" + port + "/" + dbName + "?sslmode=disable"
|
||
}
|
||
|
||
pool, err := pgxpool.New(context.Background(), dsn)
|
||
if err != nil {
|
||
t.Skipf("跳过集成测试:无法连接数据库: %v", err)
|
||
return nil
|
||
}
|
||
|
||
if err := pool.Ping(context.Background()); err != nil {
|
||
pool.Close()
|
||
t.Skipf("跳过集成测试:无法 ping 数据库: %v", err)
|
||
return nil
|
||
}
|
||
|
||
t.Cleanup(func() {
|
||
pool.Close()
|
||
})
|
||
|
||
return pool
|
||
}
|
||
|
||
// TestPackageRepository_Create_Integration 集成测试:创建套餐
|
||
func TestPackageRepository_Create_Integration(t *testing.T) {
|
||
if testing.Short() {
|
||
t.Skip("跳过集成测试(short mode)")
|
||
}
|
||
|
||
pool := getPackageTestDB(t)
|
||
if pool == nil {
|
||
return
|
||
}
|
||
|
||
// 验证 supply_packages 表存在
|
||
var tableName string
|
||
err := pool.QueryRow(context.Background(), "SELECT table_name FROM information_schema.tables WHERE table_name = 'supply_packages'").Scan(&tableName)
|
||
if err != nil {
|
||
t.Skipf("跳过:supply_packages 表不存在: %v", err)
|
||
}
|
||
|
||
t.Log("集成测试:supply_packages 表存在")
|
||
}
|
||
|
||
// TestPackageRepository_GetByID_Integration 集成测试:获取套餐
|
||
func TestPackageRepository_GetByID_Integration(t *testing.T) {
|
||
if testing.Short() {
|
||
t.Skip("跳过集成测试(short mode)")
|
||
}
|
||
|
||
pool := getPackageTestDB(t)
|
||
if pool == nil {
|
||
return
|
||
}
|
||
|
||
var count int
|
||
err := pool.QueryRow(context.Background(), "SELECT COUNT(*) FROM supply_packages").Scan(&count)
|
||
if err != nil {
|
||
t.Logf("集成测试:supply_packages 查询结果: %v", err)
|
||
} else {
|
||
t.Logf("集成测试:supply_packages 共有 %d 条记录", count)
|
||
}
|
||
}
|
||
|
||
// TestPackageRepository_Update_Integration 集成测试:更新套餐(乐观锁)
|
||
func TestPackageRepository_Update_Integration(t *testing.T) {
|
||
if testing.Short() {
|
||
t.Skip("跳过集成测试(short mode)")
|
||
}
|
||
|
||
pool := getPackageTestDB(t)
|
||
if pool == nil {
|
||
return
|
||
}
|
||
|
||
// 验证 version 字段存在(乐观锁)
|
||
var columnExists bool
|
||
err := pool.QueryRow(context.Background(), `
|
||
SELECT EXISTS(
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_name = 'supply_packages' AND column_name = 'version'
|
||
)
|
||
`).Scan(&columnExists)
|
||
if err != nil || !columnExists {
|
||
t.Skip("跳过:supply_packages 表缺少 version 字段")
|
||
}
|
||
|
||
t.Log("集成测试:supply_packages 包含 version 字段(乐观锁)")
|
||
}
|
||
|
||
// TestPackageRepository_List_Integration 集成测试:列出套餐
|
||
func TestPackageRepository_List_Integration(t *testing.T) {
|
||
if testing.Short() {
|
||
t.Skip("跳过集成测试(short mode)")
|
||
}
|
||
|
||
pool := getPackageTestDB(t)
|
||
if pool == nil {
|
||
return
|
||
}
|
||
|
||
// 列出供应商标套餐
|
||
rows, err := pool.Query(context.Background(), `
|
||
SELECT id, supplier_id, available_quota
|
||
FROM supply_packages
|
||
LIMIT 10
|
||
`)
|
||
if err != nil {
|
||
t.Logf("集成测试:列出套餐: %v", err)
|
||
return
|
||
}
|
||
defer rows.Close()
|
||
|
||
count := 0
|
||
for rows.Next() {
|
||
var id, supplierID int64
|
||
var availableQuota int
|
||
rows.Scan(&id, &supplierID, &availableQuota)
|
||
count++
|
||
t.Logf("集成测试:套餐 ID=%d, SupplierID=%d, AvailableQuota=%d", id, supplierID, availableQuota)
|
||
}
|
||
|
||
t.Logf("集成测试:列出 %d 个套餐", count)
|
||
}
|
||
|
||
// TestPackageRepository_UpdateQuota_Integration 集成测试:扣减配额
|
||
func TestPackageRepository_UpdateQuota_Integration(t *testing.T) {
|
||
if testing.Short() {
|
||
t.Skip("跳过集成测试(short mode)")
|
||
}
|
||
|
||
pool := getPackageTestDB(t)
|
||
if pool == nil {
|
||
return
|
||
}
|
||
|
||
// 验证 available_quota 字段存在
|
||
var columnExists bool
|
||
err := pool.QueryRow(context.Background(), `
|
||
SELECT EXISTS(
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_name = 'supply_packages' AND column_name = 'available_quota'
|
||
)
|
||
`).Scan(&columnExists)
|
||
if err != nil || !columnExists {
|
||
t.Skip("跳过:supply_packages 表缺少 available_quota 字段")
|
||
}
|
||
|
||
t.Log("集成测试:supply_packages 包含 available_quota 字段")
|
||
}
|
||
|
||
// TestPackageRepository_GetForUpdate_Integration 集成测试:悲观锁获取
|
||
func TestPackageRepository_GetForUpdate_Integration(t *testing.T) {
|
||
if testing.Short() {
|
||
t.Skip("跳过集成测试(short mode)")
|
||
}
|
||
|
||
pool := getPackageTestDB(t)
|
||
if pool == nil {
|
||
return
|
||
}
|
||
|
||
// 测试 FOR UPDATE 锁
|
||
tx, err := pool.Begin(context.Background())
|
||
if err != nil {
|
||
t.Fatalf("开始事务失败: %v", err)
|
||
}
|
||
defer tx.Rollback(context.Background())
|
||
|
||
rows, err := tx.Query(context.Background(), "SELECT id FROM supply_packages LIMIT 1 FOR UPDATE")
|
||
if err != nil {
|
||
t.Logf("集成测试:FOR UPDATE 查询: %v", err)
|
||
} else {
|
||
rows.Close()
|
||
t.Log("集成测试:FOR UPDATE 锁获取成功")
|
||
}
|
||
|
||
tx.Rollback(context.Background())
|
||
}
|
||
|
||
// TestPackageRepository_OptimisticLock_Integration 集成测试:乐观锁冲突
|
||
func TestPackageRepository_OptimisticLock_Integration(t *testing.T) {
|
||
if testing.Short() {
|
||
t.Skip("跳过集成测试(short mode)")
|
||
}
|
||
|
||
pool := getPackageTestDB(t)
|
||
if pool == nil {
|
||
return
|
||
}
|
||
|
||
// 验证 version 字段存在
|
||
var versionCol int
|
||
err := pool.QueryRow(context.Background(), `
|
||
SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_name = 'supply_packages' AND column_name = 'version'
|
||
`).Scan(&versionCol)
|
||
if err != nil || versionCol == 0 {
|
||
t.Skip("跳过:supply_packages 表缺少 version 字段")
|
||
}
|
||
|
||
t.Log("集成测试:乐观锁字段验证通过")
|
||
}
|