Fix the try on 63980bf5: keep a test database's name within postgres's 63-byte limit so two calls in one long-named test never collide (issue 1517)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// integrationDatabaseName builds a one-off postgres database name for an integration test: unique (n, a
|
||||
// counter that only ever grows, sits at its very end, where nothing can truncate it away) and never over
|
||||
// 63 bytes — postgres's own identifier limit (NAMEDATALEN - 1) — however long testName is. A short hash of
|
||||
// testName keeps the name traceable to its test without spending the length budget a long test name would
|
||||
// (issue 1517, fixed per the lead's try feedback on 63980bf5): the earlier name,
|
||||
// "mesh_mail_reader_test_"+lower(testName)+"_"+n, ran past 63 bytes for
|
||||
// TestPrepareOnADatabaseAtMigration0001AppliesWhatFollows, and postgres silently truncated both of that
|
||||
// test's two calls (n=1, n=2) to the SAME 63-byte name — the second call's own `drop ... with (force)`
|
||||
// then dropped the first call's database while its store was still connected to it, "terminating
|
||||
// connection due to administrator command" (SQLSTATE 57P01). TestIntegrationDatabaseNameStaysUniqueAndWithinPostgresLimit
|
||||
// proves both properties, with no database, including for that exact test's own long name.
|
||||
func integrationDatabaseName(testName string, n int64) string {
|
||||
sum := sha256.Sum256([]byte(testName))
|
||||
return fmt.Sprintf("mmr_test_%x_%d", sum[:4], n)
|
||||
}
|
||||
|
||||
// TestIntegrationDatabaseNameStaysUniqueAndWithinPostgresLimit (1517, fixed per the lead's try feedback on
|
||||
// 63980bf5): for a very long test name — the exact one whose two calls collided in try 63980bf5 — two
|
||||
// successive counters give two different names, and both stay at or under postgres's own 63-byte limit. A
|
||||
// plain unit test (no `//go:build integration`, no database) so it runs on every `go test ./...`.
|
||||
func TestIntegrationDatabaseNameStaysUniqueAndWithinPostgresLimit(t *testing.T) {
|
||||
const long = "TestPrepareOnADatabaseAtMigration0001AppliesWhatFollowsAndThenSomeEvenLongerSuffixToBeSure"
|
||||
a := integrationDatabaseName(long, 1)
|
||||
b := integrationDatabaseName(long, 2)
|
||||
if a == b {
|
||||
t.Fatalf("two different counters gave the same name %q: the second drop would kill the first's connection (SQLSTATE 57P01)", a)
|
||||
}
|
||||
for _, name := range []string{a, b} {
|
||||
if len(name) > 63 {
|
||||
t.Fatalf("name %q is %d bytes, want at most 63 (postgres's own identifier limit)", name, len(name))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
@@ -32,9 +31,9 @@ func testDatabaseURL(t *testing.T) string {
|
||||
|
||||
// freshDatabaseCounter makes freshIntegrationDatabase's own name unique across every call, even two calls
|
||||
// within the SAME test (t.Name() alone is not: TestPrepareOnADatabaseAtMigration0001AppliesWhatFollows
|
||||
// calls it twice, for "fresh" and "stepped", and two databases of the same name collided — the first
|
||||
// call's own pool was still open on it when the second tried to drop and recreate it, "database ... is
|
||||
// being accessed by other users", SQLSTATE 55006; the coordinator's own fix for issue 1517).
|
||||
// calls it twice, for "fresh" and "stepped") — combined with integrationDatabaseName's own 63-byte limit
|
||||
// (issue 1517, fixed per the lead's try feedback on 63980bf5: this test's own long name, truncated by
|
||||
// postgres past that limit, once made both calls' names the same).
|
||||
var freshDatabaseCounter atomic.Int64
|
||||
|
||||
// freshIntegrationDatabase makes a new, empty database on the server MESH_TEST_POSTGRES names, and returns
|
||||
@@ -51,7 +50,7 @@ func freshIntegrationDatabase(t *testing.T) string {
|
||||
}
|
||||
defer admin.Close()
|
||||
n := freshDatabaseCounter.Add(1)
|
||||
name := fmt.Sprintf("mesh_mail_reader_test_%s_%d", strings.ToLower(strings.ReplaceAll(t.Name(), "/", "_")), n)
|
||||
name := integrationDatabaseName(t.Name(), n)
|
||||
if _, err := admin.Exec(ctx, "drop database if exists "+name+" with (force)"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user