TestNewClusterState_PortZero verifies that nodes reported by CLUSTER SLOTS with a port of 0 — the topology produced by TLS-only Redis clusters started with `--port 0 --tls-port 6379` — get their dial address normalized so they do not surface to the dial path as ":0". Regression test for https://git
(t *testing.T)
| 362 | // CLUSTER SLOTS reports port 0 — the origin is by definition reachable, since |
| 363 | // that is the address we used to obtain the slot map in the first place. |
| 364 | func TestNewClusterState_PortZero(t *testing.T) { |
| 365 | t.Run("non-loopback origin replaces :0 port with origin port", func(t *testing.T) { |
| 366 | opt := &ClusterOptions{} |
| 367 | opt.init() |
| 368 | nodes := newClusterNodes(opt) |
| 369 | defer nodes.Close() |
| 370 | |
| 371 | // Simulates a TLS-only cluster: CLUSTER SLOTS reports port 0 because |
| 372 | // the server is configured with `--port 0 --tls-port 6379`. |
| 373 | slots := []ClusterSlot{{ |
| 374 | Start: 0, |
| 375 | End: 5460, |
| 376 | Nodes: []ClusterNode{{Addr: "172.30.0.10:0"}}, |
| 377 | }, { |
| 378 | Start: 5461, |
| 379 | End: 10922, |
| 380 | Nodes: []ClusterNode{{Addr: "172.30.0.11:0"}}, |
| 381 | }} |
| 382 | |
| 383 | // Origin is the TLS configuration endpoint that returned this slot map. |
| 384 | state, err := newClusterState(nodes, slots, "172.30.0.10:6379") |
| 385 | if err != nil { |
| 386 | t.Fatalf("newClusterState failed: %v", err) |
| 387 | } |
| 388 | |
| 389 | for i, want := range []string{"172.30.0.10:6379", "172.30.0.11:6379"} { |
| 390 | got := state.slots[i].nodes[0].Client.Options().Addr |
| 391 | if _, port, _ := net.SplitHostPort(got); port == "0" { |
| 392 | t.Fatalf("slot %d Addr = %q: port 0 reached the dial path "+ |
| 393 | "(see #3726: PubSub dials this verbatim and fails with "+ |
| 394 | "`connection refused`)", i, got) |
| 395 | } |
| 396 | if got != want { |
| 397 | t.Errorf("slot %d Addr = %q, want %q (origin port should be "+ |
| 398 | "used as fallback for :0 nodes)", i, got, want) |
| 399 | } |
| 400 | |
| 401 | // NodeAddress documents the raw server-reported endpoint |
| 402 | // (see options.go: "the exact endpoint string returned by |
| 403 | // CLUSTER SLOTS before any resolution or transformation"). |
| 404 | // The port-0 fix is a transformation, so NodeAddress keeps |
| 405 | // the original ":0" value used by maintenance-notification |
| 406 | // matching. If the design changes this, update this assertion. |
| 407 | wantNodeAddr := []string{"172.30.0.10:0", "172.30.0.11:0"}[i] |
| 408 | if got := state.slots[i].nodes[0].Client.NodeAddress(); got != wantNodeAddr { |
| 409 | t.Errorf("slot %d NodeAddress = %q, want %q (raw server value)", |
| 410 | i, got, wantNodeAddr) |
| 411 | } |
| 412 | } |
| 413 | }) |
| 414 | |
| 415 | t.Run("non-zero ports are unchanged", func(t *testing.T) { |
| 416 | // Guards against the fix over-rewriting ports for normal clusters. |
| 417 | opt := &ClusterOptions{} |
| 418 | opt.init() |
| 419 | nodes := newClusterNodes(opt) |
| 420 | defer nodes.Close() |
| 421 |
nothing calls this directly
no test coverage detected