""" Unit tests for the actual application functions from minimal_app.py Tests the real business logic with proper imports """ import pytest import sqlite3 import sys import os from datetime import datetime # Add project root to path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Import functions from minimal_app from minimal_app import ( add_tablet, add_user, loan_tablet, return_tablet, show_available_tablets, show_active_loans, show_loan_history, add_non_loanable_device, show_non_loanable_devices, delete_non_loanable_device ) @pytest.fixture def test_db_path(): """Path to test database""" return 'test_minimal_app.db' @pytest.fixture def init_test_db(test_db_path): """Initialize a fresh test database with schema (same as minimal_app)""" # Remove existing test database if it exists if os.path.exists(test_db_path): os.remove(test_db_path) # Use the same init_db function from minimal_app from minimal_app import init_db # Temporarily rename the database original_db = 'tablets.db' if os.path.exists(original_db): os.rename(original_db, f'{original_db}.backup') try: # Create test database os.environ['TEST_DB'] = test_db_path init_db() yield test_db_path finally: # Cleanup if os.path.exists(test_db_path): os.remove(test_db_path) if os.path.exists(f'{original_db}.backup'): os.rename(f'{original_db}.backup', original_db) if 'TEST_DB' in os.environ: del os.environ['TEST_DB'] @pytest.fixture def clean_db(): """Fixture that ensures we have a clean database for each test""" # This is simpler - just create a temp database for each test import tempfile import shutil # Create temp directory for database temp_dir = tempfile.mkdtemp() db_path = os.path.join(temp_dir, 'test.db') # Initialize database conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS tablets ( id INTEGER PRIMARY KEY AUTOINCREMENT, brand TEXT NOT NULL, model TEXT NOT NULL, serial_number TEXT UNIQUE NOT NULL, status TEXT DEFAULT 'available' ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, identification TEXT UNIQUE ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS loans ( id INTEGER PRIMARY KEY AUTOINCREMENT, tablet_id INTEGER NOT NULL, user_id INTEGER NOT NULL, loan_date TEXT NOT NULL, return_date TEXT, status TEXT DEFAULT 'active', FOREIGN KEY (tablet_id) REFERENCES tablets (id), FOREIGN KEY (user_id) REFERENCES users (id) ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS non_loanable_devices ( id INTEGER PRIMARY KEY AUTOINCREMENT, brand TEXT NOT NULL, model TEXT NOT NULL, serial_number TEXT UNIQUE NOT NULL, device_type TEXT NOT NULL, location TEXT, status TEXT DEFAULT 'available', notes TEXT, purchase_date TEXT, purchase_cost REAL ) ''') conn.commit() conn.close() # Temporarily replace the database original_db = 'tablets.db' backup_path = f'{original_db}.test_backup' # Backup original if exists if os.path.exists(original_db): if os.path.exists(backup_path): os.remove(backup_path) os.rename(original_db, backup_path) # Copy temp db to tablets.db location shutil.copy(db_path, original_db) yield original_db # Cleanup if os.path.exists(original_db): os.remove(original_db) if os.path.exists(backup_path): os.rename(backup_path, original_db) shutil.rmtree(temp_dir, ignore_errors=True) class TestMinimalAppFunctions: """Test the actual functions from minimal_app.py""" def test_add_tablet_function(self, clean_db): """Test the add_tablet function""" add_tablet('TestBrand', 'TestModel', 'TEST_SN_001') # Verify it was added conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT * FROM tablets WHERE serial_number = 'TEST_SN_001'") tablet = cursor.fetchone() conn.close() assert tablet is not None assert tablet[1] == 'TestBrand' # brand is index 1 assert tablet[2] == 'TestModel' # model is index 2 assert tablet[3] == 'TEST_SN_001' # serial_number is index 3 def test_add_tablet_duplicate(self, clean_db, capsys): """Test that duplicate serial numbers are rejected""" add_tablet('Brand1', 'Model1', 'DUP_SN') add_tablet('Brand2', 'Model2', 'DUP_SN') captured = capsys.readouterr() assert 'already exists' in captured.out def test_add_user_function(self, clean_db): """Test the add_user function""" add_user('Test User', 'TEST_ID_001') conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT * FROM users WHERE identification = 'TEST_ID_001'") user = cursor.fetchone() conn.close() assert user is not None assert user[1] == 'Test User' # name is index 1 assert user[2] == 'TEST_ID_001' # identification is index 2 def test_add_user_duplicate(self, clean_db, capsys): """Test that duplicate user identifications are rejected""" add_user('User1', 'DUP_ID') add_user('User2', 'DUP_ID') captured = capsys.readouterr() assert 'already exists' in captured.out def test_loan_tablet_function(self, clean_db): """Test the loan_tablet function""" # Add tablet and user add_tablet('LoanBrand', 'LoanModel', 'LOAN_SN_001') add_user('LoanUser', 'LOAN_ID_001') conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT id FROM tablets WHERE serial_number = 'LOAN_SN_001'") tablet_id = cursor.fetchone()[0] cursor.execute("SELECT id FROM users WHERE identification = 'LOAN_ID_001'") user_id = cursor.fetchone()[0] conn.close() # Loan the tablet loan_tablet(tablet_id, user_id) # Verify loan was created and tablet status changed conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT status FROM tablets WHERE id = ?", (tablet_id,)) status = cursor.fetchone()[0] assert status == 'loaned' cursor.execute("SELECT * FROM loans WHERE tablet_id = ? AND user_id = ?", (tablet_id, user_id)) loan = cursor.fetchone() assert loan is not None assert loan[5] == 'active' # status is index 5 conn.close() def test_loan_already_loaned_tablet(self, clean_db, capsys): """Test loaning a tablet that's already loaned""" add_tablet('Brand', 'Model', 'ALREADY_LOANED_SN') add_user('User1', 'USER1_ID') add_user('User2', 'USER2_ID') conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT id FROM tablets WHERE serial_number = 'ALREADY_LOANED_SN'") tablet_id = cursor.fetchone()[0] cursor.execute("SELECT id FROM users WHERE identification = 'USER1_ID'") user1_id = cursor.fetchone()[0] cursor.execute("SELECT id FROM users WHERE identification = 'USER2_ID'") user2_id = cursor.fetchone()[0] conn.close() # Loan to first user loan_tablet(tablet_id, user1_id) # Try to loan to second user - should fail loan_tablet(tablet_id, user2_id) captured = capsys.readouterr() assert 'not available' in captured.out def test_return_tablet_function(self, clean_db): """Test the return_tablet function""" add_tablet('ReturnBrand', 'ReturnModel', 'RETURN_SN_001') add_user('ReturnUser', 'RETURN_ID_001') conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT id FROM tablets WHERE serial_number = 'RETURN_SN_001'") tablet_id = cursor.fetchone()[0] cursor.execute("SELECT id FROM users WHERE identification = 'RETURN_ID_001'") user_id = cursor.fetchone()[0] conn.close() # Loan the tablet loan_tablet(tablet_id, user_id) # Get loan ID conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT id FROM loans WHERE tablet_id = ?", (tablet_id,)) loan_id = cursor.fetchone()[0] conn.close() # Return the tablet return_tablet(loan_id) # Verify return conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT status FROM tablets WHERE id = ?", (tablet_id,)) status = cursor.fetchone()[0] assert status == 'available' cursor.execute("SELECT status, return_date FROM loans WHERE id = ?", (loan_id,)) loan = cursor.fetchone() assert loan[0] == 'returned' assert loan[1] is not None # return_date should be set conn.close() def test_return_nonexistent_loan(self, clean_db, capsys): """Test returning a loan that doesn't exist""" return_tablet(99999) captured = capsys.readouterr() assert 'not found' in captured.out or 'Loan' in captured.out def test_show_available_tablets(self, clean_db, capsys): """Test showing available tablets""" add_tablet('Avail1', 'Model1', 'AVAIL_SN_001') add_tablet('Avail2', 'Model2', 'AVAIL_SN_002') show_available_tablets() captured = capsys.readouterr() assert 'Available Tablets' in captured.out assert 'AVAIL_SN_001' in captured.out assert 'AVAIL_SN_002' in captured.out def test_show_active_loans(self, clean_db, capsys): """Test showing active loans""" add_tablet('LoanBrand', 'LoanModel', 'ACTIVE_LOAN_SN') add_user('LoanUser', 'ACTIVE_LOAN_ID') conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT id FROM tablets WHERE serial_number = 'ACTIVE_LOAN_SN'") tablet_id = cursor.fetchone()[0] cursor.execute("SELECT id FROM users WHERE identification = 'ACTIVE_LOAN_ID'") user_id = cursor.fetchone()[0] conn.close() loan_tablet(tablet_id, user_id) show_active_loans() captured = capsys.readouterr() assert 'Active Loans' in captured.out assert 'ACTIVE_LOAN_SN' in captured.out def test_non_loanable_device_crud(self, clean_db, capsys): """Test CRUD operations for non-loanable devices""" # Add add_non_loanable_device('Projector', 'P100', 'PROJ_001', 'projector', 'Room A') captured = capsys.readouterr() assert 'Added non-loanable device' in captured.out # Show show_non_loanable_devices() captured = capsys.readouterr() assert 'PROJ_001' in captured.out # Get ID for delete conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT id FROM non_loanable_devices WHERE serial_number = 'PROJ_001'") device_id = cursor.fetchone()[0] conn.close() # Delete delete_non_loanable_device(device_id) captured = capsys.readouterr() assert 'deleted' in captured.out # Verify deletion conn = sqlite3.connect(clean_db) cursor = conn.cursor() cursor.execute("SELECT * FROM non_loanable_devices WHERE serial_number = 'PROJ_001'") device = cursor.fetchone() conn.close() assert device is None