test: isolate postgres integration fixture
This commit is contained in:
parent
2341296271
commit
02097596fa
@ -8,10 +8,15 @@ Grafana 与 Kubernetes。配置已通过静态展开,但当前仓库的 `cmd/p
|
|||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
docker compose -f deploy/docker-compose.yml config --quiet
|
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
|
kubectl kustomize deploy/kubernetes/base | Out-Null
|
||||||
go run ./deploy/tools/configcheck deploy/config/local.yaml
|
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` 中的一致性、安全、恢复、
|
运行时完成后,还必须通过 `production-readiness.md` 中的一致性、安全、恢复、
|
||||||
竞态与容量门禁,才能构建镜像并发布。
|
竞态与容量门禁,才能构建镜像并发布。
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,8 @@ package deploy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go.yaml.in/yaml/v4"
|
"go.yaml.in/yaml/v4"
|
||||||
@ -13,11 +15,48 @@ type composeDocument struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type composeService struct {
|
type composeService struct {
|
||||||
|
Image string `yaml:"image"`
|
||||||
Command []string `yaml:"command"`
|
Command []string `yaml:"command"`
|
||||||
|
Ports []string `yaml:"ports"`
|
||||||
Volumes []string `yaml:"volumes"`
|
Volumes []string `yaml:"volumes"`
|
||||||
|
Tmpfs []string `yaml:"tmpfs"`
|
||||||
DependsOn any `yaml:"depends_on"`
|
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) {
|
func TestLocalRedisIsExplicitlyEphemeral(t *testing.T) {
|
||||||
document := loadComposeDocument(t)
|
document := loadComposeDocument(t)
|
||||||
redis, ok := document.Services["redis"]
|
redis, ok := document.Services["redis"]
|
||||||
@ -55,13 +94,18 @@ func TestLocalGatewaysDoNotDependOnControlPlaneStorage(t *testing.T) {
|
|||||||
|
|
||||||
func loadComposeDocument(t *testing.T) composeDocument {
|
func loadComposeDocument(t *testing.T) composeDocument {
|
||||||
t.Helper()
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("read docker-compose.yml: %v", err)
|
t.Fatalf("read %s: %v", name, err)
|
||||||
}
|
}
|
||||||
var document composeDocument
|
var document composeDocument
|
||||||
if err := yaml.Unmarshal(payload, &document); err != nil {
|
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
|
return document
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,23 @@
|
|||||||
name: proxy-pool-test
|
name: proxy-pool-test
|
||||||
|
|
||||||
services:
|
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:
|
redis:
|
||||||
image: redis:8.2-alpine
|
image: redis:8.2-alpine
|
||||||
command: ["redis-server", "--appendonly", "no", "--save", ""]
|
command: ["redis-server", "--appendonly", "no", "--save", ""]
|
||||||
|
|||||||
@ -95,8 +95,9 @@ fixture 的执行命令是:
|
|||||||
|
|
||||||
PostgreSQL 管理面使用 `adminstate/contracttest` 作为 Memory/PostgreSQL 公用
|
PostgreSQL 管理面使用 `adminstate/contracttest` 作为 Memory/PostgreSQL 公用
|
||||||
行为契约。当前 MemoryStore 已覆盖配置提交、Upstream 幂等、100 并发 Routing
|
行为契约。当前 MemoryStore 已覆盖配置提交、Upstream 幂等、100 并发 Routing
|
||||||
CAS、审计和租约 Outbox;Schema 静态测试证明只声明六张管理表。只有 pgx
|
CAS、审计分页、Routing no-op、原子批量 ACK 和租约 Outbox;Schema 静态测试
|
||||||
Adapter 在真实 PostgreSQL 18 上运行同一契约后,才标记生产持久化完成。
|
证明只声明六张管理表。PostgreSQL 18 的回环端口、tmpfs 隔离 fixture 已完成
|
||||||
|
静态验证;只有 pgx Adapter 在该实例上运行同一契约后,才标记生产持久化完成。
|
||||||
|
|
||||||
Admin 应用层测试覆盖 typed-nil 依赖、Actor/SourceIP 映射、Routing CAS 错误、
|
Admin 应用层测试覆盖 typed-nil 依赖、Actor/SourceIP 映射、Routing CAS 错误、
|
||||||
权威管理快照与低基数运行态聚合、未知字段拒绝、主配置/Secret 文件 I/O 分类、
|
权威管理快照与低基数运行态聚合、未知字段拒绝、主配置/Secret 文件 I/O 分类、
|
||||||
|
|||||||
10
findings.md
10
findings.md
@ -173,3 +173,13 @@ Routing 自上而下匹配,首条命中停止;支持 Gateway 与 Extract 两
|
|||||||
可执行的 `deploy/tools/configcheck`,并明确生产 Controller 入口仍是计划能力。
|
可执行的 `deploy/tools/configcheck`,并明确生产 Controller 入口仍是计划能力。
|
||||||
- 文档中的具体 `go run`/`go build` 目标现在必须存在;包含 `...` 的通配包命令
|
- 文档中的具体 `go run`/`go build` 目标现在必须存在;包含 `...` 的通配包命令
|
||||||
由 Go 工具链自身解析并在完整验证中执行。
|
由 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`;直接修改可能影响已有本地数据,必须配套迁移步骤后
|
||||||
|
单独处理,当前不能把生产持久化拓扑视为已验证。
|
||||||
|
|||||||
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
## 2026-07-29
|
## 2026-07-29
|
||||||
|
|
||||||
|
- 新增 PostgreSQL 18 隔离测试服务的静态契约:回环端口、正确 PG18 tmpfs 数据
|
||||||
|
根目录、零持久卷;Redis 测试改用独立 Compose 项目和显式服务启动,避免两个
|
||||||
|
fixture 相互影响。未拉取或启动容器,pgx Adapter/真实契约仍待依赖确认。
|
||||||
- 扩展 `adminstate/contracttest` 公用契约:审计完整字段和 AfterID 分页、Routing
|
- 扩展 `adminstate/contracttest` 公用契约:审计完整字段和 AfterID 分页、Routing
|
||||||
no-op 审计且不写 Outbox、批量 ACK 零部分提交,以及六个 Store 方法的 Context
|
no-op 审计且不写 Outbox、批量 ACK 零部分提交,以及六个 Store 方法的 Context
|
||||||
取消;MemoryStore 全部通过,后续 PostgreSQL Adapter 必须运行同一套契约。
|
取消;MemoryStore 全部通过,后续 PostgreSQL Adapter 必须运行同一套契约。
|
||||||
|
|||||||
@ -2,10 +2,11 @@ $ErrorActionPreference = "Stop"
|
|||||||
|
|
||||||
$repositoryRoot = Split-Path -Parent $PSScriptRoot
|
$repositoryRoot = Split-Path -Parent $PSScriptRoot
|
||||||
$composeFile = Join-Path $repositoryRoot "deploy/docker-compose.test.yml"
|
$composeFile = Join-Path $repositoryRoot "deploy/docker-compose.test.yml"
|
||||||
|
$composeProject = "proxy-pool-redis-test"
|
||||||
$previousRedisURL = [Environment]::GetEnvironmentVariable("PROXY_POOL_TEST_REDIS_URL", "Process")
|
$previousRedisURL = [Environment]::GetEnvironmentVariable("PROXY_POOL_TEST_REDIS_URL", "Process")
|
||||||
|
|
||||||
try {
|
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) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
throw "starting Redis test fixture failed with exit code $LASTEXITCODE"
|
throw "starting Redis test fixture failed with exit code $LASTEXITCODE"
|
||||||
}
|
}
|
||||||
@ -29,5 +30,5 @@ finally {
|
|||||||
else {
|
else {
|
||||||
$env:PROXY_POOL_TEST_REDIS_URL = $previousRedisURL
|
$env:PROXY_POOL_TEST_REDIS_URL = $previousRedisURL
|
||||||
}
|
}
|
||||||
docker compose -f $composeFile down --remove-orphans
|
docker compose -p $composeProject -f $composeFile down --volumes --remove-orphans
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user