Repository: symfony/symfony
File: src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php (PHP)

Code before:
            use RedisTrait;
        };
        $mock::createConnection($dsn);
    }

    public static function provideInvalidDbIndexDsnParameter(): array
    {
        return [
            [
                'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/abc',
            ],
            [
                'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/3?dbindex=6',
            ],
        ];
    }

    public function testPredisMasterAuthFallsBackToQueryAuth()
    {
        $predisClass = new class extends \Predis\Client {
            public static array $captured = [];
            private object $connection;

            public function __construct($parameters = null, $options = null)
            {
                self::$captured = [
                    'parameters' => $parameters,
                    'options' => $options,
                ];
                $this->connection = new class {
                    public function setSentinelTimeout(float $timeout): void
                    {
                    }
                };
            }

            public function getConnection()
            {
                return $this->connection;
            }
        };

        $mock = new class {
            use RedisTrait;
        };

        $mock::createConnection('redis://localhost?auth=bar', ['class' => $predisClass::class]);

        self::assertSame('bar', $predisClass::$captured['options']['parameters']['password'] ?? null);
    }


Review comment (security):
I like the way you made this unit-testable 👍🏼 
But let's use dataproviders and test all cases, also for the other Classes to make sure the behaviour is the same?

What I am lacking:
- redis://user:pass@host
- redis://user@pass@host?auth[]=otheruser&auth[]=otherpass 
- redis://host?auth[]=user&auth[]=pass
- redis://host?auth[username]=user&auth[password]=pass
- redis://host?auth[password]=password&auth[username]=user (does not work?)
- redis://host & auth in options array
- combination of all of the above & auth in options (show and ensure precedence)

Code after:
            use RedisTrait;
        };
        $mock::createConnection($dsn);
    }

    public static function provideInvalidDbIndexDsnParameter(): array
    {
        return [
            [
                'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/abc',
            ],
            [
                'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/3?dbindex=6',
            ],
        ];
    }

    #[DataProvider('providePredisMasterAuthResolution')]
    public function testPredisMasterAuthResolution(string $dsn, array $options, string|array|null $expectedMasterAuth)
    {
        $predisClass = $this->createPredisCaptureClass();

        $mock = new class {
            use RedisTrait;
        };

        $mock::createConnection($dsn, ['class' => $predisClass] + $options);

        $this->assertAuthMatchesExpected($expectedMasterAuth, $predisClass::$captured['options']['parameters'] ?? []);
    }

    public static function providePredisMasterAuthResolution(): \Generator
    {
        yield 'userinfo user+pass' => [
            'redis://user:pass@localhost',
            [],
            ['user', 'pass'],
        ];

        yield 'userinfo with @ + query auth array' => [
            'redis://user@pass@localhost?auth[]=otheruser&auth[]=otherpass',
            [],
            ['otheruser', 'otherpass'],
        ];

        yield 'query auth array' => [
            'redis://localhost?auth[]=user&auth[]=pass',
            [],
            ['user', 'pass'],
        ];