Преглед на файлове

Merge remote-tracking branch 'origin/main' into feature/wallet-db-transactions

Cesar Rodas преди 2 месеца
родител
ревизия
9e3388c81f
променени са 5 файла, в които са добавени 263 реда и са изтрити 187 реда
  1. 33 14
      crates/cdk-ffi/tests/README.md
  2. 216 167
      crates/cdk-ffi/tests/test_transactions.py
  3. 3 5
      crates/cdk-sql-common/src/pool.rs
  4. 1 1
      crates/cdk-sqlite/src/common.rs
  5. 10 0
      crates/cdk-sqlite/src/mint/mod.rs

+ 33 - 14
crates/cdk-ffi/tests/README.md

@@ -1,6 +1,6 @@
 # CDK FFI Python Tests
 
-This directory contains Python tests for the CDK FFI (Foreign Function Interface) bindings.
+This directory contains Python tests for the CDK FFI (Foreign Function Interface) bindings, covering both transaction operations and wallet database operations.
 
 ## Running the Tests
 
@@ -34,35 +34,51 @@ python3 crates/cdk-ffi/tests/test_transactions.py
 The test script automatically:
 1. Locates the bindings in `target/bindings/python/`
 2. Copies the shared library from `target/release/` to the bindings directory
-3. Runs all transaction tests
+3. Runs all tests
 
 **No manual file copying required!**
 
 ## Test Suite
 
-### Transaction Tests (test_transactions.py)
+### Transaction Tests (explicit transaction management)
 
-Comprehensive tests for database transaction operations:
+Tests that use `begin_db_transaction()`, `commit()`, and `rollback()`:
 
 1. **Increment Counter with Commit** - Tests `increment_keyset_counter()` and persistence
-2. **Implicit Rollback on Drop** - Verifies automatic rollback when transactions are dropped  
+2. **Implicit Rollback on Drop** - Verifies automatic rollback when transactions are dropped
 3. **Explicit Rollback** - Tests manual `rollback()` calls
 4. **Transaction Reads** - Tests reading data within active transactions
-5. **Multiple Increments** - Tests sequential counter operations
-6. **Wallet Mint Operations** - Tests adding and removing mints in transactions
-7. **Wallet Proof Operations** - Basic proof database operations
-8. **Wallet Quote Operations** - Basic quote operations  
-9. **Wallet Balance Query** - Basic balance query operations
-10. **Wallet Transaction Atomicity** - Tests that rollback properly reverts ALL changes
+5. **Multiple Increments** - Tests sequential counter operations in one transaction
+6. **Transaction Atomicity** - Tests that rollback properly reverts ALL changes
+7. **Get Proofs by Y Values (Transaction)** - Tests retrieving proofs within transactions
+
+### Wallet Tests (direct wallet methods)
+
+Tests that use wallet database methods directly without explicit transactions:
+
+8. **Wallet Creation** - Tests creating a wallet with SQLite backend
+9. **Wallet Mint Management** - Tests adding, querying, and removing mints
+10. **Wallet Keyset Management** - Tests adding and querying keysets
+11. **Wallet Keyset Counter** - Tests keyset counter increment operations
+12. **Wallet Quote Operations** - Tests querying mint and melt quotes
+13. **Wallet Get Proofs by Y Values** - Tests retrieving proofs by Y values
 
 ### Key Features Tested
 
+**Transaction Features:**
 - ✅ **Transaction atomicity** - All-or-nothing commits/rollbacks
 - ✅ **Isolation** - Uncommitted changes not visible outside transaction
 - ✅ **Durability** - Committed changes persist
 - ✅ **Implicit rollback** - Automatic cleanup on transaction drop
+- ✅ **Explicit rollback** - Manual transaction rollback
+
+**Wallet Features:**
+- ✅ **Wallet creation** - SQLite backend initialization
+- ✅ **Mint management** - Add, query, and retrieve mint URLs
+- ✅ **Keyset operations** - Add keysets and query by ID or mint
 - ✅ **Counter operations** - Keyset counter increment/read
-- ✅ **Mint management** - Add/remove mints with proper constraints
+- ✅ **Quote queries** - Retrieve mint and melt quotes
+- ✅ **Proof retrieval** - Get proofs by Y values
 - ✅ **Foreign key constraints** - Proper referential integrity
 
 ## Test Output
