From 3413d6a77807a6011036288f85dad26bd04c1b4d Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sat, 12 Oct 2024 15:50:44 +0100 Subject: [PATCH] fix tests Signed-off-by: Grant Ramsay --- tests/test_unique.py | 141 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 11 deletions(-) diff --git a/tests/test_unique.py b/tests/test_unique.py index 977549c..68a3d89 100644 --- a/tests/test_unique.py +++ b/tests/test_unique.py @@ -1,11 +1,11 @@ """Test the Unique constraint.""" -from typing import Annotated +from typing import Annotated, Union import pytest from sqliter import SqliterDB -from sqliter.exceptions import SqliterError +from sqliter.exceptions import RecordInsertionError from sqliter.model import BaseDBModel from sqliter.model.unique import Unique @@ -13,8 +13,8 @@ class TestUnique: """Test suite for the Unique constraint.""" - def test_unique_constraint(self) -> None: - """Test that the Unique constraint is properly applied.""" + def test_unique_constraint_single_field(self) -> None: + """Test that the Unique constraint is applied to a single field.""" class User(BaseDBModel): name: str @@ -30,7 +30,7 @@ class User(BaseDBModel): # Attempt to insert a user with the same email user2 = User(name="Bob", email="alice@example.com") - with pytest.raises(SqliterError) as excinfo: + with pytest.raises(RecordInsertionError) as excinfo: db.insert(user2) assert "UNIQUE constraint failed: users.email" in str(excinfo.value) @@ -41,15 +41,134 @@ class User(BaseDBModel): assert users[0].name == "Alice" assert users[0].email == "alice@example.com" - # Insert a user with a different email successfully - user3 = User(name="Charlie", email="charlie@example.com") - db.insert(user3) + def test_unique_constraint_multi_column(self) -> None: + """Test the Unique constraint on multiple fields.""" - # Verify that two users are now in the database + class User(BaseDBModel): + name: Annotated[str, Unique()] + email: str + + db = SqliterDB(":memory:") + db.create_table(User) + + # Insert a user successfully + user1 = User(name="Alice", email="alice@example.com") + db.insert(user1) + + # Insert another user with the same email but different name (no + # conflict) + user2 = User(name="Bob", email="alice@example.com") + db.insert(user2) + + # Attempt to insert a user with the same name (should fail) + user3 = User(name="Alice", email="charlie@example.com") + + with pytest.raises(RecordInsertionError) as excinfo: + db.insert(user3) + + assert "UNIQUE constraint failed: users.name" in str(excinfo.value) + + def test_unique_constraint_sql_generation(self, mocker) -> None: + """Test that the correct SQL for the Unique constraint is generated.""" + + class User(BaseDBModel): + name: Annotated[str, Unique()] + email: str + + # Mock the cursor to capture executed SQL + mock_cursor = mocker.MagicMock() + mocker.patch.object( + SqliterDB, "connect" + ).return_value.__enter__.return_value.cursor.return_value = mock_cursor + + db = SqliterDB(":memory:") + db.create_table(User) + + # Capture the generated SQL statement for table creation + sql = mock_cursor.execute.call_args[0][0] + + # Remove the primary key part from the SQL for easier assertion + sql_without_pk = sql.replace( + '"pk" INTEGER PRIMARY KEY AUTOINCREMENT, ', "" + ) + + # Assert that the correct UNIQUE syntax is present for the 'name' field + assert "CREATE TABLE" in sql + assert "name TEXT UNIQUE" in sql_without_pk # Correct SQLite syntax + + def test_unique_constraint_across_records(self) -> None: + """Test that unique constraints hold across multiple records.""" + + class User(BaseDBModel): + name: str + email: Annotated[str, Unique()] + + db = SqliterDB(":memory:") + db.create_table(User) + + # Insert multiple users with unique emails + user1 = User(name="Alice", email="alice@example.com") + user2 = User(name="Bob", email="bob@example.com") + db.insert(user1) + db.insert(user2) + + # Insert another user with a duplicate email (should fail) + user3 = User(name="Charlie", email="bob@example.com") + with pytest.raises(RecordInsertionError) as excinfo: + db.insert(user3) + + assert "UNIQUE constraint failed: users.email" in str(excinfo.value) + + # Verify that only two users are inserted + users = db.select(User).fetch_all() + assert len(users) == 2 + + def test_unique_constraint_with_null(self) -> None: + """Test that the Unique constraint allows null values if applicable.""" + + class User(BaseDBModel): + name: str + email: Annotated[Union[str, None], Unique()] + + db = SqliterDB(":memory:") + db.create_table(User) + + # Insert a user with a null email + user1 = User(name="Alice", email=None) + db.insert(user1) + + # Insert another user with a null email (no conflict) + user2 = User(name="Bob", email=None) + db.insert(user2) + + # Verify that both users were inserted successfully + users = db.select(User).fetch_all() + assert len(users) == 2 + assert {u.name for u in users} == {"Alice", "Bob"} + assert {u.email for u in users} == {None} + + def test_unique_constraint_with_different_values(self) -> None: + """Test that Unique constraint allows different unique values.""" + + class User(BaseDBModel): + name: str + email: Annotated[str, Unique()] + + db = SqliterDB(":memory:") + db.create_table(User) + + # Insert a user with a unique email + user1 = User(name="Alice", email="alice@example.com") + db.insert(user1) + + # Insert another user with a different email (no conflict) + user2 = User(name="Bob", email="bob@example.com") + db.insert(user2) + + # Verify that both users were inserted successfully users = db.select(User).fetch_all() assert len(users) == 2 - assert {u.name for u in users} == {"Alice", "Charlie"} assert {u.email for u in users} == { "alice@example.com", - "charlie@example.com", + "bob@example.com", }