diff --git a/leggen/services/database_service.py b/leggen/services/database_service.py index 5619930..b441e93 100644 --- a/leggen/services/database_service.py +++ b/leggen/services/database_service.py @@ -693,7 +693,7 @@ class DatabaseService: # Add the display_name column cursor.execute(""" - ALTER TABLE accounts + ALTER TABLE accounts ADD COLUMN display_name TEXT """) @@ -857,6 +857,14 @@ class DatabaseService: for transaction in transactions: try: + # Check if transaction already exists before insertion + cursor.execute( + """SELECT COUNT(*) FROM transactions + WHERE accountId = ? AND transactionId = ?""", + (transaction["accountId"], transaction["transactionId"]), + ) + exists = cursor.fetchone()[0] > 0 + cursor.execute( insert_sql, ( @@ -873,7 +881,11 @@ class DatabaseService: json.dumps(transaction["rawTransaction"]), ), ) - new_transactions.append(transaction) + + # Only add to new_transactions if it didn't exist before + if not exists: + new_transactions.append(transaction) + except sqlite3.IntegrityError as e: logger.warning( f"Failed to insert transaction {transaction.get('transactionId')}: {e}" diff --git a/tests/unit/test_database_service.py b/tests/unit/test_database_service.py index 94f90f7..221d36f 100644 --- a/tests/unit/test_database_service.py +++ b/tests/unit/test_database_service.py @@ -324,6 +324,8 @@ class TestDatabaseService: with patch("sqlite3.connect") as mock_connect: mock_conn = mock_connect.return_value mock_cursor = mock_conn.cursor.return_value + # Mock fetchone to return (0,) indicating transaction doesn't exist yet + mock_cursor.fetchone.return_value = (0,) result = await database_service._persist_transactions_sqlite( "test-account-123", sample_transactions_db_format @@ -338,6 +340,29 @@ class TestDatabaseService: mock_conn.commit.assert_called_once() mock_conn.close.assert_called_once() + async def test_persist_transactions_sqlite_duplicate_detection( + self, database_service, sample_transactions_db_format + ): + """Test that existing transactions are not returned as new.""" + with patch("sqlite3.connect") as mock_connect: + mock_conn = mock_connect.return_value + mock_cursor = mock_conn.cursor.return_value + # Mock fetchone to return (1,) indicating transaction already exists + mock_cursor.fetchone.return_value = (1,) + + result = await database_service._persist_transactions_sqlite( + "test-account-123", sample_transactions_db_format + ) + + # Should return empty list since all transactions already exist + assert len(result) == 0 + + # Verify database operations still happened (INSERT OR REPLACE executed) + mock_connect.assert_called() + mock_cursor.execute.assert_called() + mock_conn.commit.assert_called_once() + mock_conn.close.assert_called_once() + async def test_persist_transactions_sqlite_error(self, database_service): """Test handling error during transaction persistence.""" with patch("sqlite3.connect") as mock_connect: