from types import SimpleNamespace from sqlalchemy.dialects import sqlite from app.services.customers_search import ( apply_customer_filters, apply_customer_sorting, prepare_customer_csv_rows, ) class FakeQuery: """Lightweight stand-in for SQLAlchemy Query that captures filters and orderings. We only need to verify that our helper functions add the expected number of filter/order_by clauses and roughly target the expected columns. We do not execute any SQL. """ def __init__(self): self.filters = [] self.orderings = [] def filter(self, *args): self.filters.extend(args) return self def order_by(self, *args): self.orderings.extend(args) return self def compile_sql(expr): """Compile a SQLAlchemy expression to a SQLite SQL string for simple assertions.""" try: return str(expr.compile(dialect=sqlite.dialect())) except Exception: return str(expr) def test_apply_customer_filters_search_and_comma_pattern(): q = FakeQuery() q = apply_customer_filters(q, search="Smith, John", group=None, state=None, groups=None, states=None) # One filter clause added (combined search filter) assert len(q.filters) == 1 sql = compile_sql(q.filters[0]) assert "last" in sql and "first" in sql def test_apply_customer_filters_groups_and_states(): q = FakeQuery() q = apply_customer_filters(q, search=None, group="A", state="NY", groups=None, states=None) # Two filter clauses added: group and state assert len(q.filters) == 2 sql_group = compile_sql(q.filters[0]) sql_state = compile_sql(q.filters[1]) assert "group" in sql_group assert "abrev" in sql_state or "state" in sql_state def test_apply_customer_filters_multi_groups_priority(): q = FakeQuery() q = apply_customer_filters(q, search=None, group="A", state=None, groups=["X", "Y"], states=None) # Only one filter (multi-groups) should be applied for groups assert len(q.filters) == 1 assert "IN" in compile_sql(q.filters[0]) def test_apply_customer_sorting_fields_and_direction(): # name sorting => two orderings q1 = FakeQuery() q1 = apply_customer_sorting(q1, sort_by="name", sort_dir="asc") assert len(q1.orderings) == 2 assert "last" in compile_sql(q1.orderings[0]) assert "first" in compile_sql(q1.orderings[1]) # id sorting desc => one ordering and DESC direction in SQL q2 = FakeQuery() q2 = apply_customer_sorting(q2, sort_by="id", sort_dir="desc") assert len(q2.orderings) == 1 assert "DESC" in compile_sql(q2.orderings[0]).upper() # unknown field falls back to id q3 = FakeQuery() q3 = apply_customer_sorting(q3, sort_by="unknown", sort_dir="asc") assert len(q3.orderings) == 1 assert "id" in compile_sql(q3.orderings[0]).lower() def test_prepare_customer_csv_rows_default_and_selected_fields(): cust1 = SimpleNamespace( id="001", first="John", last="Smith", group="G1", city="New York", abrev="NY", email="john@example.com", phone_numbers=[SimpleNamespace(phone="123-456-7890")], ) cust2 = SimpleNamespace( id="002", first="Jane", last="Doe", group="G2", city="Boston", abrev="MA", email="jane@example.com", phone_numbers=[], ) # Default fields header, rows = prepare_customer_csv_rows([cust1, cust2], fields=None) assert header == [ "Customer ID", "Name", "Group", "City", "State", "Primary Phone", "Email", ] assert rows[0][0] == "001" assert rows[0][1] == "John Smith" assert rows[0][2] == "G1" assert rows[0][3] == "New York" assert rows[0][4] == "NY" assert rows[0][5] == "123-456-7890" assert rows[0][6] == "john@example.com" # Selected subset of fields header_sel, rows_sel = prepare_customer_csv_rows([cust1], fields=["id", "name", "email"]) # any case ok assert header_sel == ["Customer ID", "Name", "Email"] assert rows_sel[0] == ["001", "John Smith", "john@example.com"]