TestInitConnNilMaintNotificationsConfig is a regression test for https://github.com/redis/go-redis/issues/3675 initConn previously accessed MaintNotificationsConfig.EndpointType unconditionally, even though the preceding line correctly nil-checked MaintNotificationsConfig. This caused a nil pointer
(t *testing.T)
| 24 | // MaintNotificationsConfig. This caused a nil pointer dereference panic |
| 25 | // and left optLock.RLock held, leading to a subsequent deadlock. |
| 26 | func TestInitConnNilMaintNotificationsConfig(t *testing.T) { |
| 27 | // Start a minimal TCP server that speaks enough RESP to let |
| 28 | // initConn get past the HELLO / AUTH / pipeline phases and reach |
| 29 | // the MaintNotificationsConfig code path. |
| 30 | ln, err := net.Listen("tcp", "127.0.0.1:0") |
| 31 | if err != nil { |
| 32 | t.Fatalf("failed to listen: %v", err) |
| 33 | } |
| 34 | defer ln.Close() |
| 35 | |
| 36 | // mockRedis responds to every RESP command with a Redis-protocol |
| 37 | // error. This lets initConn fall through HELLO (Redis errors are |
| 38 | // not fatal when there is no password) and the empty pipeline |
| 39 | // succeeds trivially. |
| 40 | go func() { |
| 41 | for { |
| 42 | conn, err := ln.Accept() |
| 43 | if err != nil { |
| 44 | return |
| 45 | } |
| 46 | go func(c net.Conn) { |
| 47 | defer c.Close() |
| 48 | scanner := bufio.NewScanner(c) |
| 49 | for scanner.Scan() { |
| 50 | line := scanner.Text() |
| 51 | if strings.HasPrefix(line, "*") { |
| 52 | _, _ = c.Write([]byte("-ERR unknown command\r\n")) |
| 53 | } |
| 54 | } |
| 55 | }(conn) |
| 56 | } |
| 57 | }() |
| 58 | |
| 59 | opt := &Options{ |
| 60 | Addr: ln.Addr().String(), |
| 61 | } |
| 62 | opt.init() |
| 63 | |
| 64 | // Force MaintNotificationsConfig to nil after init() to reproduce |
| 65 | // the scenario from issue #3675. |
| 66 | opt.MaintNotificationsConfig = nil |
| 67 | |
| 68 | c := &baseClient{ |
| 69 | opt: opt, |
| 70 | } |
| 71 | c.initHooks(hooks{ |
| 72 | dial: c.dial, |
| 73 | process: c.process, |
| 74 | pipeline: c.processPipeline, |
| 75 | txPipeline: c.processTxPipeline, |
| 76 | }) |
| 77 | |
| 78 | // Dial a real connection to the mock server. |
| 79 | netConn, err := net.DialTimeout("tcp", ln.Addr().String(), 2*time.Second) |
| 80 | if err != nil { |
| 81 | t.Fatalf("failed to dial mock server: %v", err) |
| 82 | } |
| 83 | defer netConn.Close() |
nothing calls this directly
no test coverage detected