193 lines
5.9 KiB
Python
193 lines
5.9 KiB
Python
import os
|
|
import uuid
|
|
import csv
|
|
import io
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
# Ensure required env vars for app import/config
|
|
os.environ.setdefault("SECRET_KEY", "x" * 32)
|
|
os.environ.setdefault("DATABASE_URL", "sqlite:////tmp/delphi_test.sqlite")
|
|
|
|
from app.main import app # noqa: E402
|
|
from app.auth.security import get_current_user # noqa: E402
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def client():
|
|
# Override auth to bypass JWT for these tests
|
|
class _User:
|
|
def __init__(self):
|
|
self.id = "test"
|
|
self.username = "tester"
|
|
self.is_admin = True
|
|
self.is_active = True
|
|
|
|
app.dependency_overrides[get_current_user] = lambda: _User()
|
|
|
|
try:
|
|
yield TestClient(app)
|
|
finally:
|
|
app.dependency_overrides.pop(get_current_user, None)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def phone_book_data(client: TestClient):
|
|
gid1 = f"PBGRP1-{uuid.uuid4().hex[:6]}"
|
|
gid2 = f"PBGRP2-{uuid.uuid4().hex[:6]}"
|
|
|
|
def _create_customer(cid: str, last: str, first: str, group: str):
|
|
payload = {
|
|
"id": cid,
|
|
"last": last,
|
|
"first": first,
|
|
"group": group,
|
|
"email": f"{cid.lower()}@example.com",
|
|
}
|
|
resp = client.post("/api/customers/", json=payload)
|
|
assert resp.status_code == 200, resp.text
|
|
|
|
def _add_phone(cid: str, location: str, number: str):
|
|
resp = client.post(f"/api/customers/{cid}/phones", json={"location": location, "phone": number})
|
|
assert resp.status_code == 200, resp.text
|
|
|
|
# Create entries for letters A, B and non-alpha '#'
|
|
cid_a1 = f"PB-{uuid.uuid4().hex[:6]}-A1"
|
|
cid_a2 = f"PB-{uuid.uuid4().hex[:6]}-A2"
|
|
cid_b1 = f"PB-{uuid.uuid4().hex[:6]}-B1"
|
|
cid_hash = f"PB-{uuid.uuid4().hex[:6]}-H"
|
|
|
|
_create_customer(cid_a1, last="Alpha", first="Alice", group=gid1)
|
|
_add_phone(cid_a1, "Office", "111-111-1111")
|
|
_add_phone(cid_a1, "Mobile", "111-111-2222")
|
|
|
|
_create_customer(cid_a2, last="Able", first="Andy", group=gid1)
|
|
_add_phone(cid_a2, "Office", "222-222-2222")
|
|
|
|
_create_customer(cid_b1, last="Beta", first="Bob", group=gid2)
|
|
_add_phone(cid_b1, "Home", "333-333-3333")
|
|
|
|
_create_customer(cid_hash, last="123Company", first="NA", group=gid1)
|
|
_add_phone(cid_hash, "Main", "444-444-4444")
|
|
|
|
try:
|
|
yield {
|
|
"gid1": gid1,
|
|
"gid2": gid2,
|
|
"ids": [cid_a1, cid_a2, cid_b1, cid_hash],
|
|
}
|
|
finally:
|
|
# Cleanup
|
|
for cid in [cid_a1, cid_a2, cid_b1, cid_hash]:
|
|
client.delete(f"/api/customers/{cid}")
|
|
|
|
|
|
def _parse_csv(text: str):
|
|
reader = csv.reader(io.StringIO(text))
|
|
rows = list(reader)
|
|
return rows[0], rows[1:]
|
|
|
|
|
|
def test_phone_book_csv_letter_column_when_grouped_by_letter(client: TestClient, phone_book_data):
|
|
# Only include our test group gid1 to avoid interference
|
|
gid = phone_book_data["gid1"]
|
|
resp = client.get(
|
|
"/api/customers/phone-book",
|
|
params={
|
|
"format": "csv",
|
|
"mode": "numbers",
|
|
"grouping": "letter",
|
|
"groups": gid,
|
|
},
|
|
)
|
|
assert resp.status_code == 200
|
|
assert "text/csv" in resp.headers.get("content-type", "")
|
|
header, rows = _parse_csv(resp.text)
|
|
assert "Letter" in header
|
|
# Ensure letters include 'A' and '#'
|
|
letters = {r[header.index("Letter")] for r in rows}
|
|
assert "A" in letters and "#" in letters
|
|
|
|
|
|
def test_phone_book_html_sections_by_letter_with_page_break(client: TestClient, phone_book_data):
|
|
gid = phone_book_data["gid1"]
|
|
resp = client.get(
|
|
"/api/customers/phone-book",
|
|
params={
|
|
"format": "html",
|
|
"mode": "numbers",
|
|
"grouping": "letter",
|
|
"page_break": "1",
|
|
"groups": gid,
|
|
},
|
|
)
|
|
assert resp.status_code == 200
|
|
html = resp.text
|
|
# Snapshot-style checks: section headers and page-break class
|
|
assert "Letter: A" in html
|
|
assert "Letter: #" in html
|
|
assert "class=\"section-title\"" in html
|
|
assert "page-break" in html # later sections should have page-break class
|
|
|
|
|
|
def test_phone_book_html_group_then_letter_sections(client: TestClient, phone_book_data):
|
|
gid1 = phone_book_data["gid1"]
|
|
gid2 = phone_book_data["gid2"]
|
|
# Include both groups to verify group and nested letter sections
|
|
resp = client.get(
|
|
"/api/customers/phone-book",
|
|
params=[
|
|
("format", "html"),
|
|
("mode", "addresses"),
|
|
("grouping", "group_letter"),
|
|
("groups", gid1),
|
|
("groups", gid2),
|
|
],
|
|
)
|
|
assert resp.status_code == 200
|
|
html = resp.text
|
|
assert f"Group: {gid1}" in html
|
|
assert f"Group: {gid2}" in html
|
|
assert "Letter: A" in html or "Letter: #" in html
|
|
|
|
|
|
def test_phone_book_csv_no_letter_for_grouping_none(client: TestClient, phone_book_data):
|
|
gid = phone_book_data["gid1"]
|
|
resp = client.get(
|
|
"/api/customers/phone-book",
|
|
params={
|
|
"format": "csv",
|
|
"mode": "numbers",
|
|
"grouping": "none",
|
|
"groups": gid,
|
|
},
|
|
)
|
|
assert resp.status_code == 200
|
|
header, rows = _parse_csv(resp.text)
|
|
assert "Letter" not in header
|
|
# Basic sanity: names and phones are present
|
|
assert any("Alpha" in ",".join(row) or "Able" in ",".join(row) for row in rows)
|
|
|
|
|
|
def test_phone_book_respects_group_filter(client: TestClient, phone_book_data):
|
|
gid2 = phone_book_data["gid2"]
|
|
resp = client.get(
|
|
"/api/customers/phone-book",
|
|
params={
|
|
"format": "csv",
|
|
"mode": "numbers",
|
|
"grouping": "letter",
|
|
"groups": gid2,
|
|
},
|
|
)
|
|
assert resp.status_code == 200
|
|
header, rows = _parse_csv(resp.text)
|
|
# Only Beta/Bob (gid2) should be present
|
|
all_text = "\n".join([",".join(r) for r in rows])
|
|
assert "Beta" in all_text or "Bob" in all_text
|
|
# Ensure gid1 names are not present
|
|
assert "Alpha" not in all_text and "Able" not in all_text
|
|
|
|
|