Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DynamoDB: Add integration test for full-load operation #42

Merged
merged 2 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ jobs:
pip install --use-pep517 --prefer-binary --editable=.[develop,test]

- name: Run linters and software tests
run: poe check
run: |
poe lint
poe test -- -m 'not (dynamodb or mongodb)'

# https://github.com/codecov/codecov-action
- name: Upload coverage results to Codecov
Expand All @@ -67,6 +69,61 @@ jobs:
fail_ci_if_error: true


test-dynamodb:
name: "
DynamoDB: Python ${{ matrix.python-version }}
"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: ['ubuntu-latest']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

env:
OS: ${{ matrix.os }}
PYTHON: ${{ matrix.python-version }}

steps:

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

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: x64
cache: 'pip'
cache-dependency-path:
pyproject.toml

- name: Set up project
run: |

# `setuptools 0.64.0` adds support for editable install hooks (PEP 660).
# https://github.com/pypa/setuptools/blob/main/CHANGES.rst#v6400
pip install "setuptools>=64" --upgrade

# Install package in editable mode.
pip install --use-pep517 --prefer-binary --editable=.[mongodb,develop,test]

- name: Run linters and software tests
run: poe test -- -m 'dynamodb'

# https://github.com/codecov/codecov-action
- name: Upload coverage results to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
files: ./coverage.xml
flags: dynamodb
env_vars: OS,PYTHON
name: codecov-umbrella
fail_ci_if_error: true


test-mongodb:
name: "
MongoDB: Python ${{ matrix.python-version }}
Expand Down Expand Up @@ -107,7 +164,7 @@ jobs:
pip install --use-pep517 --prefer-binary --editable=.[mongodb,develop,test]

- name: Run linters and software tests
run: poe check
run: poe test -- -m 'mongodb'

# https://github.com/codecov/codecov-action
- name: Upload coverage results to Codecov
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# commons-codec

