514 lines
20 KiB
Python
514 lines
20 KiB
Python
|
|
"""
|
||
|
|
Edge case tests for Tablet Management System
|
||
|
|
Tests critical scenarios: already loaned devices, non-existent loans, duplicates, etc.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
import sqlite3
|
||
|
|
from datetime import datetime
|
||
|
|
import sys
|
||
|
|
import os
|
||
|
|
|
||
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
|
|
|
||
|
|
|
||
|
|
class TestLoanEdgeCases:
|
||
|
|
"""Critical edge cases for loan operations"""
|
||
|
|
|
||
|
|
def test_loan_device_already_loaned(self, db_conn):
|
||
|
|
"""
|
||
|
|
CRITICAL: Test that a device already loaned cannot be loaned again
|
||
|
|
This prevents the same physical device from being loaned to multiple users
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add a tablet
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', ('Brand', 'Model', 'ALREADY_LOANED'))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
cursor.execute("SELECT id FROM tablets WHERE serial_number = 'ALREADY_LOANED'")
|
||
|
|
tablet = cursor.fetchone()
|
||
|
|
|
||
|
|
# Add a user
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', ('User1', 'USER1'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM users WHERE identification = 'USER1'")
|
||
|
|
user1 = cursor.fetchone()
|
||
|
|
|
||
|
|
# Add another user
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', ('User2', 'USER2'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM users WHERE identification = 'USER2'")
|
||
|
|
user2 = cursor.fetchone()
|
||
|
|
|
||
|
|
# Loan to first user
|
||
|
|
loan_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet['id'],))
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO loans (tablet_id, user_id, loan_date, status)
|
||
|
|
VALUES (?, ?, ?, 'active')
|
||
|
|
''', (tablet['id'], user1['id'], loan_date))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# Verify tablet is loaned
|
||
|
|
cursor.execute("SELECT status FROM tablets WHERE id = ?", (tablet['id'],))
|
||
|
|
status = cursor.fetchone()['status']
|
||
|
|
assert status == 'loaned'
|
||
|
|
|
||
|
|
# Try to loan to second user - should check status first
|
||
|
|
# In the actual application, this would be prevented by checking status
|
||
|
|
cursor.execute("SELECT status FROM tablets WHERE id = ?", (tablet['id'],))
|
||
|
|
current_status = cursor.fetchone()['status']
|
||
|
|
|
||
|
|
# The application logic should prevent this
|
||
|
|
assert current_status == 'loaned'
|
||
|
|
|
||
|
|
# If we tried to loan it anyway (without checking), we'd get a constraint error
|
||
|
|
# because the tablet status is already 'loaned'
|
||
|
|
# The proper app logic checks status before allowing loan
|
||
|
|
|
||
|
|
def test_return_nonexistent_loan_id(self, db_conn):
|
||
|
|
"""
|
||
|
|
CRITICAL: Test returning a loan that doesn't exist
|
||
|
|
Should handle gracefully without crashing
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Try to return a non-existent loan
|
||
|
|
loan_id = 99999
|
||
|
|
cursor.execute("SELECT tablet_id FROM loans WHERE id = ? AND status = 'active'", (loan_id,))
|
||
|
|
loan = cursor.fetchone()
|
||
|
|
|
||
|
|
# Should return None (no such loan)
|
||
|
|
assert loan is None
|
||
|
|
|
||
|
|
# The application should handle this by showing an error message
|
||
|
|
# rather than crashing
|
||
|
|
|
||
|
|
def test_return_already_returned_loan(self, db_conn):
|
||
|
|
"""
|
||
|
|
CRITICAL: Test returning a loan that's already been returned
|
||
|
|
Should handle gracefully
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add tablet and user
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', ('Brand', 'Model', 'ALREADY_RETURNED'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM tablets WHERE serial_number = 'ALREADY_RETURNED'")
|
||
|
|
tablet = cursor.fetchone()
|
||
|
|
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', ('User', 'RETURN_USER'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM users WHERE identification = 'RETURN_USER'")
|
||
|
|
user = cursor.fetchone()
|
||
|
|
|
||
|
|
# Create and return a loan
|
||
|
|
loan_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
return_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet['id'],))
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO loans (tablet_id, user_id, loan_date, return_date, status)
|
||
|
|
VALUES (?, ?, ?, ?, 'returned')
|
||
|
|
''', (tablet['id'], user['id'], loan_date, return_date))
|
||
|
|
cursor.execute("UPDATE tablets SET status = 'available' WHERE id = ?", (tablet['id'],))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# Get the loan ID
|
||
|
|
cursor.execute("SELECT id FROM loans WHERE tablet_id = ?", (tablet['id'],))
|
||
|
|
loan = cursor.fetchone()
|
||
|
|
loan_id = loan['id']
|
||
|
|
|
||
|
|
# Try to return it again
|
||
|
|
cursor.execute("SELECT tablet_id FROM loans WHERE id = ? AND status = 'active'", (loan_id,))
|
||
|
|
active_loan = cursor.fetchone()
|
||
|
|
|
||
|
|
# Should be None because status is 'returned', not 'active'
|
||
|
|
assert active_loan is None
|
||
|
|
|
||
|
|
def test_loan_with_invalid_tablet_id(self, db_conn):
|
||
|
|
"""
|
||
|
|
CRITICAL: Test loaning with an invalid/non-existent tablet ID
|
||
|
|
Should fail gracefully
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add a user
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', ('User', 'INVALID_USER'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM users WHERE identification = 'INVALID_USER'")
|
||
|
|
user = cursor.fetchone()
|
||
|
|
|
||
|
|
# Try to loan with invalid tablet ID
|
||
|
|
invalid_tablet_id = 99999
|
||
|
|
loan_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
|
||
|
|
# Note: SQLite doesn't enforce foreign keys by default unless we enable it
|
||
|
|
# The application should validate this at the application level
|
||
|
|
# For now, we verify that the tablet doesn't exist
|
||
|
|
cursor.execute("SELECT id FROM tablets WHERE id = ?", (invalid_tablet_id,))
|
||
|
|
tablet_check = cursor.fetchone()
|
||
|
|
assert tablet_check is None # Tablet doesn't exist
|
||
|
|
|
||
|
|
# In a real app with FK enforcement, this would raise IntegrityError
|
||
|
|
# For SQLite without FK enforcement, we just verify the tablet check
|
||
|
|
|
||
|
|
def test_loan_with_invalid_user_id(self, db_conn):
|
||
|
|
"""
|
||
|
|
CRITICAL: Test loaning with an invalid/non-existent user ID
|
||
|
|
Should fail gracefully
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add a tablet
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', ('Brand', 'Model', 'INVALID_LOAN'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM tablets WHERE serial_number = 'INVALID_LOAN'")
|
||
|
|
tablet = cursor.fetchone()
|
||
|
|
|
||
|
|
# Try to loan with invalid user ID
|
||
|
|
invalid_user_id = 99999
|
||
|
|
loan_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
|
||
|
|
# Note: SQLite doesn't enforce foreign keys by default unless we enable it
|
||
|
|
# The application should validate this at the application level
|
||
|
|
# For now, we verify that the user doesn't exist
|
||
|
|
cursor.execute("SELECT id FROM users WHERE id = ?", (invalid_user_id,))
|
||
|
|
user_check = cursor.fetchone()
|
||
|
|
assert user_check is None # User doesn't exist
|
||
|
|
|
||
|
|
# In a real app with FK enforcement, this would raise IntegrityError
|
||
|
|
# For SQLite without FK enforcement, we just verify the user check
|
||
|
|
|
||
|
|
|
||
|
|
class TestDuplicatePrevention:
|
||
|
|
"""Tests for preventing duplicate entries"""
|
||
|
|
|
||
|
|
def test_duplicate_tablet_serial_number(self, db_conn):
|
||
|
|
"""
|
||
|
|
CRITICAL: Test that duplicate tablet serial numbers are prevented
|
||
|
|
Serial numbers must be unique for tracking
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add first tablet
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', ('Brand1', 'Model1', 'DUP_SERIAL'))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# Try to add duplicate
|
||
|
|
with pytest.raises(sqlite3.IntegrityError):
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', ('Brand2', 'Model2', 'DUP_SERIAL'))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
def test_duplicate_user_identification(self, db_conn):
|
||
|
|
"""
|
||
|
|
CRITICAL: Test that duplicate user identifications are prevented
|
||
|
|
User identifications must be unique
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add first user
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', ('User1', 'DUP_ID'))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# Try to add duplicate
|
||
|
|
with pytest.raises(sqlite3.IntegrityError):
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', ('User2', 'DUP_ID'))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
def test_duplicate_non_loanable_device_serial(self, db_conn):
|
||
|
|
"""
|
||
|
|
CRITICAL: Test that duplicate non-loanable device serials are prevented
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add first device
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO non_loanable_devices
|
||
|
|
(brand, model, serial_number, device_type, status)
|
||
|
|
VALUES (?, ?, ?, ?, 'available')
|
||
|
|
''', ('Brand', 'Model', 'DUP_DEVICE_SERIAL', 'projector'))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# Try to add duplicate
|
||
|
|
with pytest.raises(sqlite3.IntegrityError):
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO non_loanable_devices
|
||
|
|
(brand, model, serial_number, device_type, status)
|
||
|
|
VALUES (?, ?, ?, ?, 'available')
|
||
|
|
''', ('Brand2', 'Model2', 'DUP_DEVICE_SERIAL', 'monitor'))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
|
||
|
|
class TestDataIntegrity:
|
||
|
|
"""Tests for data integrity constraints"""
|
||
|
|
|
||
|
|
def test_foreign_key_tablet_deletion(self, db_conn):
|
||
|
|
"""
|
||
|
|
Test that deleting a tablet with active loans is handled
|
||
|
|
SQLite defaults to allowing this, but we should be aware
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add tablet and user
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', ('Brand', 'Model', 'FK_TEST'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM tablets WHERE serial_number = 'FK_TEST'")
|
||
|
|
tablet = cursor.fetchone()
|
||
|
|
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', ('User', 'FK_USER'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM users WHERE identification = 'FK_USER'")
|
||
|
|
user = cursor.fetchone()
|
||
|
|
|
||
|
|
# Create active loan
|
||
|
|
loan_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet['id'],))
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO loans (tablet_id, user_id, loan_date, status)
|
||
|
|
VALUES (?, ?, ?, 'active')
|
||
|
|
''', (tablet['id'], user['id'], loan_date))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# SQLite allows this by default (no ON DELETE RESTRICT)
|
||
|
|
# In production, we might want to add CASCADE or RESTRICT
|
||
|
|
# For now, just verify the loan exists
|
||
|
|
cursor.execute("SELECT COUNT(*) FROM loans WHERE tablet_id = ?", (tablet['id'],))
|
||
|
|
loan_count = cursor.fetchone()[0]
|
||
|
|
assert loan_count == 1
|
||
|
|
|
||
|
|
def test_null_serial_number_prevention(self, db_conn):
|
||
|
|
"""
|
||
|
|
Test that NULL serial numbers are prevented
|
||
|
|
Serial numbers are required (NOT NULL constraint)
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Try to add tablet with NULL serial number
|
||
|
|
with pytest.raises(sqlite3.IntegrityError):
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', ('Brand', 'Model', None))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
def test_null_identification_prevention(self, db_conn):
|
||
|
|
"""
|
||
|
|
Test that NULL user identifications are prevented
|
||
|
|
Identifications are required (NOT NULL constraint)
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Try to add user with NULL identification
|
||
|
|
# Note: identification is NOT marked as NOT NULL in the schema
|
||
|
|
# This test verifies the current behavior
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', ('User', None))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# This should work because identification is not NOT NULL
|
||
|
|
# But in practice, we should have this constraint
|
||
|
|
cursor.execute("SELECT COUNT(*) FROM users WHERE identification IS NULL")
|
||
|
|
count = cursor.fetchone()[0]
|
||
|
|
# This will be 1, showing that NULL is currently allowed
|
||
|
|
# In production, we should add NOT NULL constraint
|
||
|
|
|
||
|
|
def test_empty_string_serial_number(self, db_conn):
|
||
|
|
"""
|
||
|
|
Test handling of empty string serial numbers
|
||
|
|
Empty strings are different from NULL
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add tablet with empty string serial number
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', ('Brand', 'Model', ''))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# This should work (empty string is allowed unless we add CHECK constraint)
|
||
|
|
cursor.execute("SELECT COUNT(*) FROM tablets WHERE serial_number = ''")
|
||
|
|
count = cursor.fetchone()[0]
|
||
|
|
assert count == 1
|
||
|
|
|
||
|
|
# In production, we might want to prevent empty strings
|
||
|
|
# with a CHECK constraint: CHECK(serial_number <> '')
|
||
|
|
|
||
|
|
|
||
|
|
class TestConcurrentScenarioSimulations:
|
||
|
|
"""Simulate scenarios that could cause issues in concurrent environments"""
|
||
|
|
|
||
|
|
def test_loan_return_loan_sequence(self, db_conn):
|
||
|
|
"""
|
||
|
|
Test the sequence: loan -> return -> loan again
|
||
|
|
This simulates a device being loaned multiple times over its lifetime
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add tablet and user
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', ('Brand', 'Model', 'SEQUENCE_TEST'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM tablets WHERE serial_number = 'SEQUENCE_TEST'")
|
||
|
|
tablet = cursor.fetchone()
|
||
|
|
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', ('User', 'SEQUENCE_USER'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM users WHERE identification = 'SEQUENCE_USER'")
|
||
|
|
user = cursor.fetchone()
|
||
|
|
|
||
|
|
# First loan
|
||
|
|
loan_date1 = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet['id'],))
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO loans (tablet_id, user_id, loan_date, status)
|
||
|
|
VALUES (?, ?, ?, 'active')
|
||
|
|
''', (tablet['id'], user['id'], loan_date1))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# Return
|
||
|
|
return_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
cursor.execute("SELECT id FROM loans WHERE tablet_id = ?", (tablet['id'],))
|
||
|
|
loan1 = cursor.fetchone()
|
||
|
|
cursor.execute('''
|
||
|
|
UPDATE loans SET status = 'returned', return_date = ? WHERE id = ?
|
||
|
|
''', (return_date, loan1['id']))
|
||
|
|
cursor.execute("UPDATE tablets SET status = 'available' WHERE id = ?", (tablet['id'],))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# Second loan
|
||
|
|
loan_date2 = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet['id'],))
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO loans (tablet_id, user_id, loan_date, status)
|
||
|
|
VALUES (?, ?, ?, 'active')
|
||
|
|
''', (tablet['id'], user['id'], loan_date2))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# Verify we have 2 loans for this tablet
|
||
|
|
cursor.execute("SELECT COUNT(*) FROM loans WHERE tablet_id = ?", (tablet['id'],))
|
||
|
|
loan_count = cursor.fetchone()[0]
|
||
|
|
assert loan_count == 2
|
||
|
|
|
||
|
|
# Verify 1 active, 1 returned
|
||
|
|
cursor.execute("SELECT COUNT(*) FROM loans WHERE tablet_id = ? AND status = 'active'", (tablet['id'],))
|
||
|
|
active_count = cursor.fetchone()[0]
|
||
|
|
cursor.execute("SELECT COUNT(*) FROM loans WHERE tablet_id = ? AND status = 'returned'", (tablet['id'],))
|
||
|
|
returned_count = cursor.fetchone()[0]
|
||
|
|
|
||
|
|
assert active_count == 1
|
||
|
|
assert returned_count == 1
|
||
|
|
|
||
|
|
def test_multiple_users_multiple_tablets(self, db_conn):
|
||
|
|
"""
|
||
|
|
Test complex scenario with multiple users and tablets
|
||
|
|
Ensures the many-to-many relationship works correctly
|
||
|
|
"""
|
||
|
|
cursor = db_conn.cursor()
|
||
|
|
|
||
|
|
# Add 3 users
|
||
|
|
users = []
|
||
|
|
for i in range(3):
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO users (name, identification)
|
||
|
|
VALUES (?, ?)
|
||
|
|
''', (f'User{i}', f'MULTI_USER_{i}'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM users WHERE identification = ?", (f'MULTI_USER_{i}',))
|
||
|
|
users.append(cursor.fetchone())
|
||
|
|
|
||
|
|
# Add 5 tablets
|
||
|
|
tablets = []
|
||
|
|
for i in range(5):
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
||
|
|
VALUES (?, ?, ?, 'available')
|
||
|
|
''', (f'Brand{i}', f'Model{i}', f'MULTI_TABLET_{i}'))
|
||
|
|
db_conn.commit()
|
||
|
|
cursor.execute("SELECT id FROM tablets WHERE serial_number = ?", (f'MULTI_TABLET_{i}',))
|
||
|
|
tablets.append(cursor.fetchone())
|
||
|
|
|
||
|
|
# Loan tablets to users in a pattern
|
||
|
|
# User 0: tablets 0, 1
|
||
|
|
# User 1: tablets 2, 3
|
||
|
|
# User 2: tablet 4
|
||
|
|
loan_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
|
|
||
|
|
loans_map = {
|
||
|
|
users[0]['id']: [tablets[0]['id'], tablets[1]['id']],
|
||
|
|
users[1]['id']: [tablets[2]['id'], tablets[3]['id']],
|
||
|
|
users[2]['id']: [tablets[4]['id']],
|
||
|
|
}
|
||
|
|
|
||
|
|
for user_id, tablet_ids in loans_map.items():
|
||
|
|
for tablet_id in tablet_ids:
|
||
|
|
cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet_id,))
|
||
|
|
cursor.execute('''
|
||
|
|
INSERT INTO loans (tablet_id, user_id, loan_date, status)
|
||
|
|
VALUES (?, ?, ?, 'active')
|
||
|
|
''', (tablet_id, user_id, loan_date))
|
||
|
|
db_conn.commit()
|
||
|
|
|
||
|
|
# Verify counts
|
||
|
|
cursor.execute("SELECT COUNT(*) FROM loans")
|
||
|
|
total_loans = cursor.fetchone()[0]
|
||
|
|
assert total_loans == 5 # 2 + 2 + 1
|
||
|
|
|
||
|
|
# Verify each user has correct number of loans
|
||
|
|
for user_id, expected_tablet_ids in loans_map.items():
|
||
|
|
cursor.execute("SELECT COUNT(*) FROM loans WHERE user_id = ?", (user_id,))
|
||
|
|
count = cursor.fetchone()[0]
|
||
|
|
assert count == len(expected_tablet_ids)
|
||
|
|
|
||
|
|
# Verify all loaned tablets have correct status
|
||
|
|
cursor.execute("SELECT COUNT(*) FROM tablets WHERE status = 'loaned'")
|
||
|
|
loaned_count = cursor.fetchone()[0]
|
||
|
|
assert loaned_count == 5
|