| 1 | package main |
| 2 | |
| 3 | // 🤖 Runs the backend-agnostic vaultStore contract suite (store_test.go) |
| 4 | // against a REAL S3 bucket — conditional-write semantics are exactly the kind |
| 5 | // of thing a fake gets subtly wrong, so there is deliberately no mock here. |
| 6 | // Gated on env so the default `go test ./...` stays offline: |
| 7 | // |
| 8 | // eval "$(aws configure export-credentials --profile eleven-dev --format env)" && \ |
| 9 | // LCHAT_TEST_S3_BUCKET=eleven-dev-home-427880590669 LCHAT_TEST_S3_REGION=eu-west-1 \ |
| 10 | // go test ./home/ -run TestS3VaultStore -count=1 -v |
| 11 | // |
| 12 | // Each run isolates itself under a random test-<hex>/ key prefix (and each |
| 13 | // subtest store under its own sub-prefix, since the contract expects a fresh |
| 14 | // store), and cleanup permanently deletes every version of every object under |
| 15 | // the run prefix. |
| 16 | |
| 17 | import ( |
| 18 | "crypto/rand" |
| 19 | "encoding/hex" |
| 20 | "fmt" |
| 21 | "net/url" |
| 22 | "os" |
| 23 | "testing" |
| 24 | ) |
| 25 | |
| 26 | func TestS3VaultStore(t *testing.T) { |
| 27 | bucket := os.Getenv("LCHAT_TEST_S3_BUCKET") |
| 28 | if bucket == "" { |
| 29 | t.Skip("LCHAT_TEST_S3_BUCKET not set; skipping live S3 contract suite (see file header for how to run)") |
| 30 | } |
| 31 | region := os.Getenv("LCHAT_TEST_S3_REGION") |
| 32 | if region == "" { |
| 33 | t.Fatal("LCHAT_TEST_S3_REGION must be set alongside LCHAT_TEST_S3_BUCKET") |
| 34 | } |
| 35 | |
| 36 | var runID [8]byte |
| 37 | if _, err := rand.Read(runID[:]); err != nil { |
| 38 | t.Fatalf("rand: %v", err) |
| 39 | } |
| 40 | runPrefix := "test-" + hex.EncodeToString(runID[:]) + "/" |
| 41 | root, err := newS3Store(bucket, region, runPrefix, os.Getenv("LCHAT_TEST_S3_ENDPOINT")) |
| 42 | if err != nil { |
| 43 | t.Fatalf("newS3Store: %v", err) |
| 44 | } |
| 45 | t.Cleanup(func() { |
| 46 | versions, err := root.listVersions(runPrefix) |
| 47 | if err != nil { |
| 48 | t.Errorf("cleanup list: %v (objects may remain under %s)", err, runPrefix) |
| 49 | return |
| 50 | } |
| 51 | for _, v := range versions { |
| 52 | res, err := root.c.do("DELETE", v.key, url.Values{"versionId": {v.versionID}}, nil, nil) |
| 53 | if err != nil || (res.status != 204 && res.status != 200) { |
| 54 | t.Errorf("cleanup delete %s@%s: status=%v err=%v", v.key, v.versionID, res, err) |
| 55 | } |
| 56 | } |
| 57 | }) |
| 58 | |
| 59 | // The contract suite expects every newStore call to hand back a FRESH, |
| 60 | // empty store — so each gets its own sub-prefix under the run prefix. |
| 61 | n := 0 |
| 62 | testVaultStore(t, func(t *testing.T) vaultStore { |
| 63 | n++ |
| 64 | return &s3Store{c: root.c, prefix: fmt.Sprintf("%ss%d/", runPrefix, n)} |
| 65 | }) |
| 66 | } |
| 67 | |