test: isolate postgres integration fixture

This commit is contained in:
youfak 2026-07-29 21:53:33 +08:00
parent 2341296271
commit 02097596fa
7 changed files with 89 additions and 8 deletions

View File

@ -8,10 +8,15 @@ Grafana 与 Kubernetes。配置已通过静态展开但当前仓库的 `cmd/p
```powershell
docker compose -f deploy/docker-compose.yml config --quiet
docker compose -f deploy/docker-compose.test.yml config --quiet
kubectl kustomize deploy/kubernetes/base | Out-Null
go run ./deploy/tools/configcheck deploy/config/local.yaml
```
`docker-compose.test.yml` 为 Redis 与 PostgreSQL 18 提供互相独立的本地集成测试
服务。两者只绑定回环地址Redis 禁止持久化PostgreSQL 使用
`/var/lib/postgresql` tmpfs。`test-redis.ps1` 通过专属 Compose 项目只启动 Redis
PostgreSQL Adapter 和对应执行脚本完成前,不把数据库契约记为通过。
运行时完成后,还必须通过 `production-readiness.md` 中的一致性、安全、恢复、
竞态与容量门禁,才能构建镜像并发布。

View File

@ -2,6 +2,8 @@ package deploy
import (
"os"
"slices"
"strings"
"testing"
"go.yaml.in/yaml/v4"
@ -13,11 +15,48 @@ type composeDocument struct {
}
type composeService struct {
Image string `yaml:"image"`
Command []string `yaml:"command"`
Ports []string `yaml:"ports"`
Volumes []string `yaml:"volumes"`
Tmpfs []string `yaml:"tmpfs"`
DependsOn any `yaml:"depends_on"`
}
func TestPostgresIntegrationFixtureIsIsolatedAndEphemeral(t *testing.T) {
document := loadComposeFile(t, "docker-compose.test.yml")
postgres, ok := document.Services["postgres"]
if !ok {
t.Fatal("docker-compose.test.yml has no postgres service")
}
if postgres.Image != "postgres:18-alpine" {
t.Fatalf("postgres image = %q, want postgres:18-alpine", postgres.Image)
}
if !slices.Equal(postgres.Ports, []string{"127.0.0.1:15432:5432"}) {
t.Fatalf("postgres ports = %v, want loopback test port", postgres.Ports)
}
if !slices.Contains(postgres.Tmpfs, "/var/lib/postgresql") || len(postgres.Volumes) != 0 {
t.Fatalf("postgres tmpfs = %v, volumes = %v; want ephemeral PG18 data root", postgres.Tmpfs, postgres.Volumes)
}
}
func TestRedisFixtureScriptUsesDedicatedComposeProject(t *testing.T) {
payload, err := os.ReadFile("../scripts/test-redis.ps1")
if err != nil {
t.Fatalf("read test-redis.ps1: %v", err)
}
script := string(payload)
for _, required := range []string{
`-p $composeProject`,
`up -d --wait --wait-timeout 60 redis`,
`down --volumes --remove-orphans`,
} {
if !strings.Contains(script, required) {
t.Errorf("test-redis.ps1 missing %q", required)
}
}
}
func TestLocalRedisIsExplicitlyEphemeral(t *testing.T) {
document := loadComposeDocument(t)
redis, ok := document.Services["redis"]
@ -55,13 +94,18 @@ func TestLocalGatewaysDoNotDependOnControlPlaneStorage(t *testing.T) {
func loadComposeDocument(t *testing.T) composeDocument {
t.Helper()
payload, err := os.ReadFile("docker-compose.yml")
return loadComposeFile(t, "docker-compose.yml")
}
func loadComposeFile(t *testing.T, name string) composeDocument {
t.Helper()
payload, err := os.ReadFile(name)
if err != nil {
t.Fatalf("read docker-compose.yml: %v", err)
t.Fatalf("read %s: %v", name, err)
}
var document composeDocument
if err := yaml.Unmarshal(payload, &document); err != nil {
t.Fatalf("parse docker-compose.yml: %v", err)
t.Fatalf("parse %s: %v", name, err)
}
return document
}

View File

@ -1,6 +1,23 @@
name: proxy-pool-test
services:
postgres:
image: postgres:18-alpine
environment:
POSTGRES_DB: proxy_pool_test
POSTGRES_USER: proxy_pool_test
POSTGRES_PASSWORD: proxy-pool-test
ports:
- "127.0.0.1:15432:5432"
tmpfs:
- /var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U proxy_pool_test -d proxy_pool_test"]
interval: 1s
timeout: 1s
retries: 30
start_period: 1s
redis:
image: redis:8.2-alpine
command: ["redis-server", "--appendonly", "no", "--save", ""]

View File

@ -95,8 +95,9 @@ fixture 的执行命令是:
PostgreSQL 管理面使用 `adminstate/contracttest` 作为 Memory/PostgreSQL 公用
行为契约。当前 MemoryStore 已覆盖配置提交、Upstream 幂等、100 并发 Routing
CAS、审计和租约 OutboxSchema 静态测试证明只声明六张管理表。只有 pgx
Adapter 在真实 PostgreSQL 18 上运行同一契约后,才标记生产持久化完成。
CAS、审计分页、Routing no-op、原子批量 ACK 和租约 OutboxSchema 静态测试
证明只声明六张管理表。PostgreSQL 18 的回环端口、tmpfs 隔离 fixture 已完成
静态验证;只有 pgx Adapter 在该实例上运行同一契约后,才标记生产持久化完成。
Admin 应用层测试覆盖 typed-nil 依赖、Actor/SourceIP 映射、Routing CAS 错误、
权威管理快照与低基数运行态聚合、未知字段拒绝、主配置/Secret 文件 I/O 分类、

View File

@ -173,3 +173,13 @@ Routing 自上而下匹配,首条命中停止;支持 Gateway 与 Extract 两
可执行的 `deploy/tools/configcheck`,并明确生产 Controller 入口仍是计划能力。
- 文档中的具体 `go run`/`go build` 目标现在必须存在;包含 `...` 的通配包命令
由 Go 工具链自身解析并在完整验证中执行。
## PostgreSQL 18 Fixture 审计2026-07-29
- 集成 Compose 已新增仅绑定 `127.0.0.1:15432``postgres:18-alpine` 服务,
数据目录挂载为 `/var/lib/postgresql` tmpfs不声明测试持久卷。
- Redis 测试脚本现使用专属 Compose 项目并只启动 Redis防止未来加入的
PostgreSQL fixture 被无关测试启动或清理。
- 生产 `docker-compose.yml` 仍把 PostgreSQL 18 命名卷挂在旧路径
`/var/lib/postgresql/data`;直接修改可能影响已有本地数据,必须配套迁移步骤后
单独处理,当前不能把生产持久化拓扑视为已验证。

View File

@ -2,6 +2,9 @@
## 2026-07-29
- 新增 PostgreSQL 18 隔离测试服务的静态契约:回环端口、正确 PG18 tmpfs 数据
根目录、零持久卷Redis 测试改用独立 Compose 项目和显式服务启动,避免两个
fixture 相互影响。未拉取或启动容器pgx Adapter/真实契约仍待依赖确认。
- 扩展 `adminstate/contracttest` 公用契约:审计完整字段和 AfterID 分页、Routing
no-op 审计且不写 Outbox、批量 ACK 零部分提交,以及六个 Store 方法的 Context
取消MemoryStore 全部通过,后续 PostgreSQL Adapter 必须运行同一套契约。

View File

@ -2,10 +2,11 @@ $ErrorActionPreference = "Stop"
$repositoryRoot = Split-Path -Parent $PSScriptRoot
$composeFile = Join-Path $repositoryRoot "deploy/docker-compose.test.yml"
$composeProject = "proxy-pool-redis-test"
$previousRedisURL = [Environment]::GetEnvironmentVariable("PROXY_POOL_TEST_REDIS_URL", "Process")
try {
docker compose -f $composeFile up -d --wait
docker compose -p $composeProject -f $composeFile up -d --wait --wait-timeout 60 redis
if ($LASTEXITCODE -ne 0) {
throw "starting Redis test fixture failed with exit code $LASTEXITCODE"
}
@ -29,5 +30,5 @@ finally {
else {
$env:PROXY_POOL_TEST_REDIS_URL = $previousRedisURL
}
docker compose -f $composeFile down --remove-orphans
docker compose -p $composeProject -f $composeFile down --volumes --remove-orphans
}