Skip to content

Commit fa0ae5a

Browse files
committed
More tests
1 parent 85d74b0 commit fa0ae5a

File tree

7 files changed

+148
-67
lines changed

7 files changed

+148
-67
lines changed

manage.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python
2+
"""Django's command-line utility for administrative tasks."""
3+
4+
import os
5+
import sys
6+
7+
8+
def main() -> None:
9+
"""Run administrative tasks."""
10+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings")
11+
try:
12+
from django.core.management import execute_from_command_line
13+
except ImportError as exc:
14+
raise ImportError(
15+
"Couldn't import Django. Are you sure it's installed?"
16+
) from exc
17+
execute_from_command_line(sys.argv)
18+
19+
20+
if __name__ == "__main__":
21+
main()

postgres_copy/psycopg_compat.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,16 @@ def copy_to(
8282
if is_text:
8383
# For text destinations, we need to decode to str
8484
text_dest = typing.cast(typing.TextIO, destination)
85+
# Handle both bytes and memoryview objects
86+
if isinstance(data, memoryview):
87+
data = data.tobytes()
8588
text_dest.write(data.decode("utf-8"))
8689
else:
8790
# For binary destinations, we keep as bytes
8891
binary_dest = typing.cast(typing.BinaryIO, destination)
92+
# Handle both bytes and memoryview objects
93+
if isinstance(data, memoryview):
94+
data = data.tobytes()
8995
binary_dest.write(data)
9096

9197
def copy_from(cursor: CursorProtocol, sql: str, source: FileObj) -> None:

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ docs = [
105105
addopts = "-p no:warnings --cov=postgres_copy --cov-branch -cov-report=term-missing:skip-covered --cov-context=test"
106106
testpaths = ["tests"]
107107
python_files = "test_*.py"
108+
DJANGO_SETTINGS_MODULE = "tests.settings"
108109

109110
[tool.coverage.run]
110111
source = ["postgres_copy"]

tests/conftest.py

Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,10 @@
11
import os
2-
from pathlib import Path
2+
import sys
33
import pytest
4-
from django.conf import settings
54
from django.db import connections
6-
import django
75

8-
ROOT_DIR = Path(__file__).parent.parent
9-
PG_USER = os.environ.get("PG_USER", "postgres")
10-
11-
12-
def pytest_configure():
13-
settings.configure(
14-
DATABASES={
15-
"default": {
16-
"HOST": "localhost",
17-
"PORT": 5432,
18-
"NAME": "postgres",
19-
"USER": PG_USER,
20-
"ENGINE": "django.db.backends.postgresql_psycopg2",
21-
},
22-
"other": {
23-
"HOST": "localhost",
24-
"PORT": 5432,
25-
"NAME": "postgres",
26-
"USER": PG_USER,
27-
"ENGINE": "django.db.backends.postgresql_psycopg2",
28-
},
29-
"sqlite": {"NAME": "sqlite", "ENGINE": "django.db.backends.sqlite3"},
30-
"secondary": {
31-
"HOST": "localhost",
32-
"PORT": 5432,
33-
"NAME": "postgres",
34-
"USER": PG_USER,
35-
"ENGINE": "django.db.backends.postgresql_psycopg2",
36-
},
37-
},
38-
INSTALLED_APPS=("tests",),
39-
DATABASE_ROUTERS=["tests.test_router.CustomRouter"],
40-
DEFAULT_AUTO_FIELD="django.db.models.BigAutoField",
41-
LOGGING={
42-
"version": 1,
43-
"disable_existing_loggers": False,
44-
"handlers": {
45-
"file": {
46-
"level": "DEBUG",
47-
"class": "logging.FileHandler",
48-
"filename": ROOT_DIR / "tests.log",
49-
},
50-
},
51-
"formatters": {
52-
"verbose": {
53-
"format": "%(levelname)s|%(asctime)s|%(module)s|%(message)s",
54-
"datefmt": "%d/%b/%Y %H:%M:%S",
55-
}
56-
},
57-
"loggers": {
58-
"postgres_copy": {
59-
"handlers": ["file"],
60-
"level": "DEBUG",
61-
"propagate": True,
62-
},
63-
},
64-
},
65-
)
66-
67-
# Initialize Django
68-
django.setup()
6+
# Add the parent directory to sys.path
7+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
698

709

7110
@pytest.fixture(scope="session")
@@ -74,7 +13,6 @@ def django_db_setup(django_db_setup, django_db_blocker):
7413
Fixture to set up the test database with the required tables.
7514
This extends the built-in django_db_setup fixture.
7615
"""
77-
7816
# Import all test models
7917
from tests.test_models import (
8018
MockObject,

tests/settings.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import os
2+
from pathlib import Path
3+
4+
# Build paths inside the project like this: BASE_DIR / 'subdir'.
5+
BASE_DIR = Path(__file__).resolve().parent.parent
6+
7+
# Database settings
8+
PG_USER = os.environ.get("PG_USER", "postgres")
9+
10+
# Use a unique database name for testing
11+
TEST_DB_NAME = "django_postgres_copy_test"
12+
13+
DATABASES = {
14+
"default": {
15+
"HOST": "localhost",
16+
"PORT": 5432,
17+
"NAME": TEST_DB_NAME,
18+
"USER": PG_USER,
19+
"ENGINE": "django.db.backends.postgresql_psycopg2",
20+
"TEST": {
21+
"NAME": TEST_DB_NAME,
22+
},
23+
},
24+
"other": {
25+
"HOST": "localhost",
26+
"PORT": 5432,
27+
"NAME": "postgres",
28+
"USER": PG_USER,
29+
"ENGINE": "django.db.backends.postgresql_psycopg2",
30+
"TEST": {
31+
"NAME": "django_postgres_copy_test_other",
32+
},
33+
},
34+
"sqlite": {
35+
"NAME": "sqlite",
36+
"ENGINE": "django.db.backends.sqlite3",
37+
"TEST": {
38+
"NAME": "django_postgres_copy_test_sqlite",
39+
},
40+
},
41+
"secondary": {
42+
"HOST": "localhost",
43+
"PORT": 5432,
44+
"NAME": "postgres",
45+
"USER": PG_USER,
46+
"ENGINE": "django.db.backends.postgresql_psycopg2",
47+
"TEST": {
48+
"NAME": "django_postgres_copy_test_secondary",
49+
},
50+
},
51+
}
52+
53+
# Application definition
54+
INSTALLED_APPS = [
55+
"tests",
56+
]
57+
58+
# Database router
59+
DATABASE_ROUTERS = ["tests.test_router.CustomRouter"]
60+
61+
# Default primary key field type
62+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
63+
64+
# Logging configuration
65+
LOGGING = {
66+
"version": 1,
67+
"disable_existing_loggers": False,
68+
"handlers": {
69+
"file": {
70+
"level": "DEBUG",
71+
"class": "logging.FileHandler",
72+
"filename": BASE_DIR / "tests.log",
73+
},
74+
},
75+
"formatters": {
76+
"verbose": {
77+
"format": "%(levelname)s|%(asctime)s|%(module)s|%(message)s",
78+
"datefmt": "%d/%b/%Y %H:%M:%S",
79+
}
80+
},
81+
"loggers": {
82+
"postgres_copy": {
83+
"handlers": ["file"],
84+
"level": "DEBUG",
85+
"propagate": True,
86+
},
87+
},
88+
}
89+
90+
# Secret key (required for Django but not used in tests)
91+
SECRET_KEY = "django-insecure-test-key-not-used-in-production"
92+
93+
# Required Django settings (not used in tests)
94+
DEBUG = True
95+
ALLOWED_HOSTS = []
96+
MIDDLEWARE = []
97+
ROOT_URLCONF = "tests.urls"
98+
TEMPLATES = []
99+
WSGI_APPLICATION = ""

tests/test_psycopg_compat.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,18 @@ def test_copy_to_with_psycopg2(self):
2727
"""Test copy_to function with psycopg2."""
2828
# Mock the psycopg2 import to succeed and psycopg to fail
2929
with mock.patch.dict("sys.modules", {"psycopg": None}):
30+
# Reload the module to ensure it uses the psycopg2 implementation
31+
import importlib
32+
import postgres_copy.psycopg_compat
33+
34+
importlib.reload(postgres_copy.psycopg_compat)
35+
from postgres_copy.psycopg_compat import copy_to as reloaded_copy_to
36+
3037
with mock.patch("psycopg2.extensions.adapt") as mock_adapt:
3138
mock_adapt.return_value.getquoted.return_value = b"'test'"
3239

3340
# Call the function
34-
copy_to(self.cursor, self.sql, (1, 2), self.destination)
41+
reloaded_copy_to(self.cursor, self.sql, (1, 2), self.destination)
3542

3643
# Check that the psycopg2 version was called with the right parameters
3744
self.cursor.copy_expert.assert_called_once()
@@ -45,8 +52,15 @@ def test_copy_from_with_psycopg2(self):
4552
"""Test copy_from function with psycopg2."""
4653
# Mock the psycopg2 import to succeed and psycopg to fail
4754
with mock.patch.dict("sys.modules", {"psycopg": None}):
55+
# Reload the module to ensure it uses the psycopg2 implementation
56+
import importlib
57+
import postgres_copy.psycopg_compat
58+
59+
importlib.reload(postgres_copy.psycopg_compat)
60+
from postgres_copy.psycopg_compat import copy_from as reloaded_copy_from
61+
4862
# Call the function
49-
copy_from(self.cursor, self.sql, self.source)
63+
reloaded_copy_from(self.cursor, self.sql, self.source)
5064

5165
# Check that the psycopg2 version was called with the right parameters
5266
self.cursor.copy_expert.assert_called_once()

tests/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# This file is required by Django but not used in tests
2+
urlpatterns = []

0 commit comments

Comments
 (0)