|
| 1 | +// /////////////////////////////////////////////////////////////////////////// |
| 2 | +// |
| 3 | +// # ACE - Active Consistency Engine |
| 4 | +// |
| 5 | +// Copyright (C) 2023 - 2026, pgEdge (https://www.pgedge.com/) |
| 6 | +// |
| 7 | +// This software is released under the PostgreSQL License: |
| 8 | +// https://opensource.org/license/postgresql |
| 9 | +// |
| 10 | +// /////////////////////////////////////////////////////////////////////////// |
| 11 | + |
| 12 | +package integration |
| 13 | + |
| 14 | +import ( |
| 15 | + "context" |
| 16 | + "fmt" |
| 17 | + "testing" |
| 18 | + |
| 19 | + "github.com/jackc/pgx/v5" |
| 20 | + "github.com/jackc/pgx/v5/pgxpool" |
| 21 | + "github.com/stretchr/testify/require" |
| 22 | +) |
| 23 | + |
| 24 | +// mtree build on a table that is empty on every node must fail with a clear, |
| 25 | +// actionable "0 rows on all nodes" message -- not the internal "could not |
| 26 | +// determine a reference node" error that leaves the user unsure whether the |
| 27 | +// table is missing, the nodes are unreachable, or something is broken. |
| 28 | +func TestBuildMtreeFailsClearlyOnEmptyTable(t *testing.T) { |
| 29 | + ctx := context.Background() |
| 30 | + tableName := "mtree_empty_table_test" |
| 31 | + qualifiedTable := fmt.Sprintf("%s.%s", testSchema, tableName) |
| 32 | + safeTable := pgx.Identifier{testSchema, tableName}.Sanitize() |
| 33 | + nodes := []string{serviceN1, serviceN2} |
| 34 | + |
| 35 | + // Create the table on both nodes but insert no rows. |
| 36 | + for _, pool := range []*pgxpool.Pool{pgCluster.Node1Pool, pgCluster.Node2Pool} { |
| 37 | + _, err := pool.Exec(ctx, "CREATE TABLE IF NOT EXISTS "+safeTable+" (id INT PRIMARY KEY, payload TEXT)") // nosemgrep |
| 38 | + require.NoError(t, err) |
| 39 | + } |
| 40 | + t.Cleanup(func() { |
| 41 | + for _, pool := range []*pgxpool.Pool{pgCluster.Node1Pool, pgCluster.Node2Pool} { |
| 42 | + _, _ = pool.Exec(ctx, "DROP TABLE IF EXISTS "+safeTable) // nosemgrep |
| 43 | + } |
| 44 | + }) |
| 45 | + |
| 46 | + task := newTestMerkleTreeTask(t, qualifiedTable, nodes) |
| 47 | + require.NoError(t, task.RunChecks(false)) |
| 48 | + require.NoError(t, task.MtreeInit()) |
| 49 | + t.Cleanup(func() { _ = task.MtreeTeardown() }) |
| 50 | + |
| 51 | + err := task.BuildMtree() |
| 52 | + require.Error(t, err, "build on an empty table must fail") |
| 53 | + require.Contains(t, err.Error(), "has 0 rows on all nodes", |
| 54 | + "error must name the real cause: the table is empty") |
| 55 | + require.Contains(t, err.Error(), qualifiedTable, |
| 56 | + "error must name the offending table") |
| 57 | + require.NotContains(t, err.Error(), "could not determine a reference node", |
| 58 | + "the internal reference-node error must no longer leak for empty tables") |
| 59 | +} |
0 commit comments