@@ -70,11 +86,11 @@ Comprehensive tests for database transaction operations:
 Expected output for successful run:
 
 ```
-Starting CDK FFI Transaction Tests
+Starting CDK FFI Wallet and Transaction Tests
 ==================================================
 ... (test execution) ...
 ==================================================
-Test Results: 10 passed, 0 failed
+Test Results: 13 passed, 0 failed
 ==================================================
 ```
 
@@ -107,6 +123,7 @@ When adding new tests:
 2. Add test to the `tests` list in `main()`
 3. Use temporary databases for isolation
 4. Follow existing patterns for setup/teardown
+5. Clearly indicate if the test uses transactions or direct wallet methods
 
 ## Implementation Notes
 
@@ -114,3 +131,5 @@ When adding new tests:
 - Each test is fully isolated with its own database
 - Tests clean up automatically via `finally` blocks
 - The script handles path resolution and library loading automatically
+- Transaction tests demonstrate ACID properties
+- Wallet tests demonstrate direct database operations

+ 216 - 167
crates/cdk-ffi/tests/test_transactions.py

@@ -1,10 +1,6 @@
 #!/usr/bin/env python3
 """
-Test suite for database transactions focusing on:
-- increment_keyset_counter operations
-- Transaction commits
-- Transaction reads
-- Implicit rollbacks on drop
+Test suite for CDK FFI wallet and transaction operations
 """
 
 import asyncio
@@ -33,6 +29,8 @@ sys.path.insert(0, str(bindings_path))
 import cdk_ffi
 
 
+# Transaction Tests (using explicit transactions)
+
 async def test_increment_keyset_counter_commit():
     """Test that increment_keyset_counter works and persists after commit"""
     print("\n=== Test: Increment Keyset Counter with Commit ===")
@@ -41,14 +39,10 @@ async def test_increment_keyset_counter_commit():
         db_path = tmp.name
 
     try:
-        # Create database
         backend = cdk_ffi.WalletDbBackend.SQLITE(path=db_path)
         db = cdk_ffi.create_wallet_db(backend)
 
-        # Create a keyset ID (16 hex characters = 8 bytes)
         keyset_id = cdk_ffi.Id(hex="004146bdf4a9afab")
-
-        # Add keyset info first
         mint_url = cdk_ffi.MintUrl(url="https://testmint.example.com")
         keyset_info = cdk_ffi.KeySetInfo(
             id=keyset_id.hex,
@@ -57,43 +51,38 @@ async def test_increment_keyset_counter_commit():
             input_fee_ppk=0
         )
 
-        # Begin transaction, add mint and keyset
+        # Setup
         tx = await db.begin_db_transaction()
-        await tx.add_mint(mint_url, None)  # Add mint first (foreign key requirement)
+        await tx.add_mint(mint_url, None)
         await tx.add_mint_keysets(mint_url, [keyset_info])
         await tx.commit()
 
-        # Begin new transaction and increment counter
+        # Increment counter in transaction
         tx = await db.begin_db_transaction()
         counter1 = await tx.increment_keyset_counter(keyset_id, 1)
-        print(f"First increment: {counter1}")
-        assert counter1 == 1, f"Expected counter to be 1, got {counter1}"
-
         counter2 = await tx.increment_keyset_counter(keyset_id, 5)
-        print(f"Second increment (+5): {counter2}")
-        assert counter2 == 6, f"Expected counter to be 6, got {counter2}"
-
-        # Commit the transaction
         await tx.commit()
-        print("✓ Transaction committed")
 
-        # Verify the counter persisted by reading in a new transaction
+        assert counter1 == 1, f"Expected counter 1, got {counter1}"
+        assert counter2 == 6, f"Expected counter 6, got {counter2}"
+        print("✓ Counters incremented correctly")
+
+        # Verify persistence
         tx_read = await db.begin_db_transaction()
         counter3 = await tx_read.increment_keyset_counter(keyset_id, 0)
         await tx_read.rollback()
-        print(f"Counter after commit (read with +0): {counter3}")
-        assert counter3 == 6, f"Expected counter to persist at 6, got {counter3}"
+        assert counter3 == 6, f"Expected persisted counter 6, got {counter3}"
+        print("✓ Counter persisted after commit")
 
-        print("✓ Test passed: Counter increments and commits work correctly")
+        print("✓ Test passed: Counter increments and commits work")
 
     finally:
-        # Cleanup
         if os.path.exists(db_path):
             os.unlink(db_path)
 
 
 async def test_implicit_rollback_on_drop():
