internal/config/store_compose_test.go
Rama main · réplica de lectura del repositorio original.
package config
// Guards which object store the shipped stack names, which is the thing issue
// #388 got wrong: the guide and docker-compose.yml each pointed at a MinIO image,
// both registries stopped serving them anonymously, and every documented way to
// stand Cairn up failed at `docker compose pull`. CI had been running Garage the
// whole time, so "S3-compatible" was a claim the pipeline tested and the
// documentation assumed.
//
// The invariant is therefore about agreement, not about any one vendor: the image
// the guide tells people to pull, the image `make up` pulls, and the image the
// integration job stands up are one image at one tag. When a registry stops
// serving it, all three move together, and a drift between the documented path
// and the tested one is a failing test rather than a broken self-hosting story.
//
// @joestump-agent 10/19/2026 - Added for cairn#388.
import (
"os"
"regexp"
"testing"
)
var (
garageImageRe = regexp.MustCompile(`dxflrs/garage:([A-Za-z0-9._-]+)`)
minioImageRe = regexp.MustCompile(`(?m)^\s*image:\s*(\S*minio\S*)`)
)
// imageTag returns the single Garage tag a file names, failing the test when it
// names none (a store swapped out silently) or more than one (a file that pins
// two versions of itself).
func imageTag(t *testing.T, path string) string {
t.Helper()
src, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
tag := ""
seen := map[string]bool{}
for _, m := range garageImageRe.FindAllStringSubmatch(string(src), -1) {
seen[m[1]] = true
tag = m[1]
}
if len(seen) == 0 {
t.Errorf("%s names no Garage image: the store it starts is untested by this repo", path)
return ""
}
if len(seen) > 1 {
t.Errorf("%s pins more than one Garage tag: %v", path, keys(seen))
}
return tag
}
func keys(m map[string]bool) []string {
out := make([]string, 0, len(m))
for k := range m {
out = append(out, k)
}
return out
}
func TestShippedObjectStoreMatchesTheTestedOne(t *testing.T) {
ci := imageTag(t, "../../.gitea/workflows/pipeline.yaml")
dev := imageTag(t, "../../docker-compose.yml")
guide := imageTag(t, "../../website/docs/guides/self-hosting.md")
if ci == "" || dev == "" || guide == "" {
return
}
if dev != ci {
t.Errorf("docker-compose.yml pins Garage %s but CI tests %s: the store developers run is not the store cairn's S3 code is exercised against", dev, ci)
}
if guide != ci {
t.Errorf("the self-hosting guide pins Garage %s but CI tests %s: a fresh instance runs an image nothing tests", guide, ci)
}
}
// The MinIO images are what broke: quay.io answers 401 and the Docker Hub
// repositories are gone, so naming one in either documented path makes that path
// unrunnable no matter what the store is nominally compatible with.
func TestShippedStackNamesNoUnpullableMinioImage(t *testing.T) {
for _, path := range []string{"../../docker-compose.yml", "../../website/docs/guides/self-hosting.md"} {
src, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
for _, m := range minioImageRe.FindAllStringSubmatch(string(src), -1) {
t.Errorf("%s pulls %s: MinIO's registries no longer serve anonymous pulls (cairn#388), so this path fails at `docker compose pull`", path, m[1])
}
}
}
// The guide's compose block has to point cairn at the store that block starts.
func TestGuideComposePointsAtTheStoreItStarts(t *testing.T) {
vars := guideComposeVars(t)
endpoint, ok := vars["CAIRN_S3_ENDPOINT"]
if !ok {
t.Fatal("the guide's compose file sets no CAIRN_S3_ENDPOINT")
}
if endpoint != "garage:3900" {
t.Errorf("CAIRN_S3_ENDPOINT = %q: the garage service in the same block serves S3 on garage:3900", endpoint)
}
}