diff --git a/internal/testutil/testdb/db.go b/internal/testutil/testdb/db.go
index 2f507690f..2903d19c9 100644
--- a/internal/testutil/testdb/db.go
+++ b/internal/testutil/testdb/db.go
@@ -99,8 +99,8 @@ func TestDBURL() string {
const maxPostgresIdentifier = 63
// derivedDBSuffix derives the database-name suffix beneath the configured base:
-// a per-WORKSPACE component plus a per-PACKAGE component, or "" when the process
-// is not a test binary or sharing is forced.
+// a per-WORKSPACE component plus a per-PACKAGE component, or "" when sharing
+// is forced.
//
// Two dimensions, because per-package alone was not enough. It stops packages in
// ONE run from truncating each other, but every checkout computed the same names,
@@ -111,6 +111,13 @@ const maxPostgresIdentifier = 63
// makes the isolation structural: two checkouts cannot collide even when nobody
// configures anything.
//
+// Derived for every binary, not only `go test` ones: a plain binary built from
+// this module (cmd/e2a-contract-server is the one that calls into the test
+// harness) previously fell through to the base URL verbatim, so two contract
+// server processes on one machine shared and truncated each other's database.
+// os.Args[0] for a non-test binary is just its own name (no ".test" suffix),
+// which still yields a distinct, stable per-binary component.
+//
// Name length: _ws<8>_pkg_ runs ~40 chars for this repo's longest
// package names, well inside Postgres's 63-byte identifier limit. A much longer
// custom base could push past it, where Postgres truncates silently — keep bases
@@ -121,9 +128,6 @@ func derivedDBSuffix() string {
return ""
}
bin := filepath.Base(os.Args[0])
- if !strings.HasSuffix(bin, ".test") {
- return ""
- }
name := strings.ToLower(strings.TrimSuffix(bin, ".test"))
sanitized := make([]rune, 0, len(name))
for _, r := range name {
diff --git a/internal/testutil/testdb/db_test.go b/internal/testutil/testdb/db_test.go
index 4e46dafd4..761c03fb5 100644
--- a/internal/testutil/testdb/db_test.go
+++ b/internal/testutil/testdb/db_test.go
@@ -7,6 +7,7 @@ import (
"net/url"
"os"
"os/exec"
+ "path/filepath"
"strings"
"testing"
"time"
@@ -418,6 +419,53 @@ func TestTestDBURLDerivesPerPackageDatabase(t *testing.T) {
}
}
+// TestTestDBURLDerivesForNonTestBinaries proves the derivation also covers
+// cmd/e2a-contract-server (see #827): a symlink to this test binary under a
+// non-test name reproduces a real non-".test" argv[0].
+func TestTestDBURLDerivesForNonTestBinaries(t *testing.T) {
+ if os.Getenv(testDBErrorChildEnv) == t.Name() {
+ u, err := url.Parse(TestDBURL())
+ if err != nil {
+ t.Fatalf("parse TestDBURL: %v", err)
+ }
+ fmt.Printf("DBNAME=%s\n", strings.TrimPrefix(u.Path, "/"))
+ return
+ }
+
+ self, err := filepath.Abs(os.Args[0])
+ if err != nil {
+ t.Fatalf("resolve this test binary's path: %v", err)
+ }
+ renamed := filepath.Join(t.TempDir(), "e2a-contract-server")
+ if err := os.Symlink(self, renamed); err != nil {
+ t.Fatalf("symlink the test binary under a non-test name: %v", err)
+ }
+
+ cmd := exec.Command(renamed, "-test.run=^"+t.Name()+"$")
+ cmd.Env = testDBChildEnv(t.Name(), "postgres://e2a:e2a@localhost:5433/e2a_test?sslmode=disable")
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("child failed: %v\n%s", err, output)
+ }
+
+ var dbname string
+ for _, line := range strings.Split(string(output), "\n") {
+ if rest, ok := strings.CutPrefix(line, "DBNAME="); ok {
+ dbname = rest
+ break
+ }
+ }
+ if dbname == "" {
+ t.Fatalf("child did not report a dbname:\n%s", output)
+ }
+ if !strings.Contains(dbname, "_ws") {
+ t.Errorf("dbname = %q from a non-test binary, want a _ws workspace component", dbname)
+ }
+ if !strings.HasSuffix(dbname, "_pkg_e2a_contract_server") {
+ t.Errorf("dbname = %q, want the _pkg_e2a_contract_server suffix", dbname)
+ }
+}
+
func TestOpenPreparedTestDBCreatesMissingDatabase(t *testing.T) {
// First use of a package database must self-provision: connect failure
// with SQLSTATE 3D000 creates the database from the base URL's server