-    """Test that transactions are implicitly rolled back when dropped without commit"""
+    """Test that transactions are implicitly rolled back when dropped"""
     print("\n=== Test: Implicit Rollback on Drop ===")
 
     with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
@@ -106,9 +95,9 @@ async def test_implicit_rollback_on_drop():
         keyset_id = cdk_ffi.Id(hex="004146bdf4a9afab")
         mint_url = cdk_ffi.MintUrl(url="https://testmint.example.com")
 
-        # Setup: Add keyset
+        # Setup
         tx = await db.begin_db_transaction()
-        await tx.add_mint(mint_url, None)  # Add mint first (foreign key requirement)
+        await tx.add_mint(mint_url, None)
         keyset_info = cdk_ffi.KeySetInfo(
             id=keyset_id.hex,
             unit=cdk_ffi.CurrencyUnit.SAT(),
@@ -124,29 +113,25 @@ async def test_implicit_rollback_on_drop():
         await tx_read.rollback()
         print(f"Initial counter: {initial_counter}")
 
-        # Start a transaction and increment counter but don't commit
-        print("Starting transaction without commit...")
+        # Increment without commit
         tx_no_commit = await db.begin_db_transaction()
         incremented = await tx_no_commit.increment_keyset_counter(keyset_id, 10)
         print(f"Counter incremented to {incremented} (not committed)")
-
-        # Let the transaction go out of scope (implicit rollback)
         del tx_no_commit
 
-        # Give async cleanup time to run
         await asyncio.sleep(0.5)
         print("Transaction dropped (should trigger implicit rollback)")
 
-        # Verify counter was rolled back
+        # Verify rollback
         tx_verify = await db.begin_db_transaction()
         final_counter = await tx_verify.increment_keyset_counter(keyset_id, 0)
         await tx_verify.rollback()
-        print(f"Counter after implicit rollback: {final_counter}")
 
         assert final_counter == initial_counter, \
             f"Expected counter to rollback to {initial_counter}, got {final_counter}"
+        print("✓ Implicit rollback works correctly")
 
-        print("✓ Test passed: Implicit rollback works correctly")
+        print("✓ Test passed: Implicit rollback on drop works")
 
     finally:
         if os.path.exists(db_path):
@@ -169,7 +154,7 @@ async def test_explicit_rollback():
 
         # Setup
         tx = await db.begin_db_transaction()
-        await tx.add_mint(mint_url, None)  # Add mint first (foreign key requirement)
+        await tx.add_mint(mint_url, None)
         keyset_info = cdk_ffi.KeySetInfo(
             id=keyset_id.hex,
             unit=cdk_ffi.CurrencyUnit.SAT(),
@@ -179,14 +164,12 @@ async def test_explicit_rollback():
         await tx.add_mint_keysets(mint_url, [keyset_info])
         counter_initial = await tx.increment_keyset_counter(keyset_id, 5)
         await tx.commit()
-        print(f"Initial counter committed: {counter_initial}")
+        print(f"Initial counter: {counter_initial}")
 
-        # Start transaction, increment, then explicitly rollback
+        # Increment and rollback
         tx_rollback = await db.begin_db_transaction()
         counter_incremented = await tx_rollback.increment_keyset_counter(keyset_id, 100)
         print(f"Counter incremented to {counter_incremented} in transaction")
-
-        # Explicit rollback
         await tx_rollback.rollback()
         print("Explicitly rolled back transaction")
 
@@ -194,12 +177,12 @@ async def test_explicit_rollback():
         tx_verify = await db.begin_db_transaction()
         counter_after = await tx_verify.increment_keyset_counter(keyset_id, 0)
         await tx_verify.rollback()
-        print(f"Counter after explicit rollback: {counter_after}")
 
         assert counter_after == counter_initial, \
-            f"Expected counter to be {counter_initial} after rollback, got {counter_after}"
+            f"Expected counter {counter_initial}, got {counter_after}"
+        print("✓ Explicit rollback works correctly")
 
-        print("✓ Test passed: Explicit rollback works correctly")
+        print("✓ Test passed: Explicit rollback works")
 
     finally:
         if os.path.exists(db_path):
@@ -220,9 +203,9 @@ async def test_transaction_reads():
         keyset_id = cdk_ffi.Id(hex="004146bdf4a9afab")
         mint_url = cdk_ffi.MintUrl(url="https://testmint.example.com")
 
-        # Add keyset in transaction
+        # Add keyset in transaction and read within same transaction
         tx = await db.begin_db_transaction()
-        await tx.add_mint(mint_url, None)  # Add mint first (foreign key requirement)
+        await tx.add_mint(mint_url, None)
         keyset_info = cdk_ffi.KeySetInfo(
             id=keyset_id.hex,
             unit=cdk_ffi.CurrencyUnit.SAT(),
@@ -231,23 +214,21 @@ async def test_transaction_reads():
         )
         await tx.add_mint_keysets(mint_url, [keyset_info])
 
-        # Read within the same transaction (should see uncommitted data)
         keyset_read = await tx.get_keyset_by_id(keyset_id)
-        assert keyset_read is not None, "Should be able to read keyset within transaction"
+        assert keyset_read is not None, "Should read within transaction"
         assert keyset_read.id == keyset_id.hex, "Keyset ID should match"
-        print(f"✓ Read keyset within transaction: {keyset_read.id}")
+        print("✓ Read keyset within transaction")
 
         await tx.commit()
-        print("✓ Transaction committed")
 
-        # Read from a new transaction
+        # Read from new transaction
         tx_new = await db.begin_db_transaction()
         keyset_read2 = await tx_new.get_keyset_by_id(keyset_id)
-        assert keyset_read2 is not None, "Should be able to read committed keyset"
-        print(f"✓ Read keyset in new transaction: {keyset_read2.id}")
+        assert keyset_read2 is not None, "Should read committed keyset"
         await tx_new.rollback()
+        print("✓ Read keyset in new transaction")
 
-        print("✓ Test passed: Transaction reads work correctly")
+        print("✓ Test passed: Transaction reads work")
 
     finally:
         if os.path.exists(db_path):
@@ -255,7 +236,7 @@ async def test_transaction_reads():
 
 
 async def test_multiple_increments_same_transaction():
-    """Test multiple increments in the same transaction"""
+    """Test multiple increments in same transaction"""
     print("\n=== Test: Multiple Increments in Same Transaction ===")
 
     with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
@@ -270,7 +251,7 @@ async def test_multiple_increments_same_transaction():
 
         # Setup
         tx = await db.begin_db_transaction()
-        await tx.add_mint(mint_url, None)  # Add mint first (foreign key requirement)
+        await tx.add_mint(mint_url, None)
         keyset_info = cdk_ffi.KeySetInfo(
             id=keyset_id.hex,
             unit=cdk_ffi.CurrencyUnit.SAT(),
@@ -282,38 +263,34 @@ async def test_multiple_increments_same_transaction():
 
         # Multiple increments in one transaction
         tx = await db.begin_db_transaction()
-
         counters = []
         for i in range(1, 6):
             counter = await tx.increment_keyset_counter(keyset_id, 1)
             counters.append(counter)
-            print(f"Increment {i}: counter = {counter}")
 
-        # Verify sequence
         expected = list(range(1, 6))
         assert counters == expected, f"Expected {expected}, got {counters}"
-        print(f"✓ Counters incremented correctly: {counters}")
+        print(f"✓ Counters incremented: {counters}")
 
         await tx.commit()
-        print("✓ All increments committed")
 
         # Verify final value
         tx_verify = await db.begin_db_transaction()
         final = await tx_verify.increment_keyset_counter(keyset_id, 0)
         await tx_verify.rollback()
-        assert final == 5, f"Expected final counter to be 5, got {final}"
-        print(f"✓ Final counter value: {final}")
+        assert final == 5, f"Expected final counter 5, got {final}"
+        print("✓ Final counter value correct")
 
-        print("✓ Test passed: Multiple increments work correctly")
+        print("✓ Test passed: Multiple increments work")
 
     finally:
         if os.path.exists(db_path):
             os.unlink(db_path)
 
 
-async def test_wallet_mint_operations():
-    """Test adding and querying mints in transactions"""
-    print("\n=== Test: Wallet Mint Operations ===")
+async def test_transaction_atomicity():
+    """Test that transaction rollback reverts all changes"""
+    print("\n=== Test: Transaction Atomicity ===")
 
     with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
         db_path = tmp.name
@@ -324,32 +301,89 @@ async def test_wallet_mint_operations():
 
         mint_url1 = cdk_ffi.MintUrl(url="https://mint1.example.com")
         mint_url2 = cdk_ffi.MintUrl(url="https://mint2.example.com")
+        keyset_id = cdk_ffi.Id(hex="004146bdf4a9afab")
 
-        # Add multiple mints in a transaction
+        # Transaction with multiple operations
         tx = await db.begin_db_transaction()
         await tx.add_mint(mint_url1, None)
         await tx.add_mint(mint_url2, None)
-        await tx.commit()
-        print("✓ Added 2 mints in transaction")
+        keyset_info = cdk_ffi.KeySetInfo(
+            id=keyset_id.hex,
+            unit=cdk_ffi.CurrencyUnit.SAT(),
+            active=True,
+            input_fee_ppk=0
+        )
+        await tx.add_mint_keysets(mint_url1, [keyset_info])
+        await tx.increment_keyset_counter(keyset_id, 42)
+        print("✓ Performed multiple operations")
 
-        # Test removing a mint
-        tx = await db.begin_db_transaction()
-        await tx.remove_mint(mint_url1)
-        await tx.commit()
-        print("✓ Removed mint1")
+        # Rollback
+        await tx.rollback()
+        print("✓ Rolled back transaction")
 
-        print("✓ Mint operations completed successfully")
+        # Verify nothing persisted
+        tx_read = await db.begin_db_transaction()
+        keyset_read = await tx_read.get_keyset_by_id(keyset_id)
+        await tx_read.rollback()
+        assert keyset_read is None, "Keyset should not exist after rollback"
+        print("✓ Nothing persisted after rollback")
 
-        print("✓ Test passed: Wallet mint operations work correctly")
+        # Now commit
+        tx2 = await db.begin_db_transaction()
+        await tx2.add_mint(mint_url1, None)
+        await tx2.add_mint(mint_url2, None)
+        await tx2.add_mint_keysets(mint_url1, [keyset_info])
+        await tx2.increment_keyset_counter(keyset_id, 42)
+        await tx2.commit()
+        print("✓ Committed transaction")
+
+        # Verify persistence
+        tx_verify = await db.begin_db_transaction()
+        keyset_after = await tx_verify.get_keyset_by_id(keyset_id)
+        assert keyset_after is not None, "Keyset should exist after commit"
+        counter_after = await tx_verify.increment_keyset_counter(keyset_id, 0)
+        await tx_verify.rollback()
+        assert counter_after == 42, f"Expected counter 42, got {counter_after}"
+        print("✓ All operations persisted after commit")
+
+        print("✓ Test passed: Transaction atomicity works")
+
+    finally:
+        if os.path.exists(db_path):
+            os.unlink(db_path)
+
+
+
+
+# Wallet Tests (using direct wallet methods without explicit transactions)
+
+async def test_wallet_creation():
+    """Test creating a wallet with SQLite backend"""
+    print("\n=== Test: Wallet Creation ===")
+
+    with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
+        db_path = tmp.name
+
+    try:
+        backend = cdk_ffi.WalletDbBackend.SQLITE(path=db_path)
+        db = cdk_ffi.create_wallet_db(backend)
+        print("✓ Wallet database created")
+
+        # Verify database is accessible
+        mint_quotes = await db.get_mint_quotes()
+        assert isinstance(mint_quotes, list), "get_mint_quotes should return a list"
+        print("✓ Wallet database accessible")
+
+        print("✓ Test passed: Wallet creation works")
 
     finally:
         if os.path.exists(db_path):
             os.unlink(db_path)
 
 
-async def test_wallet_proof_operations():
-    """Test adding and querying proofs with transactions"""
-    print("\n=== Test: Wallet Proof Operations ===")
+async def test_wallet_mint_management():
+    """Test adding and querying mints"""
+    print("\n=== Test: Wallet Mint Management ===")
 
     with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
         db_path = tmp.name
@@ -359,35 +393,38 @@ async def test_wallet_proof_operations():
         db = cdk_ffi.create_wallet_db(backend)
 
         mint_url = cdk_ffi.MintUrl(url="https://testmint.example.com")
-        keyset_id = cdk_ffi.Id(hex="004146bdf4a9afab")
 
-        # Setup mint and keyset
+        # Add mint (using transaction)
         tx = await db.begin_db_transaction()
         await tx.add_mint(mint_url, None)
-        keyset_info = cdk_ffi.KeySetInfo(
-            id=keyset_id.hex,
-            unit=cdk_ffi.CurrencyUnit.SAT(),
-            active=True,
-            input_fee_ppk=0
-        )
-        await tx.add_mint_keysets(mint_url, [keyset_info])
         await tx.commit()
-        print("✓ Setup mint and keyset")
+        print("✓ Added mint to wallet")
+
+        # Get specific mint (read-only, can use db directly)
+        await db.get_mint(mint_url)
+        print("✓ Retrieved mint from database")
+
+        # Remove mint (using transaction)
+        tx = await db.begin_db_transaction()
+        await tx.remove_mint(mint_url)
+        await tx.commit()
+        print("✓ Removed mint from wallet")
 
-        # Proof operations are complex and require proper key generation
-        # This would require implementing PublicKey API properly
-        print("✓ Proof operations (basic test - complex operations require proper FFI key API)")
+        # Verify removal
+        mint_info_after = await db.get_mint(mint_url)
+        assert mint_info_after is None, "Mint should be removed"
+        print("✓ Verified mint removal")
 
-        print("✓ Test passed: Wallet proof operations work correctly")
+        print("✓ Test passed: Mint management works")
 
     finally:
         if os.path.exists(db_path):
             os.unlink(db_path)
 
 
-async def test_wallet_quote_operations():
-    """Test mint and melt quote operations with transactions"""
-    print("\n=== Test: Wallet Quote Operations ===")
+async def test_wallet_keyset_management():
+    """Test adding and querying keysets"""
+    print("\n=== Test: Wallet Keyset Management ===")
 
     with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
         db_path = tmp.name
@@ -397,26 +434,42 @@ async def test_wallet_quote_operations():
         db = cdk_ffi.create_wallet_db(backend)
 
         mint_url = cdk_ffi.MintUrl(url="https://testmint.example.com")
+        keyset_id = cdk_ffi.Id(hex="004146bdf4a9afab")
 
-        # Setup mint
+        # Add mint and keyset (using transaction)
         tx = await db.begin_db_transaction()
         await tx.add_mint(mint_url, None)
+        keyset_info = cdk_ffi.KeySetInfo(
+            id=keyset_id.hex,
+            unit=cdk_ffi.CurrencyUnit.SAT(),
+            active=True,
+            input_fee_ppk=0
+        )
+        await tx.add_mint_keysets(mint_url, [keyset_info])
         await tx.commit()
+        print("✓ Added mint and keyset")
+
+        # Query keyset by ID (read-only)
+        keyset = await db.get_keyset_by_id(keyset_id)
+        assert keyset is not None, "Keyset should exist"
+        assert keyset.id == keyset_id.hex, "Keyset ID should match"
+        print(f"✓ Retrieved keyset: {keyset.id}")
 
-        # Quote operations require proper QuoteState enum construction
-        # which varies by FFI implementation
-        print("✓ Quote operations (basic test - requires proper QuoteState API)")
+        # Query keysets for mint (read-only)
+        keysets = await db.get_mint_keysets(mint_url)
+        assert keysets is not None and len(keysets) > 0, "Should have keysets for mint"
+        print(f"✓ Retrieved {len(keysets)} keyset(s) for mint")
 
-        print("✓ Test passed: Wallet quote operations work correctly")
+        print("✓ Test passed: Keyset management works")
 
     finally:
         if os.path.exists(db_path):
             os.unlink(db_path)
 
 
-async def test_wallet_balance_query():
-    """Test querying wallet balance with different proof states"""
-    print("\n=== Test: Wallet Balance Query ===")
+async def test_wallet_keyset_counter():
+    """Test keyset counter operations"""
+    print("\n=== Test: Wallet Keyset Counter ===")
 
     with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
         db_path = tmp.name
@@ -428,7 +481,7 @@ async def test_wallet_balance_query():
         mint_url = cdk_ffi.MintUrl(url="https://testmint.example.com")
         keyset_id = cdk_ffi.Id(hex="004146bdf4a9afab")
 
-        # Setup
+        # Setup (using transaction)
         tx = await db.begin_db_transaction()
         await tx.add_mint(mint_url, None)
         keyset_info = cdk_ffi.KeySetInfo(
@@ -439,21 +492,32 @@ async def test_wallet_balance_query():
         )
         await tx.add_mint_keysets(mint_url, [keyset_info])
         await tx.commit()
+        print("✓ Setup complete")
 
-        # Balance query requires proper proof creation with PublicKey
-        # which needs proper FFI key generation API
-        print("✓ Balance query (basic test - requires proper PublicKey API for proof creation)")
+        # Increment counter (using transaction)
+        tx = await db.begin_db_transaction()
+        counter1 = await tx.increment_keyset_counter(keyset_id, 1)
+        counter2 = await tx.increment_keyset_counter(keyset_id, 5)
+        counter3 = await tx.increment_keyset_counter(keyset_id, 0)
+        await tx.commit()
 
-        print("✓ Test passed: Wallet balance query works correctly")
+        print(f"✓ Counter after +1: {counter1}")
+        assert counter1 == 1, f"Expected counter 1, got {counter1}"
+        print(f"✓ Counter after +5: {counter2}")
+        assert counter2 == 6, f"Expected counter 6, got {counter2}"
+        print(f"✓ Current counter: {counter3}")
+        assert counter3 == 6, f"Expected counter 6, got {counter3}"
+
+        print("✓ Test passed: Keyset counter works")
 
     finally:
         if os.path.exists(db_path):
             os.unlink(db_path)
 
 
-async def test_wallet_transaction_atomicity():
-    """Test that transaction rollback properly reverts all changes"""
-    print("\n=== Test: Wallet Transaction Atomicity ===")
+async def test_wallet_quotes():
+    """Test mint and melt quote operations"""
+    print("\n=== Test: Wallet Quote Operations ===")
 
     with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
         db_path = tmp.name
@@ -462,66 +526,47 @@ async def test_wallet_transaction_atomicity():
         backend = cdk_ffi.WalletDbBackend.SQLITE(path=db_path)
         db = cdk_ffi.create_wallet_db(backend)
 
-        mint_url1 = cdk_ffi.MintUrl(url="https://mint1.example.com")
-        mint_url2 = cdk_ffi.MintUrl(url="https://mint2.example.com")
-        keyset_id = cdk_ffi.Id(hex="004146bdf4a9afab")
+        mint_url = cdk_ffi.MintUrl(url="https://testmint.example.com")
 
-        # Start a transaction with multiple operations
+        # Add mint (using transaction)
         tx = await db.begin_db_transaction()
+        await tx.add_mint(mint_url, None)
+        await tx.commit()
+        print("✓ Added mint")
 
-        # Add mints
-        await tx.add_mint(mint_url1, None)
-        await tx.add_mint(mint_url2, None)
+        # Query quotes (read-only)
+        mint_quotes = await db.get_mint_quotes()
+        assert isinstance(mint_quotes, list), "get_mint_quotes should return a list"
+        print(f"✓ Retrieved {len(mint_quotes)} mint quote(s)")
 
-        # Add keyset
-        keyset_info = cdk_ffi.KeySetInfo(
-            id=keyset_id.hex,
-            unit=cdk_ffi.CurrencyUnit.SAT(),
-            active=True,
-            input_fee_ppk=0
-        )
-        await tx.add_mint_keysets(mint_url1, [keyset_info])
+        melt_quotes = await db.get_melt_quotes()
+        assert isinstance(melt_quotes, list), "get_melt_quotes should return a list"
+        print(f"✓ Retrieved {len(melt_quotes)} melt quote(s)")
 
-        # Increment counter
-        await tx.increment_keyset_counter(keyset_id, 42)
+        print("✓ Test passed: Quote operations work")
 
-        print("✓ Performed multiple operations in transaction")
+    finally:
+        if os.path.exists(db_path):
+            os.unlink(db_path)
 
-        # Rollback instead of commit
-        await tx.rollback()
-        print("✓ Rolled back transaction")
 
-        # Verify nothing was persisted
-        mints = await db.get_mints()
-        assert len(mints) == 0, f"Expected 0 mints after rollback, got {len(mints)}"
-        print("✓ Mints were not persisted")
+async def test_wallet_proofs_by_ys():
+    """Test retrieving proofs by Y values"""
+    print("\n=== Test: Wallet Get Proofs by Y Values ===")
 
-        # Try to read keyset (should not exist)
-        tx_read = await db.begin_db_transaction()
-        keyset_read = await tx_read.get_keyset_by_id(keyset_id)
-        await tx_read.rollback()
-        assert keyset_read is None, "Keyset should not exist after rollback"
-        print("✓ Keyset was not persisted")
+    with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp:
+        db_path = tmp.name
 
-        # Now commit the same operations
-        tx2 = await db.begin_db_transaction()
-        await tx2.add_mint(mint_url1, None)
-        await tx2.add_mint(mint_url2, None)
-        await tx2.add_mint_keysets(mint_url1, [keyset_info])
-        await tx2.increment_keyset_counter(keyset_id, 42)
-        await tx2.commit()
-        print("✓ Committed transaction with same operations")
+    try:
+        backend = cdk_ffi.WalletDbBackend.SQLITE(path=db_path)
+        db = cdk_ffi.create_wallet_db(backend)
 
-        # Verify keyset and counter were persisted
-        tx_verify = await db.begin_db_transaction()
-        keyset_after = await tx_verify.get_keyset_by_id(keyset_id)
-        assert keyset_after is not None, "Keyset should exist after commit"
-        counter_after = await tx_verify.increment_keyset_counter(keyset_id, 0)
-        await tx_verify.rollback()
-        assert counter_after == 42, f"Expected counter 42, got {counter_after}"
+        # Test with empty list
+        proofs = await db.get_proofs_by_ys([])
+        assert len(proofs) == 0, f"Expected 0 proofs, got {len(proofs)}"
+        print("✓ get_proofs_by_ys returns empty for empty input")
 
-        print("✓ All operations persisted after commit (mints query skipped due to API complexity)")
-        print("✓ Test passed: Transaction atomicity works correctly")
+        print("✓ Test passed: get_proofs_by_ys works")
 
     finally:
         if os.path.exists(db_path):
@@ -530,20 +575,24 @@ async def test_wallet_transaction_atomicity():
 
 async def main():
     """Run all tests"""
-    print("Starting CDK FFI Transaction Tests")
+    print("Starting CDK FFI Wallet and Transaction Tests")
     print("=" * 50)
 
     tests = [
+        # Transaction tests
         ("Increment Counter with Commit", test_increment_keyset_counter_commit),
         ("Implicit Rollback on Drop", test_implicit_rollback_on_drop),
         ("Explicit Rollback", test_explicit_rollback),
         ("Transaction Reads", test_transaction_reads),
         ("Multiple Increments", test_multiple_increments_same_transaction),
-        ("Wallet Mint Operations", test_wallet_mint_operations),
-        ("Wallet Proof Operations", test_wallet_proof_operations),
-        ("Wallet Quote Operations", test_wallet_quote_operations),
-        ("Wallet Balance Query", test_wallet_balance_query),
-        ("Wallet Transaction Atomicity", test_wallet_transaction_atomicity),
+        ("Transaction Atomicity", test_transaction_atomicity),
+        # Wallet tests (read methods + write via transactions)
+        ("Wallet Creation", test_wallet_creation),
+        ("Wallet Mint Management", test_wallet_mint_management),
+        ("Wallet Keyset Management", test_wallet_keyset_management),
+        ("Wallet Keyset Counter", test_wallet_keyset_counter),
+        ("Wallet Quote Operations", test_wallet_quotes),
+        ("Wallet Get Proofs by Y Values", test_wallet_proofs_by_ys),
     ]
 
     passed = 0

+ 3 - 5
crates/cdk-sql-common/src/pool.rs

@@ -208,14 +208,12 @@ where
 
             if self.in_use.load(Ordering::Relaxed) < self.max_size {
                 drop(resources);
-                self.increment_connection_counter();
                 let stale: Arc<AtomicBool> = Arc::new(false.into());
+                let new_resource = RM::new_resource(&self.config, stale.clone(), timeout)?;
+                self.increment_connection_counter();
 
                 return Ok(PooledResource {
-                    resource: Some((
-                        stale.clone(),
-                        RM::new_resource(&self.config, stale, timeout)?,
-                    )),
+                    resource: Some((stale, new_resource)),
                     pool: self.clone(),
                     #[cfg(feature = "prometheus")]
                     start_time: Instant::now(),

+ 1 - 1
crates/cdk-sqlite/src/common.rs

@@ -50,7 +50,7 @@ impl DatabasePool for SqliteConnectionManager {
             // Check if parent directory exists before attempting to open database
             let path_buf = PathBuf::from(path);
             if let Some(parent) = path_buf.parent() {
-                if !parent.exists() {
+                if !parent.to_str().unwrap_or_default().is_empty() && !parent.exists() {
                     return Err(pool::Error::Resource(rusqlite::Error::InvalidPath(
                         path_buf.clone(),
                     )));

+ 10 - 0
crates/cdk-sqlite/src/mint/mod.rs

@@ -32,6 +32,16 @@ mod test {
     mint_db_test!(provide_db);
 
     #[tokio::test]
+    async fn bug_opening_relative_path() {
+        let config: Config = "test.db".into();
+
+        let pool = Pool::<SqliteConnectionManager>::new(config);
+        let db = pool.get();
+        assert!(db.is_ok());
+        let _ = remove_file("test.db");
+    }
+
+    #[tokio::test]
     async fn open_legacy_and_migrate() {
         let file = format!(
             "{}/db.sqlite",