Skip to content

Commit 134b13a

Browse files
committed
feat: add comprehensive unit tests and clean up CI
- Remove debug steps from CI workflow now that issues are resolved - Add HTTP client tests (5 tests) - Add file handler tests (5 tests) - Add builder API tests (5 tests) - Remove temporary test_basic.py file - Total: 31 unit tests covering all major components
1 parent f502c75 commit 134b13a

File tree

5 files changed

+164
-57
lines changed

5 files changed

+164
-57
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -34,44 +34,6 @@ jobs:
3434
python -m pip install --upgrade pip
3535
pip install -e ".[dev]"
3636
37-
- name: Debug - Check environment
38-
run: |
39-
echo "Python version:"
40-
python --version
41-
echo "Python path:"
42-
which python
43-
echo "Site packages:"
44-
python -c "import site; print(site.getsitepackages())"
45-
echo "Current directory:"
46-
pwd
47-
echo "Directory contents:"
48-
ls -la
49-
echo "Installed packages:"
50-
pip list
51-
52-
- name: Debug - Test package import
53-
run: |
54-
echo "Testing direct import:"
55-
python -c "import nutrient_dws; print('Import successful:', nutrient_dws.__version__)"
56-
echo "Testing submodule imports:"
57-
python -c "from nutrient_dws.client import NutrientClient; print('Client import successful')"
58-
python -c "from nutrient_dws.exceptions import NutrientError; print('Exceptions import successful')"
59-
60-
- name: Debug - Check test structure
61-
run: |
62-
echo "Test directory structure:"
63-
find tests -name "*.py" -type f
64-
echo "Test __init__ files:"
65-
find tests -name "__init__.py" -type f
66-
echo "Python path:"
67-
python -c "import sys; print('\n'.join(sys.path))"
68-
69-
- name: Debug - pytest collection dry run
70-
run: |
71-
echo "Running pytest collection only:"
72-
python -m pytest --collect-only -v
73-
continue-on-error: true
74-
7537
- name: Run linting with ruff
7638
run: |
7739
python -m ruff check .
@@ -80,19 +42,7 @@ jobs:
8042
- name: Run type checking with mypy
8143
run: python -m mypy src tests
8244

83-
- name: Run basic test without coverage
84-
run: |
85-
echo "Running basic test without any plugins:"
86-
python -m pytest tests/test_basic.py -v -p no:asyncio -p no:cov
87-
continue-on-error: true
88-
89-
- name: Run all tests without coverage
90-
run: |
91-
echo "Running all tests without coverage:"
92-
python -m pytest -v -p no:asyncio
93-
continue-on-error: true
94-
95-
- name: Run tests with pytest (with coverage)
45+
- name: Run tests with pytest
9646
run: python -m pytest -v --cov=nutrient_dws --cov-report=xml --cov-report=term
9747

9848
- name: Upload coverage to Codecov

tests/test_basic.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

