Skip to content

Commit 5d2616a

Browse files
committed
Add more tests and exposed_ports
1 parent fbc47db commit 5d2616a

File tree

5 files changed

+190
-23
lines changed

5 files changed

+190
-23
lines changed

modules/cratedb/README.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.. autoclass:: testcontainers.cratedb.CrateDBContainer
2+
.. title:: testcontainers.cratedb.CrateDBContainer

modules/cratedb/example_basic.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import sqlalchemy
2+
3+
from testcontainers import cratedb
4+
5+
with cratedb.CrateDBContainer("crate:latest", ports={4200: None, 5432: None}) as container:
6+
engine = sqlalchemy.create_engine(container.get_connection_url())
7+
with engine.begin() as conn:
8+
result = conn.execute(sqlalchemy.text("select version()"))
9+
version = result.fetchone()

modules/cratedb/testcontainers/cratedb/__init__.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,19 @@ class CrateDBContainer(DockerContainer):
3333
3434
.. doctest::
3535
36-
>>> from cratedb_toolkit.testing.testcontainers.cratedb import CrateDBContainer
36+
>>> from testcontainers import cratedb import CrateDBContainer
3737
>>> import sqlalchemy
3838
39-
>>> cratedb_container = CrateDBContainer("crate:5.2.3")
40-
>>> cratedb_container.start()
41-
>>> with cratedb_container as cratedb:
39+
>>> cratedb_container =
40+
>>> with CrateDBContainer("crate:6.0") as cratedb:
4241
... engine = sqlalchemy.create_engine(cratedb.get_connection_url())
4342
... with engine.begin() as connection:
4443
... result = connection.execute(sqlalchemy.text("select version()"))
4544
... version, = result.fetchone()
4645
>>> version
47-
'CrateDB 5.2.3...'
46+
'CrateDB 6.0.2..'
4847
"""
4948

50-
CRATEDB_USER = os.environ.get("CRATEDB_USER", "crate")
51-
CRATEDB_PASSWORD = os.environ.get("CRATEDB_PASSWORD", "crate")
52-
CRATEDB_DB = os.environ.get("CRATEDB_DB", "doc")
53-
KEEPALIVE = asbool(os.environ.get("CRATEDB_KEEPALIVE", os.environ.get("TC_KEEPALIVE", False)))
5449
CMD_OPTS: t.ClassVar[dict[str, str]] = {
5550
"discovery.type": "single-node",
5651
"node.attr.storage": "hot",
@@ -82,21 +77,30 @@ def __init__(
8277
:param kwargs: misc keyword arguments
8378
"""
8479
super().__init__(image=image, **kwargs)
85-
86-
self._name = "testcontainers-cratedb"
87-
8880
cmd_opts = cmd_opts or {}
8981
self._command = self._build_cmd({**self.CMD_OPTS, **cmd_opts})
9082

91-
self.CRATEDB_USER = user or self.CRATEDB_USER
92-
self.CRATEDB_PASSWORD = password or self.CRATEDB_PASSWORD
93-
self.CRATEDB_DB = dbname or self.CRATEDB_DB
83+
self.CRATEDB_USER = user or os.environ.get("CRATEDB_USER", "crate")
84+
self.CRATEDB_PASSWORD = password or os.environ.get("CRATEDB_PASSWORD", "crate")
85+
self.CRATEDB_DB = dbname or os.environ.get("CRATEDB_DB", "doc")
9486

9587
self.port_mapping = ports if ports else {4200: None}
96-
self.port_to_expose, _ = next(iter(self.port_mapping.items()))
88+
self.port_to_expose = next(iter(self.port_mapping.items()))
9789

9890
self.waiting_for(HttpWaitStrategy(4200).for_status_code(200).with_startup_timeout(5))
9991

