Skip to content

Commit a63b68f

Browse files
committed
test(harness): add native_client helper to cluster test harness
Native protocol connections in cluster tests hand-rolled a PoolConfig per call site, defaulting to trust user "admin" which the harness never provisions, causing spurious auth failures. Add TestClusterNode::native_client()/native_client_with() that always connects as the harness's bootstrapped trust superuser, and expose it as a shared HARNESS_SUPERUSER constant so the pgwire and native paths can't drift apart. Update existing native-protocol tests to use the new helper instead of constructing PoolConfig by hand.
1 parent 74e3329 commit a63b68f

9 files changed

Lines changed: 67 additions & 17 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nodedb-cluster-tests/tests/native_gather_join_in_txn_occ.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,13 @@ fn is_serialization_abort(e: &NodeDbError) -> bool {
8989
/// the SAME socket and server session, so `BEGIN → read → writes → COMMIT` all
9090
/// share one transaction context.
9191
fn pinned_native_client(node: &TestClusterNode) -> NativeClient {
92-
NativeClient::new(PoolConfig {
93-
addr: format!("127.0.0.1:{}", node.native_port),
92+
// `native_client_with` overrides `addr`/`auth` to the harness's
93+
// bootstrapped superuser (see its doc comment for why the `PoolConfig`
94+
// default of trust user `admin` never works against this harness); the
95+
// `max_size: 1` pinning is this test's own requirement, preserved
96+
// explicitly through the caller-supplied base config.
97+
node.native_client_with(PoolConfig {
9498
max_size: 1,
95-
// The cluster harness runs trust auth with `nodedb` as the sole
96-
// materialized superuser; the `PoolConfig` default user is `admin`,
97-
// which trust mode rejects as a non-existent identity. Authenticate as
98-
// the harness superuser, matching the pgwire path (`user=nodedb`).
99-
auth: nodedb_types::protocol::AuthMethod::Trust {
100-
username: "nodedb".into(),
101-
},
10299
..Default::default()
103100
})
104101
}

nodedb-cluster-tests/tests/sql_cluster_cross_node_dml_tests/native_implicit_edge_delete_cross_node.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626
2727
use std::time::Duration;
2828

29-
use nodedb_client::NativeClient;
30-
3129
use crate::common::cluster_harness::{TestCluster, wait_for};
3230

3331
const SOURCES: usize = 12;
@@ -123,9 +121,11 @@ async fn native_implicit_edge_delete_cleans_reverse_cross_node() {
123121
.expect("a non-sequencer-leader coordinator must exist in a 3-node cluster");
124122
let coordinator = &cluster.nodes[coordinator_idx];
125123

126-
// Connect a NativeClient to the coordinator's native listener port.
127-
let native_addr = format!("127.0.0.1:{}", coordinator.native_port);
128-
let native = NativeClient::connect(&native_addr);
124+
// Connect a NativeClient to the coordinator's native listener port,
125+
// authenticated as the harness's bootstrapped trust superuser (a bare
126+
// `NativeClient::connect` defaults to trust user `admin`, which this
127+
// harness never provisions).
128+
let native = coordinator.native_client();
129129

130130
// Issue the predicate DELETE via the NATIVE protocol.
131131
// This exercises the `edge_recon_gate::try_edge_recon_dispatch` path.

nodedb-test-support/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ nodedb = { workspace = true }
1616
nodedb-array = { workspace = true }
1717
nodedb-physical = { workspace = true }
1818
nodedb-bridge = { workspace = true }
19+
nodedb-client = { workspace = true, features = ["native"] }
1920
nodedb-cluster = { workspace = true }
2021
nodedb-crdt = { workspace = true }
2122
nodedb-mem = { workspace = true }

nodedb-test-support/src/cluster_harness/node/lifecycle/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ mod spawn_variants;
3535
mod teardown;
3636
mod types;
3737

38-
pub use types::TestClusterNode;
38+
pub use types::{HARNESS_SUPERUSER, TestClusterNode};

nodedb-test-support/src/cluster_harness/node/lifecycle/spawn_full.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use nodedb::wal::WalManager;
2222
use crate::cluster_harness::cluster::ClusterSpawnConfig;
2323

2424
use super::client_slot::ClusterTestClient;
25-
use super::types::{DataDir, TestClusterNode};
25+
use super::types::{DataDir, HARNESS_SUPERUSER, TestClusterNode};
2626

2727
impl TestClusterNode {
2828
/// [`Self::spawn_with_full_config`] entry point used by every spawn
@@ -111,7 +111,7 @@ impl TestClusterNode {
111111
// `AuthMode::Trust`, which resolves — but never fabricates — a durable
112112
// stored identity. Without this every node rejects the harness connect
113113
// with `trust auth: user 'nodedb' does not exist`.
114-
credentials.bootstrap_trust_superuser("nodedb")?;
114+
credentials.bootstrap_trust_superuser(HARNESS_SUPERUSER)?;
115115
let mut shared =
116116
SharedState::new_with_credentials(dispatcher, Arc::clone(&wal), credentials)?;
117117

nodedb-test-support/src/cluster_harness/node/lifecycle/types.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ use nodedb::event::EventPlane;
1010

1111
use super::client_slot::ClusterTestClient;
1212

13+
/// The sole trust identity the cluster harness bootstraps on every node
14+
/// (see `spawn_full::spawn_with_full_config_at`'s
15+
/// `credentials.bootstrap_trust_superuser(HARNESS_SUPERUSER)`). Both the
16+
/// pre-wired `client` field (pgwire, `user=nodedb`) and
17+
/// [`super::super::native_client`]'s helper authenticate as this identity —
18+
/// single source of truth so the two paths cannot drift apart.
19+
pub const HARNESS_SUPERUSER: &str = "nodedb";
20+
1321
/// Ownership of this node's on-disk data directory.
1422
///
1523
/// `Owned` is the historical behaviour: the node minted its own `tempdir()`

nodedb-test-support/src/cluster_harness/node/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
pub mod graph;
77
pub mod inspect;
88
pub mod lifecycle;
9+
pub mod native_client;
910

1011
pub use lifecycle::TestClusterNode;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
3+
//! Native (MessagePack) protocol client construction for [`TestClusterNode`].
4+
//!
5+
//! `NativeClient::connect` / `PoolConfig::default()` authenticate as trust
6+
//! user `"admin"`, but the cluster harness bootstraps exactly ONE trust
7+
//! identity per node — [`super::lifecycle::HARNESS_SUPERUSER`] (see
8+
//! `lifecycle::spawn_full`'s `credentials.bootstrap_trust_superuser(...)`),
9+
//! the same identity the pre-wired pgwire `client` field connects as. A bare
10+
//! `NativeClient::connect` against this harness therefore ALWAYS fails auth
11+
//! with `trust user 'admin' does not exist`. Use the helpers below instead
12+
//! of hand-rolling a `PoolConfig` at each test call site.
13+
14+
use nodedb_client::NativeClient;
15+
use nodedb_client::native::pool::PoolConfig;
16+
use nodedb_types::protocol::AuthMethod;
17+
18+
use super::lifecycle::{HARNESS_SUPERUSER, TestClusterNode};
19+
20+
impl TestClusterNode {
21+
/// A `NativeClient` pool-connected to this node's native listener,
22+
/// authenticated as the harness's bootstrapped trust superuser.
23+
pub fn native_client(&self) -> NativeClient {
24+
self.native_client_with(PoolConfig::default())
25+
}
26+
27+
/// Same as [`Self::native_client`], but starting from a caller-supplied
28+
/// `PoolConfig` for fields the harness doesn't dictate — e.g. a test
29+
/// pinning `max_size: 1` so every call rides one socket/session for an
30+
/// in-transaction sequence. `addr` and `auth` are always overridden to
31+
/// this node's native port and the harness superuser, so callers cannot
32+
/// reintroduce the `admin` mismatch by way of `base`.
33+
pub fn native_client_with(&self, base: PoolConfig) -> NativeClient {
34+
NativeClient::new(PoolConfig {
35+
addr: format!("127.0.0.1:{}", self.native_port),
36+
auth: AuthMethod::Trust {
37+
username: HARNESS_SUPERUSER.to_string(),
38+
},
39+
..base
40+
})
41+
}
42+
}

0 commit comments

Comments
 (0)