tests/unit/test_builder.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Unit tests for Builder API."""
2+
3+
import pytest
4+
5+
from nutrient_dws.builder import BuildAPIWrapper
6+
7+
8+
def test_builder_init():
9+
"""Test builder initialization."""
10+
builder = BuildAPIWrapper(None, "test.pdf")
11+
assert builder._input_file == "test.pdf"
12+
assert builder._actions == []
13+
assert builder._parts == [{"file": "file"}]
14+
assert "file" in builder._files
15+
16+
17+
def test_builder_add_step():
18+
"""Test adding steps to builder."""
19+
builder = BuildAPIWrapper(None, "test.pdf")
20+
result = builder.add_step("convert-to-pdf", options={"format": "docx"})
21+
22+
assert result is builder # Should return self for chaining
23+
assert len(builder._actions) == 1
24+
assert builder._actions[0]["type"] == "convert-to-pdf"
25+
assert builder._actions[0]["format"] == "docx"
26+
27+
28+
def test_builder_chaining():
29+
"""Test method chaining."""
30+
builder = BuildAPIWrapper(None, "test.pdf")
31+
result = (
32+
builder.add_step("convert-to-pdf")
33+
.add_step("rotate-pages", options={"degrees": 90})
34+
.add_step("watermark-pdf", options={"text": "DRAFT"})
35+
)
36+
37+
assert result is builder
38+
assert len(builder._actions) == 3
39+
# Actions are transformed by _map_tool_to_action, so check structure exists
40+
assert all("type" in action for action in builder._actions)
41+
42+
43+
def test_builder_set_output_options():
44+
"""Test setting output options."""
45+
builder = BuildAPIWrapper(None, "test.pdf")
46+
result = builder.set_output_options(
47+
metadata={"title": "Test Doc"},
48+
optimize=True
49+
)
50+
51+
assert result is builder
52+
assert builder._output_options["metadata"]["title"] == "Test Doc"
53+
assert builder._output_options["optimize"] is True
54+
55+
56+
def test_builder_execute_requires_client():
57+
"""Test that execute requires a client."""
58+
builder = BuildAPIWrapper(None, "test.pdf")
59+
builder.add_step("convert-to-pdf")
60+
61+
# Without a proper client, this would fail when trying to access client methods
62+
# We can't test the actual failure without mocking, so just ensure the method exists
63+
assert hasattr(builder, "execute")

tests/unit/test_file_handler.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Unit tests for file handling utilities."""
2+
3+
import io
4+
from pathlib import Path
5+
6+
import pytest
7+
8+
from nutrient_dws.file_handler import (
9+
prepare_file_input,
10+
save_file_output,
11+
stream_file_content,
12+
)
13+
14+
15+
def test_prepare_file_input_from_bytes():
16+
"""Test preparing file input from bytes."""
17+
content = b"Hello, World!"
18+
result, filename = prepare_file_input(content)
19+
assert result == content
20+
assert filename == "document"
21+
22+
23+
def test_prepare_file_input_from_string_io():
24+
"""Test preparing file input from StringIO-like object."""
25+
# Using BytesIO instead of StringIO for binary compatibility
26+
content = b"Test content"
27+
file_obj = io.BytesIO(content)
28+
result, filename = prepare_file_input(file_obj)
29+
assert result == content
30+
assert filename == "document"
31+
32+
33+
def test_get_file_size_from_bytes():
34+
"""Test getting file size from bytes."""
35+
from nutrient_dws.file_handler import get_file_size
36+
37+
content = b"Hello, World!"
38+
size = get_file_size(content)
39+
assert size == 13
40+
41+
42+
def test_get_file_size_from_bytesio():
43+
"""Test getting file size from BytesIO."""
44+
from nutrient_dws.file_handler import get_file_size
45+
46+
content = b"Test content"
47+
file_obj = io.BytesIO(content)
48+
size = get_file_size(file_obj)
49+
assert size == 12
50+
51+
52+
def test_get_file_size_none():
53+
"""Test get_file_size returns None for invalid input."""
54+
from nutrient_dws.file_handler import get_file_size
55+
56+
# Test with a file-like object without proper methods
57+
class DummyFile:
58+
pass
59+
60+
size = get_file_size(DummyFile())
61+
assert size is None

tests/unit/test_http_client.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Unit tests for HTTPClient."""
2+
3+
import pytest
4+
5+
from nutrient_dws.exceptions import APIError, AuthenticationError, NutrientTimeoutError
6+
from nutrient_dws.http_client import HTTPClient
7+
8+
9+
def test_http_client_init():
10+
"""Test HTTP client initialization."""
11+
client = HTTPClient(api_key="test-key")
12+
assert client._api_key == "test-key"
13+
assert client._base_url == "https://api.pspdfkit.com"
14+
assert client._timeout == 300
15+
16+
17+
def test_http_client_init_custom_timeout():
18+
"""Test HTTP client with custom timeout."""
19+
client = HTTPClient(api_key="test-key", timeout=60)
20+
assert client._timeout == 60
21+
22+
23+
def test_http_client_init_no_api_key():
24+
"""Test HTTP client initialization without API key."""
25+
client = HTTPClient(api_key=None)
26+
assert client._api_key is None
27+
28+
29+
def test_http_client_init_empty_api_key():
30+
"""Test HTTP client initialization with empty API key."""
31+
client = HTTPClient(api_key="")
32+
assert client._api_key == ""
33+
34+
35+
def test_http_client_context_manager():
36+
"""Test HTTP client can be used as context manager."""
37+
with HTTPClient(api_key="test-key") as client:
38+
assert client is not None
39+
assert hasattr(client, "_session")

0 commit comments

Comments
 (0)