Skip to content

Commit 4c2f446

Browse files
authored
Adding some unit tests for listmonk sync (#52)
Signed-off-by: Phil Dibowitz <phil@ipom.com>
1 parent 3ed5ac1 commit 4c2f446

File tree

10 files changed

+878
-4
lines changed

10 files changed

+878
-4
lines changed

.github/workflows/unit.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: unit tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- master
8+
pull_request:
9+
10+
jobs:
11+
detect-changes:
12+
runs-on: ubuntu-latest
13+
outputs:
14+
listmonk: ${{ steps.filter.outputs.listmonk }}
15+
guidebook: ${{ steps.filter.outputs.guidebook }}
16+
steps:
17+
- uses: actions/checkout@v6
18+
- uses: dorny/paths-filter@v3
19+
id: filter
20+
with:
21+
filters: |
22+
listmonk:
23+
- 'listmonk/**'
24+
guidebook:
25+
- 'guidebook/**'
26+
27+
test-listmonk:
28+
needs: detect-changes
29+
if: needs.detect-changes.outputs.listmonk == 'true'
30+
runs-on: ubuntu-latest
31+
steps:
32+
- uses: actions/checkout@v6
33+
34+
- name: Set up Python
35+
uses: actions/setup-python@v5
36+
with:
37+
python-version: '3.11'
38+
39+
- name: Cache pip dependencies
40+
uses: actions/cache@v4
41+
with:
42+
path: ~/.cache/pip
43+
key: ${{ runner.os }}-pip-listmonk-${{ hashFiles('listmonk/requirements.txt', 'listmonk/requirements-test.txt') }}
44+
restore-keys: |
45+
${{ runner.os }}-pip-listmonk-
46+
47+
- name: Install dependencies
48+
run: |
49+
cd listmonk
50+
pip install -r requirements.txt
51+
pip install -r requirements-test.txt
52+
53+
- name: Run tests
54+
run: |
55+
cd listmonk
56+
pytest -v --tb=short
57+
58+
test-guidebook:
59+
needs: detect-changes
60+
if: needs.detect-changes.outputs.guidebook == 'true'
61+
runs-on: ubuntu-latest
62+
steps:
63+
- uses: actions/checkout@v6
64+
65+
- name: Set up Python
66+
uses: actions/setup-python@v5
67+
with:
68+
python-version: '3.11'
69+
70+
- name: Cache pip dependencies
71+
uses: actions/cache@v4
72+
with:
73+
path: ~/.cache/pip
74+
key: ${{ runner.os }}-pip-guidebook-${{ hashFiles('guidebook/requirements.txt', 'guidebook/requirements-test.txt') }}
75+
restore-keys: |
76+
${{ runner.os }}-pip-guidebook-
77+
78+
- name: Install dependencies
79+
run: |
80+
cd guidebook
81+
pip install -r requirements.txt
82+
# Install test dependencies if they exist
83+
if [ -f requirements-test.txt ]; then
84+
pip install -r requirements-test.txt
85+
fi
86+
87+
- name: Run tests
88+
run: |
89+
cd guidebook
90+
# Run tests if they exist
91+
if [ -d tests ] || ls test_*.py 2>/dev/null; then
92+
pytest -v --tb=short || echo "No tests found or pytest not configured"
93+
else
94+
echo "No tests directory found yet - skipping tests"
95+
fi

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.vscode
2+
__pycache__

.sugarjar.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
on_push: [lint]
2+
lint:
3+
- black -l 80 .

listmonk/pytest.ini

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[pytest]
2+
testpaths = tests
3+
python_files = test_*.py
4+
python_classes = Test*
5+
python_functions = test_*
6+
addopts =
7+
-v
8+
--tb=short
9+
--strict-markers
10+
markers =
11+
slow: marks tests as slow (deselect with '-m "not slow"')
12+
integration: marks tests as integration tests

listmonk/requirements-test.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pytest>=7.4.0
2+
pytest-cov>=4.1.0
3+
pytest-mock>=3.11.1

listmonk/requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
click>=8.1.0
2+
mysqlclient>=2.2.0
3+
requests>=2.31.0
4+
pyyaml>=6.0
5+
datadog-api-client>=2.0.0

listmonk/scale_email_sync.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ def get_csv_data(self, csv_url):
4444
reader = csv.DictReader(data.splitlines())
4545
subscribers = {}
4646
for row in reader:
47-
subscribers[row["email"]] = {
47+
email_lc = row["email"].lower()
48+
subscribers[email_lc] = {
4849
"id": row["id"],
49-
"email": row["email"].lower(),
50+
"email": email_lc,
5051
"can_email": int(row["can_email"]),
5152
}
5253
return subscribers
@@ -71,7 +72,7 @@ def get_db_data(self):
7172
"""
7273
)
7374
subscribers = {
74-
row[0]: {
75+
row[0].lower(): {
7576
"email": row[0].lower(),
7677
"name": row[1],
7778
"can_email": row[2],
@@ -185,7 +186,9 @@ def get_all_subscribers(self):
185186
return subscribers
186187

187188
def list_ids_to_names(self, list_ids):
188-
return ", ".join([self.list_ids.get(lid, str(lid)) for lid in list_ids])
189+
return ", ".join(
190+
[self.list_names.get(lid, str(lid)) for lid in list_ids]
191+
)
189192

190193
def add_subscriber(self, email, lists):
191194
logging.debug(

listmonk/tests/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Tests for scale_email_sync
2+
3+
This directory contains tests for the `scale_email_sync.py` script.
4+
5+
## Running Tests
6+
7+
### Install test dependencies
8+
9+
```bash
10+
pip install -r requirements-test.txt
11+
```
12+
13+
### Run all tests
14+
15+
```bash
16+
pytest
17+
```
18+
19+
### Run with coverage
20+
21+
```bash
22+
pytest --cov=scale_email_sync --cov-report=html
23+
```
24+
25+
### Run specific test file
26+
27+
```bash
28+
pytest tests/test_scale_email_sync.py
29+
```
30+
31+
### Run specific test class or function
32+
33+
```bash
34+
pytest tests/test_scale_email_sync.py::TestListMonk
35+
pytest tests/test_scale_email_sync.py::TestListMonk::test_add_subscriber_new
36+
```
37+
38+
### Run with verbose output
39+
40+
```bash
41+
pytest -v
42+
```
43+
44+
## Test Structure
45+
46+
- `test_scale_email_sync.py` - Main test file containing:
47+
- `TestLoadConfig` - Tests for configuration loading
48+
- `TestRegData` - Tests for RegData class (CSV and database operations)
49+
- `TestListMonk` - Tests for ListMonk class (API interactions,
50+
synchronization)
51+
- `TestIntegration` - Integration tests for the full workflow
52+
53+
## Coverage
54+
55+
The tests aim to cover:
56+
57+
- Configuration file loading
58+
- CSV data parsing
59+
- Database data fetching
60+
- Listmonk API interactions (GET, POST, PUT)
61+
- Subscriber synchronization logic
62+
- List management (expected, missing, extra lists)
63+
- Dry-run mode
64+
- Stats tracking and Datadog reporting
65+
- Error handling

listmonk/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Tests for scale_email_sync"""

0 commit comments

Comments
 (0)