Skip to content

Commit

Permalink
Testing: Demonstrate Python test layer implementations
Browse files Browse the repository at this point in the history
The patch demonstrates the whole matrix of building test harnesses with
Python, using both unittest and pytest, with and without "Testcontainers
for Python".

It only exercises basic features of the corresponding test adapters.
Demonstrating advanced features like test layer parameterization can be
added on behalf of subsequent iterations.
  • Loading branch information
amotl committed Feb 10, 2024
1 parent cf2bf38 commit a4d4104
Show file tree
Hide file tree
Showing 15 changed files with 334 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,13 @@ updates:
package-ecosystem: "gradle"
schedule:
interval: "weekly"

- directory: "/testing/testcontainers/python-pytest"
package-ecosystem: "pip"
schedule:
interval: "weekly"

- directory: "/testing/testcontainers/python-unittest"
package-ecosystem: "pip"
schedule:
interval: "weekly"
66 changes: 66 additions & 0 deletions .github/workflows/testing-native-python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Native Testing with Python

on:
pull_request:
branches: ~
paths:
- '.github/workflows/testing-native-python.yml'
- 'testing/native/python**'
- 'requirements.txt'
push:
branches: [ main ]
paths:
- '.github/workflows/testing-native-python.yml'
- 'testing/native/python**'
- 'requirements.txt'

# Allow job to be triggered manually.
workflow_dispatch:

# Run job each night after CrateDB nightly has been published.
schedule:
- cron: '0 3 * * *'

# Cancel in-progress jobs when pushing to the same branch.
concurrency:
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.ref }}

jobs:
test:
name: "
Python: ${{ matrix.python-version }}
CrateDB: ${{ matrix.cratedb-version }}
on ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ 'ubuntu-latest', 'macos-latest' ]
python-version: [ '3.7', '3.11' ]
cratedb-version: [ 'nightly' ]

steps:

- name: Acquire sources
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: x64
cache: 'pip'
cache-dependency-path: |
requirements.txt
testing/native/python-pytest/requirements.txt
testing/native/python-unittest/requirements.txt
- name: Install utilities
run: |
pip install -r requirements.txt
- name: Validate testing/native/python-{pytest,unittest}
run: |
ngr test --accept-no-venv testing/native/python-pytest
ngr test --accept-no-venv testing/native/python-unittest
66 changes: 66 additions & 0 deletions .github/workflows/testing-testcontainers-python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Testcontainers for Python

on:
pull_request:
branches: ~
paths:
- '.github/workflows/testing-testcontainers-python.yml'
- 'testing/testcontainers/python**'
- 'requirements.txt'
push:
branches: [ main ]
paths:
- '.github/workflows/testing-testcontainers-python.yml'
- 'testing/testcontainers/python**'
- 'requirements.txt'

# Allow job to be triggered manually.
workflow_dispatch:

# Run job each night after CrateDB nightly has been published.
schedule:
- cron: '0 3 * * *'

# Cancel in-progress jobs when pushing to the same branch.
concurrency:
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.ref }}

jobs:
test:
name: "
Python: ${{ matrix.python-version }}
CrateDB: ${{ matrix.cratedb-version }}
on ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ 'ubuntu-latest' ]
python-version: [ '3.7', '3.12' ]
cratedb-version: [ 'nightly' ]

steps:

- name: Acquire sources
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: x64
cache: 'pip'
cache-dependency-path: |
requirements.txt
testing/testcontainers/python-pytest/requirements.txt
testing/testcontainers/python-unittest/requirements.txt
- name: Install utilities
run: |
pip install -r requirements.txt
- name: Validate testing/testcontainers/python-{pytest,unittest}
run: |
ngr test --accept-no-venv testing/testcontainers/python-pytest
ngr test --accept-no-venv testing/testcontainers/python-unittest
10 changes: 10 additions & 0 deletions testing/native/python-pytest/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[tool.pytest.ini_options]
minversion = "2.0"
addopts = """
-rsfEX -p pytester --strict-markers --verbosity=3
--capture=no
"""
log_level = "DEBUG"
log_cli_level = "DEBUG"
testpaths = ["*.py"]
xfail_strict = true
3 changes: 3 additions & 0 deletions testing/native/python-pytest/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
crash==0.31.2
pytest<9
pytest-crate==0.3.0
51 changes: 51 additions & 0 deletions testing/native/python-pytest/test_pytest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
Using "pytest-crate" with CrateDB and pytest
Build test harnesses around CrateDB using the `crate` pytest fixture
exported by `pytest-crate`. In turn, this is using `CrateNode`
exported by `cr8`.
https://pypi.org/project/pytest-crate/
https://pypi.org/project/cr8/
"""
import subprocess

SQL_STATEMENT = "SELECT * FROM sys.summits ORDER BY height DESC LIMIT 3;"


def test_crash(crate):
"""
After provisioning a test instance of CrateDB, invoke `crash`.
"""
http_url = crate.dsn()
command = f"time crash --hosts '{http_url}' --command 'SELECT * FROM sys.summits ORDER BY height DESC LIMIT 3;'"
subprocess.check_call(command, shell=True)


def test_crate(crate):
assert crate.dsn().startswith("http://127.0.0.1:42")
assert "http" in crate.addresses
assert crate.addresses["http"].host == "127.0.0.1"
assert 4300 > crate.addresses["http"].port >= 4200
assert "psql" in crate.addresses
assert crate.addresses["psql"].host == "127.0.0.1"
assert 5500 > crate.addresses["psql"].port >= 5432
assert "transport" in crate.addresses
assert crate.addresses["transport"].host == "127.0.0.1"
assert 4400 > crate.addresses["transport"].port >= 4300


def test_cursor(crate_cursor):
crate_cursor.execute("SELECT 1")
assert crate_cursor.fetchone() == [1]


def test_execute(crate_execute, crate_cursor):
for stmt in [
"CREATE TABLE pytest (name STRING, version INT)",
"INSERT INTO pytest (name, version) VALUES ('test_execute', 1)",
"REFRESH TABLE pytest",
]:
crate_execute(stmt)
crate_cursor.execute("SELECT name, version FROM pytest")
assert crate_cursor.fetchall() == [["test_execute", 1]]
1 change: 1 addition & 0 deletions testing/native/python-unittest/.ngr-type
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python-unittest
2 changes: 2 additions & 0 deletions testing/native/python-unittest/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cr8==0.25.0
crash==0.31.2
37 changes: 37 additions & 0 deletions testing/native/python-unittest/test_unittest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Using "cr8" with CrateDB and unittest
Build test harnesses around CrateDB using the `create_node`
primitive exported by `cr8`.
https://pypi.org/project/cr8/
"""
import subprocess
from unittest import TestCase