92+
def exposed_ports(self) -> dict[int, int]:
93+
"""Returns a dictionary with the ports that are currently exposed in the container.
94+
95+
Contrary to the '--port' parameter used in docker cli, this returns {internal_port: external_port}
96+
97+
Examples:
98+
{4200: 19382}
99+
100+
:returns: The exposed ports.
101+
"""
102+
return {port: self.get_exposed_port(port) for port in self.ports}
103+
100104
@staticmethod
101105
def _build_cmd(opts: dict) -> str:
102106
"""
@@ -127,21 +131,21 @@ def _configure(self) -> None:
127131
self._configure_credentials()
128132

129133
def get_connection_url(self, dialect: str = "crate", host: t.Optional[str] = None) -> str:
134+
# We should remove this method once the new DBContainer generic gets added to the library.
130135
"""
131136
Return a connection URL to the DB
132137
133138
:param host: optional string
134139
:param dialect: a string with the dialect name to generate a DB URI
135140
:return: string containing a connection URL to te DB
136141
"""
137-
# TODO: When using `db_name=self.CRATEDB_DB`:
138-
# Connection.__init__() got an unexpected keyword argument 'database'
139142
return self._create_connection_url(
140143
dialect=dialect,
141144
username=self.CRATEDB_USER,
142145
password=self.CRATEDB_PASSWORD,
143146
host=host,
144-
port=self.port_to_expose,
147+
port=self.port_to_expose[0],
148+
dbname=self.CRATEDB_DB,
145149
)
146150

147151
def _create_connection_url(
@@ -156,12 +160,16 @@ def _create_connection_url(
156160
) -> str:
157161
if raise_for_deprecated_parameter(kwargs, "db_name", "dbname"):
158162
raise ValueError(f"Unexpected arguments: {','.join(kwargs)}")
163+
159164
if self._container is None:
160165
raise ContainerStartException("container has not been started")
166+
161167
host = host or self.get_container_host_ip()
162168
assert port is not None
169+
163170
port = self.get_exposed_port(port)
164171
quoted_password = quote(password, safe=" +")
172+
165173
url = f"{dialect}://{username}:{quoted_password}@{host}:{port}"
166174
if dbname:
167175
url = f"{url}/{dbname}"

modules/cratedb/tests/test_cratedb.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
import urllib.parse
2+
import os
3+
14
import sqlalchemy
25
import pytest
6+
37
from testcontainers.cratedb import CrateDBContainer
48

59

610
@pytest.mark.parametrize("version", ["5.9", "5.10", "6.0", "latest"])
7-
def test_docker_run_cratedb(version: str):
11+
def test_docker_run_cratedb_versions(version: str):
812
with CrateDBContainer(f"crate:{version}") as container:
913
engine = sqlalchemy.create_engine(container.get_connection_url())
1014
with engine.begin() as conn:
@@ -13,6 +17,49 @@ def test_docker_run_cratedb(version: str):
1317
assert sum_result == 15
1418

1519

20+
@pytest.mark.parametrize(
21+
"ports, expected",
22+
[
23+
({5432: None, 4200: None}, False),
24+
({5432: 5432, 4200: 4200}, {5432: 5432, 4200: 4200}),
25+
],
26+
)
27+
def test_docker_run_cratedb_ports(ports, expected):
28+
with CrateDBContainer("crate:latest", ports=ports) as container:
29+
exposed_ports = container.exposed_ports()
30+
assert len(exposed_ports) == 2
31+
assert all(map(lambda port: isinstance(port, int), exposed_ports))
32+
if expected:
33+
assert exposed_ports == expected
34+
35+
36+
def test_docker_run_cratedb_credentials():
37+
expected_user, expected_password, expected_db, expected_port = "user1", "pass1", "host1", 4200
38+
expected_default_dialect, expected_default_host = "crate", "localhost"
39+
expected_defined_dialect, expected_defined_host = "somedialect", "somehost"
40+
os.environ["CRATEDB_USER"], os.environ["CRATEDB_PASSWORD"] = expected_user, expected_password
41+
os.environ["CRATEDB_DB"] = expected_db
42+
43+
with CrateDBContainer("crate:latest", ports={4200: expected_port}) as container:
44+
url = urllib.parse.urlparse(container.get_connection_url())
45+
user, password = url.netloc.split("@")[0].split(":")
46+
host, port = url.netloc.split("@")[1].split(":")
47+
assert user == expected_user
48+
assert password == expected_password
49+
assert url.scheme == expected_default_dialect
50+
assert host == expected_default_host
51+
assert int(port) == expected_port
52+
assert url.path.replace("/", "") == expected_db
53+
54+
url = urllib.parse.urlparse(
55+
container.get_connection_url(dialect=expected_defined_dialect, host=expected_defined_host)
56+
)
57+
host, _ = url.netloc.split("@")[1].split(":")
58+
59+
assert url.scheme == expected_defined_dialect
60+
assert host == expected_defined_host
61+
62+
1663
@pytest.mark.parametrize(
1764
"opts, expected",
1865
[

poetry.lock

Lines changed: 104 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)