testcontainers / testcontainers-python

Testcontainers is a Python library that providing a friendly API to run Docker container. It is designed to create runtime environment to use during your automatic tests.
https://testcontainers-python.readthedocs.io/en/latest/
Apache License 2.0
1.51k stars 281 forks source link

Bug: PostgresContainer().get_connection_url(driver=None) does not function as documented #587

Closed oliverlambson closed 2 months ago

oliverlambson commented 3 months ago

Describe the bug

get_connection_url(driver=None) should return a connection string starting postgresql://..., but actually returns postgresql+None://...

This is not inline with the documented intention:

        If a driver is set in the constructor (defaults to psycopg2!), the URL will contain the
        driver. The optional driver argument to :code:`get_connection_url` overwrites the constructor
        set value. Pass :code:`driver=None` to get URLs without a driver.

To Reproduce

$ python <<EOF
from testcontainers.postgres import PostgresContainer
postgres = PostgresContainer("postgres:16")
postgres.start()
print(f"{postgres.get_connection_url(driver=None)=}")
EOF

WARNING:root:DOCKER_AUTH_CONFIG is experimental, see testcontainers/testcontainers-python#566
Pulling image testcontainers/ryuk:0.7.0
INFO:testcontainers.core.container:Pulling image testcontainers/ryuk:0.7.0
Container started: 2d9d0ae2c1f5
INFO:testcontainers.core.container:Container started: 2d9d0ae2c1f5
Waiting for container <Container: 2d9d0ae2c1f5> with image testcontainers/ryuk:0.7.0 to be ready ...
INFO:testcontainers.core.waiting_utils:Waiting for container <Container: 2d9d0ae2c1f5> with image testcontainers/ryuk:0.7.0 to be ready ...
Pulling image postgres:16
INFO:testcontainers.core.container:Pulling image postgres:16
Container started: 2dba2ba8e9ec
INFO:testcontainers.core.container:Container started: 2dba2ba8e9ec
Waiting for container <Container: 2dba2ba8e9ec> with image postgres:16 to be ready ...
INFO:testcontainers.core.waiting_utils:Waiting for container <Container: 2dba2ba8e9ec> with image postgres:16 to be ready ...
Waiting for container <Container: 2dba2ba8e9ec> with image postgres:16 to be ready ...
INFO:testcontainers.core.waiting_utils:Waiting for container <Container: 2dba2ba8e9ec> with image postgres:16 to be ready ...
postgres.get_connection_url(driver=None)='postgresql+None://test:test@localhost:32776/test'

It does work if driver=None is passed to the constructor. I find this unintuitive.

python <<EOF 
from testcontainers.postgres import PostgresContainer
postgres = PostgresContainer("postgres:16", driver=None)
postgres.start()
print(f"{postgres.get_connection_url()=}")
EOF

WARNING:root:DOCKER_AUTH_CONFIG is experimental, see testcontainers/testcontainers-python#566
Pulling image testcontainers/ryuk:0.7.0
INFO:testcontainers.core.container:Pulling image testcontainers/ryuk:0.7.0
Container started: 4a0e020f0691
INFO:testcontainers.core.container:Container started: 4a0e020f0691
Waiting for container <Container: 4a0e020f0691> with image testcontainers/ryuk:0.7.0 to be ready ...
INFO:testcontainers.core.waiting_utils:Waiting for container <Container: 4a0e020f0691> with image testcontainers/ryuk:0.7.0 to be ready ...
Pulling image postgres:16
INFO:testcontainers.core.container:Pulling image postgres:16
Container started: ce239a341dab
INFO:testcontainers.core.container:Container started: ce239a341dab
Waiting for container <Container: ce239a341dab> with image postgres:16 to be ready ...
INFO:testcontainers.core.waiting_utils:Waiting for container <Container: ce239a341dab> with image postgres:16 to be ready ...
Waiting for container <Container: ce239a341dab> with image postgres:16 to be ready ...
INFO:testcontainers.core.waiting_utils:Waiting for container <Container: ce239a341dab> with image postgres:16 to be ready ...
postgres.get_connection_url()='postgresql://test:test@localhost:32776/test'

Runtime environment

Provide a summary of your runtime environment. Which operating system, python version, and docker version are you using? What is the version of testcontainers-python you are using? You can run the following commands to get the relevant information.

# Get the operating system information (on a unix os).
$ uname -a
Darwin ip-... 23.4.0 Darwin Kernel Version 23.4.0: Fri Mar 15 00:11:08 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T8122 arm64

# Get the python version.
$ python --version
Python 3.11.9

# Get the docker version and other docker information.
$ docker info
Client:
 Version:    25.0.5
 Context:    orbstack
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.13.1
    Path:     /Users/oliverlambson/.docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.24.5
    Path:     /Users/oliverlambson/.docker/cli-plugins/docker-compose

# Get all python packages.
$ pip freeze
certifi==2024.2.2
charset-normalizer==3.3.2
docker==7.1.0
idna==3.7
requests==2.32.2
testcontainers==4.5.0
typing_extensions==4.12.0
urllib3==2.2.1
wrapt==1.16.0

This is either a documentation issue (it does work if driver=None is passed to the constructor) or it can be handled in code, so that the driver can be overridden by the method in this line:

-         driver_str = self.driver if driver is _UNSET else f"+{driver}"
+         driver_str = f"+{driver}" if driver not in [_UNSET, None] else self.driver