from cr8.run_crate import create_node

# Run a testing instance of CrateDB.
# TODO: How to select a nightly release?
cratedb_layer = create_node(version="latest-testing")

SQL_STATEMENT = "SELECT * FROM sys.summits ORDER BY height DESC LIMIT 3;"


def setUpModule():
cratedb_layer.start()


def tearDownModule():
cratedb_layer.stop()


class CrashTest(TestCase):

def test_crash(self):
"""
After provisioning a test instance of CrateDB, invoke `crash`.
"""
http_url = cratedb_layer.http_url
command = f"time crash --hosts '{http_url}' --command '{SQL_STATEMENT}'"
subprocess.check_call(command, shell=True)
10 changes: 10 additions & 0 deletions testing/testcontainers/python-pytest/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[tool.pytest.ini_options]
minversion = "2.0"
addopts = """
-rsfEX -p pytester --strict-markers --verbosity=3
--capture=no
"""
log_level = "DEBUG"
log_cli_level = "DEBUG"
testpaths = ["*.py"]
xfail_strict = true
3 changes: 3 additions & 0 deletions testing/testcontainers/python-pytest/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
crash==0.31.2
cratedb-toolkit[testing]==0.0.4
pytest<9
29 changes: 29 additions & 0 deletions testing/testcontainers/python-pytest/test_pytest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
Using "Testcontainers for Python" with CrateDB and pytest
Build test harnesses around CrateDB using the `cratedb_service`
pytest fixture exported by `cratedb-toolkit`.
https://pypi.org/project/cratedb-toolkit/
"""
import subprocess
from pprint import pprint

SQL_STATEMENT = "SELECT * FROM sys.summits ORDER BY height DESC LIMIT 3;"


def test_crash(cratedb_service):
"""
After provisioning a test instance of CrateDB, invoke `crash`.
"""
http_url = cratedb_service.get_http_url()
command = f"time crash --hosts '{http_url}' --command '{SQL_STATEMENT}'"
subprocess.check_call(command, shell=True)


def test_sql(cratedb_service):
"""
After provisioning a test instance of CrateDB, invoke an SQL statement.
"""
results = cratedb_service.database.run_sql(SQL_STATEMENT, records=True)
pprint(results)
1 change: 1 addition & 0 deletions testing/testcontainers/python-unittest/.ngr-type
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python-unittest
2 changes: 2 additions & 0 deletions testing/testcontainers/python-unittest/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
crash==0.31.2
cratedb-toolkit[testing]==0.0.4
43 changes: 43 additions & 0 deletions testing/testcontainers/python-unittest/test_unittest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
Using "Testcontainers for Python" with CrateDB and unittest
Build test harnesses around CrateDB using the `CrateDBTestAdapter`
exported by `cratedb-toolkit`.
https://pypi.org/project/cratedb-toolkit/
"""
import subprocess
from pprint import pprint
from unittest import TestCase

from cratedb_toolkit.testing.testcontainers.cratedb import CrateDBTestAdapter

cratedb_layer = CrateDBTestAdapter()

SQL_STATEMENT = "SELECT * FROM sys.summits ORDER BY height DESC LIMIT 3;"


def setUpModule():
cratedb_layer.start()


def tearDownModule():
cratedb_layer.stop()


class CrashTest(TestCase):

def test_crash(self):
"""
After provisioning a test instance of CrateDB, invoke `crash`.
"""
http_url = cratedb_layer.get_http_url()
command = f"time crash --hosts '{http_url}' --command '{SQL_STATEMENT}'"
subprocess.check_call(command, shell=True)

def test_sql(self):
"""
After provisioning a test instance of CrateDB, invoke an SQL statement.
"""
results = cratedb_layer.database.run_sql(SQL_STATEMENT, records=True)
pprint(results)

0 comments on commit a4d4104

Please sign in to comment.