[![Tests](https://github.com/daq-tools/commons-codec/actions/workflows/tests.yml/badge.svg)](https://github.com/daq-tools/commons-codec/actions/workflows/tests.yml)
[![Coverage](https://codecov.io/gh/daq-tools/commons-codec/branch/main/graph/badge.svg)](https://app.codecov.io/gh/daq-tools/commons-codec)
[![Tests](https://github.com/crate/commons-codec/actions/workflows/tests.yml/badge.svg)](https://github.com/crate/commons-codec/actions/workflows/tests.yml)
[![Coverage](https://codecov.io/gh/crate/commons-codec/branch/main/graph/badge.svg)](https://app.codecov.io/gh/crate/commons-codec)
[![Build status (documentation)](https://readthedocs.org/projects/commons-codec/badge/)](https://cratedb.com/docs/commons-codec/)
[![PyPI Version](https://img.shields.io/pypi/v/commons-codec.svg)](https://pypi.org/project/commons-codec/)
[![Python Version](https://img.shields.io/pypi/pyversions/commons-codec.svg)](https://pypi.org/project/commons-codec/)
Expand Down Expand Up @@ -75,8 +75,8 @@ within the header sections of relevant files.
[commons-codec]: https://pypi.org/project/commons-codec/
[Zyp research]: https://commons-codec.readthedocs.io/zyp/research.html
[documentation]: https://commons-codec.readthedocs.io/
[examples]: https://github.com/daq-tools/commons-codec/tree/main/examples
[managed on GitHub]: https://github.com/daq-tools/commons-codec
[examples]: https://github.com/crate/commons-codec/tree/main/examples
[managed on GitHub]: https://github.com/crate/commons-codec
[prior art]: https://commons-codec.readthedocs.io/prior-art.html
[PyPI]: https://pypi.org/
[Zyp]: https://commons-codec.readthedocs.io/zyp/
7 changes: 6 additions & 1 deletion doc/development.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Development Sandbox

Acquire source code, install development sandbox, and invoke software tests.
Acquire source code, and install development sandbox.
```shell
git clone https://github.com/daq-tools/commons-codec
cd commons-codec
python3 -m venv .venv
source .venv/bin/activate
pip install --editable='.[all,develop,doc,test]'
```

Invoke software tests.
```
export TC_KEEPALIVE=true
poe check
```

Expand Down
15 changes: 12 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ optional-dependencies.release = [
"twine<6",
]
optional-dependencies.test = [
"cratedb-toolkit[testing]",
"pytest<9",
"pytest-cov<6",
"pytest-mock<4",
Expand Down Expand Up @@ -245,6 +246,8 @@ python_files = [
]
xfail_strict = true
markers = [
"dynamodb",
"mongodb",
"tasmota",
"wemos",
]
Expand Down Expand Up @@ -329,6 +332,12 @@ release = [
{ cmd = "twine upload --skip-existing dist/*" },
]

test = [
{ cmd = "pytest" },
]
[tool.poe.tasks.test]
cmd = "pytest"
help = "Invoke software tests"

[tool.poe.tasks.test.args.expression]
options = [ "-k" ]

[tool.poe.tasks.test.args.marker]
options = [ "-m" ]
14 changes: 14 additions & 0 deletions tests/transform/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pytest

RESET_TABLES = [
"from.dynamodb",
]


@pytest.fixture(scope="function")
def cratedb(cratedb_service):
"""
Provide a fresh canvas to each test case invocation, by resetting database content.
"""
cratedb_service.reset(tables=RESET_TABLES)
yield cratedb_service
3 changes: 3 additions & 0 deletions tests/transform/test_dynamodb_cdc.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
from commons_codec.model import DualRecord, SQLOperation
from commons_codec.transform.dynamodb import CrateDBTypeDeserializer, DynamoDBCDCTranslator

pytestmark = pytest.mark.dynamodb


READING_BASIC = {"device": "foo", "temperature": 42.42, "humidity": 84.84}

MSG_UNKNOWN_SOURCE = {
Expand Down
104 changes: 69 additions & 35 deletions tests/transform/test_dynamodb_full.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import pytest

from commons_codec.model import SQLOperation
from commons_codec.transform.dynamodb import DynamoDBFullLoadTranslator

RECORD_ALL_TYPES = {
pytestmark = pytest.mark.dynamodb

RECORD_IN = {
"id": {"S": "5F9E-Fsadd41C-4C92-A8C1-70BF3FFB9266"},
"data": {"M": {"temperature": {"N": "42.42"}, "humidity": {"N": "84.84"}}},
"meta": {"M": {"timestamp": {"S": "2024-07-12T01:17:42"}, "device": {"S": "foo"}}},
Expand Down Expand Up @@ -37,6 +41,40 @@
"set_of_strings": {"SS": ["location_1"]},
}

RECORD_OUT_DATA = {
"id": "5F9E-Fsadd41C-4C92-A8C1-70BF3FFB9266",
"data": {"temperature": 42.42, "humidity": 84.84},
"meta": {"timestamp": "2024-07-12T01:17:42", "device": "foo"},
"location": {
"address": "Berchtesgaden Salt Mine",
"coordinates": [
"",
],
"meetingPoint": "At the end of the tunnel",
},
"list_of_objects": [
{
"date": "2024-08-28T20:05:42.603Z",
"utm_adgroup": ["", ""],
"utm_campaign": "34374686341",
"utm_medium": "foobar",
"utm_source": "google",
}
],
"map_of_numbers": {"test": 1.0, "test2": 2.0},
"set_of_binaries": ["U3Vubnk="],
"set_of_numbers": [0.34, 1.0, 2.0, 3.0],
"set_of_strings": ["location_1"],
}

RECORD_OUT_AUX = {
"list_of_varied": [
{"a": 1.0},
2.0,
"Three",
],
}


def test_sql_ddl():
assert (
Expand All @@ -45,41 +83,37 @@ def test_sql_ddl():
)


def test_to_sql_all_types():
assert DynamoDBFullLoadTranslator(table_name="foo").to_sql(RECORD_ALL_TYPES) == SQLOperation(
def test_to_sql_operation():
"""
Verify outcome of `DynamoDBFullLoadTranslator.to_sql` operation.
"""
assert DynamoDBFullLoadTranslator(table_name="foo").to_sql(RECORD_IN) == SQLOperation(
statement="INSERT INTO foo (data, aux) VALUES (:typed, :untyped);",
parameters={
"typed": {
"id": "5F9E-Fsadd41C-4C92-A8C1-70BF3FFB9266",
"data": {"temperature": 42.42, "humidity": 84.84},
"meta": {"timestamp": "2024-07-12T01:17:42", "device": "foo"},
"location": {
"address": "Berchtesgaden Salt Mine",
"coordinates": [
"",
],
"meetingPoint": "At the end of the tunnel",
},
"list_of_objects": [
{
"date": "2024-08-28T20:05:42.603Z",
"utm_adgroup": ["", ""],
"utm_campaign": "34374686341",
"utm_medium": "foobar",
"utm_source": "google",
}
],
"map_of_numbers": {"test": 1.0, "test2": 2.0},
"set_of_binaries": ["U3Vubnk="],
"set_of_numbers": [0.34, 1.0, 2.0, 3.0],
"set_of_strings": ["location_1"],
},
"untyped": {
"list_of_varied": [
{"a": 1.0},
2.0,
"Three",
],
},
"typed": RECORD_OUT_DATA,
"untyped": RECORD_OUT_AUX,
},
)


def test_to_sql_cratedb(caplog, cratedb):
"""
Verify writing converted DynamoDB record to CrateDB.
"""

# Compute CrateDB operation (SQL+parameters) from DynamoDB record.
translator = DynamoDBFullLoadTranslator(table_name="from.dynamodb")
operation = translator.to_sql(record=RECORD_IN)

# Insert into CrateDB.
cratedb.database.run_sql(translator.sql_ddl)
cratedb.database.run_sql(operation.statement, operation.parameters)

# Verify data in target database.
assert cratedb.database.table_exists("from.dynamodb") is True
assert cratedb.database.refresh_table("from.dynamodb") is True
assert cratedb.database.count_records("from.dynamodb") == 1

results = cratedb.database.run_sql('SELECT * FROM "from".dynamodb;', records=True) # noqa: S608
assert results[0]["data"] == RECORD_OUT_DATA
assert results[0]["aux"] == RECORD_OUT_AUX
4 changes: 4 additions & 0 deletions tests/transform/test_dynamodb_types_cratedb.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import unittest
from decimal import Decimal

import pytest

from commons_codec.model import DualRecord
from commons_codec.transform.dynamodb import CrateDBTypeDeserializer, DynamoDBCDCTranslator

pytestmark = pytest.mark.dynamodb


class TestDeserializer(unittest.TestCase):
def setUp(self):
Expand Down
2 changes: 2 additions & 0 deletions tests/transform/test_dynamodb_types_vanilla.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

from commons_codec.vendor.boto3.dynamodb.types import Binary, TypeDeserializer

pytestmark = pytest.mark.dynamodb


class TestBinary(unittest.TestCase):
def test_bytes_input(self):
Expand Down
6 changes: 4 additions & 2 deletions tests/transform/test_mongodb.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# ruff: noqa: E402, E501
import datetime

import pytest

pytestmark = pytest.mark.mongodb

import datetime

from commons_codec.model import SQLOperation

pytest.importorskip("pymongo")
Expand Down