GestionTablets/tests/test_minimal_app.py

366 lines
12 KiB
Python
Raw Permalink Normal View History

"""
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