SmokeScan / tests /test_ui_state.py
KinetoLabs's picture
Initial commit: FDAM AI Pipeline v4.0.1
88bdcff
raw
history blame
11.4 kB
"""Tests for UI state management."""
import json
import pytest
from ui.state import (
SessionState,
AssessmentHistory,
ProjectFormData,
RoomFormData,
ImageFormData,
ObservationsFormData,
create_new_session,
session_to_json,
session_from_json,
history_to_json,
history_from_json,
)
from ui.components import (
create_validation_message,
create_room_table_data,
create_history_dropdown_choices,
create_stats_dict,
ImageStore,
)
class TestSessionState:
"""Test SessionState model."""
def test_create_new_session(self):
session = create_new_session()
assert session.session_id is not None
assert len(session.session_id) == 32 # UUID hex
assert session.tab1_complete is False
assert len(session.rooms) == 0
def test_session_serialization(self):
session = SessionState()
session.project.project_name = "Test Project"
session.rooms.append(RoomFormData(
name="Room 1",
length_ft=100,
width_ft=50,
ceiling_height_ft=20,
))
# Serialize
json_str = session_to_json(session)
assert "Test Project" in json_str
assert "Room 1" in json_str
# Deserialize
loaded = session_from_json(json_str)
assert loaded.project.project_name == "Test Project"
assert len(loaded.rooms) == 1
assert loaded.rooms[0].name == "Room 1"
def test_session_validation_tab1_incomplete(self):
session = SessionState()
is_valid, errors = session.validate_tab1()
assert is_valid is False
assert "Project name is required" in errors
assert "Address is required" in errors
def test_session_validation_tab1_complete(self):
session = SessionState()
session.project = ProjectFormData(
project_name="Test Project",
address="123 Main St",
city="Springfield",
state="IL",
zip_code="62701",
client_name="Test Client",
fire_date="2024-12-01",
assessment_date="2024-12-15",
assessor_name="John Smith",
)
is_valid, errors = session.validate_tab1()
assert is_valid is True
assert len(errors) == 0
def test_session_validation_tab2_no_rooms(self):
session = SessionState()
is_valid, errors = session.validate_tab2()
assert is_valid is False
assert "At least one room is required" in errors
def test_session_validation_tab2_invalid_dimensions(self):
session = SessionState()
session.rooms.append(RoomFormData(
name="Room 1",
length_ft=0, # Invalid
width_ft=50,
ceiling_height_ft=20,
))
is_valid, errors = session.validate_tab2()
assert is_valid is False
assert any("Length must be greater than 0" in e for e in errors)
def test_session_validation_tab2_complete(self):
session = SessionState()
session.rooms.append(RoomFormData(
name="Room 1",
length_ft=100,
width_ft=50,
ceiling_height_ft=20,
))
is_valid, errors = session.validate_tab2()
assert is_valid is True
def test_session_validation_tab3_no_images(self):
session = SessionState()
is_valid, errors = session.validate_tab3()
assert is_valid is False
assert "At least one image is required" in errors
def test_session_validation_tab3_complete(self):
session = SessionState()
session.rooms.append(RoomFormData(id="room-001", name="Room 1"))
session.images.append(ImageFormData(
filename="test.jpg",
room_id="room-001",
))
is_valid, errors = session.validate_tab3()
assert is_valid is True
def test_session_can_generate(self):
session = SessionState()
# Fill all required fields
session.project = ProjectFormData(
project_name="Test",
address="123 Main",
city="City",
state="IL",
zip_code="12345",
client_name="Client",
fire_date="2024-01-01",
assessment_date="2024-01-02",
assessor_name="Assessor",
)
session.rooms.append(RoomFormData(
id="room-001",
name="Room 1",
length_ft=100,
width_ft=50,
ceiling_height_ft=20,
))
session.images.append(ImageFormData(
filename="test.jpg",
room_id="room-001",
))
can_gen, errors = session.can_generate()
assert can_gen is True
assert len(errors) == 0
def test_session_display_name(self):
session = SessionState()
# Default name from session ID
assert session.session_id[:8] in session.get_display_name()
# Name from project
session.project.project_name = "My Project"
assert session.get_display_name() == "My Project"
# Explicit name takes priority
session.name = "Custom Name"
assert session.get_display_name() == "Custom Name"
class TestAssessmentHistory:
"""Test AssessmentHistory model."""
def test_empty_history(self):
history = AssessmentHistory()
assert len(history.assessments) == 0
assert history.current_session_id is None
def test_add_assessment(self):
history = AssessmentHistory()
session = SessionState()
session.project.project_name = "Test"
history.add_assessment(session)
assert len(history.assessments) == 1
assert history.assessments[0].session_id == session.session_id
def test_add_assessment_updates_existing(self):
history = AssessmentHistory()
session = SessionState()
session.project.project_name = "Original"
history.add_assessment(session)
# Update and re-add
session.project.project_name = "Updated"
history.add_assessment(session)
# Should still have only 1 entry
assert len(history.assessments) == 1
assert history.assessments[0].project.project_name == "Updated"
def test_history_limit(self):
history = AssessmentHistory()
# Add 25 assessments
for i in range(25):
session = SessionState()
session.project.project_name = f"Project {i}"
history.add_assessment(session)
# Should only keep 20
assert len(history.assessments) == 20
# Most recent should be first
assert history.assessments[0].project.project_name == "Project 24"
def test_get_assessment(self):
history = AssessmentHistory()
session = SessionState()
history.add_assessment(session)
retrieved = history.get_assessment(session.session_id)
assert retrieved is not None
assert retrieved.session_id == session.session_id
# Non-existent
assert history.get_assessment("nonexistent") is None
def test_remove_assessment(self):
history = AssessmentHistory()
session = SessionState()
history.add_assessment(session)
history.remove_assessment(session.session_id)
assert len(history.assessments) == 0
def test_history_serialization(self):
history = AssessmentHistory()
session = SessionState()
session.project.project_name = "Test Project"
history.add_assessment(session)
json_str = history_to_json(history)
loaded = history_from_json(json_str)
assert len(loaded.assessments) == 1
assert loaded.assessments[0].project.project_name == "Test Project"
def test_history_items(self):
history = AssessmentHistory()
session = SessionState()
session.project.project_name = "Test Project"
session.has_results = True
history.add_assessment(session)
items = history.get_history_items()
assert len(items) == 1
assert items[0]["name"] == "Test Project"
assert items[0]["has_results"] is True
class TestUIComponents:
"""Test UI component helpers."""
def test_validation_message_success(self):
msg = create_validation_message(True, [], "All good!")
assert "✓" in msg
assert "All good!" in msg
def test_validation_message_failure(self):
msg = create_validation_message(False, ["Error 1", "Error 2"])
assert "⚠" in msg
assert "Error 1" in msg
assert "Error 2" in msg
def test_room_table_data(self):
session = SessionState()
session.rooms.append(RoomFormData(
name="Room 1",
length_ft=100,
width_ft=50,
ceiling_height_ft=20,
))
data = create_room_table_data(session)
assert len(data) == 1
assert data[0][0] == "Room 1"
assert "100 x 50 x 20" in data[0][1]
assert "5,000" in data[0][2] # Area
assert "100,000" in data[0][3] # Volume
def test_history_dropdown_choices(self):
history = AssessmentHistory()
session = SessionState()
session.project.project_name = "Test Project"
history.add_assessment(session)
choices = create_history_dropdown_choices(history)
assert len(choices) == 2 # "New Assessment" + 1 saved
assert choices[0][0] == "-- New Assessment --"
assert "Test Project" in choices[1][0]
def test_stats_dict(self):
session = SessionState()
session.rooms.append(RoomFormData(
name="Room 1",
length_ft=100,
width_ft=50,
ceiling_height_ft=20,
))
session.images.append(ImageFormData(filename="test.jpg", room_id="room-001"))
stats = create_stats_dict(session)
assert stats["rooms"] == 1
assert stats["images"] == 1
assert stats["total_floor_area_sf"] == "5,000"
assert stats["total_volume_cf"] == "100,000"
class TestImageStore:
"""Test ImageStore for in-memory image storage."""
def test_store_and_get(self):
store = ImageStore()
store.store("img-001", b"test image bytes")
assert store.get("img-001") == b"test image bytes"
assert store.get("nonexistent") is None
def test_remove(self):
store = ImageStore()
store.store("img-001", b"test")
store.remove("img-001")
assert store.get("img-001") is None
def test_clear(self):
store = ImageStore()
store.store("img-001", b"test1")
store.store("img-002", b"test2")
store.clear()
assert store.get("img-001") is None
assert store.get("img-002") is None
def test_missing_ids(self):
store = ImageStore()
store.store("img-001", b"test")
missing = store.get_missing_ids(["img-001", "img-002", "img-003"])
assert missing == ["img-002", "img-003"]
def test_has_all(self):
store = ImageStore()
store.store("img-001", b"test1")
store.store("img-002", b"test2")
assert store.has_all(["img-001", "img-002"]) is True
assert store.has_all(["img-001", "img-003"]) is False