"""Integration tests for printer and client CRUD endpoints.""" import json import pytest from fastapi.testclient import TestClient # --------------------------------------------------------------------------- # Task 1: RED state — routes do not exist yet, tests should fail # --------------------------------------------------------------------------- def test_create_printer_persisted(client: TestClient) -> None: """POST /printers with required fields returns 303; GET /printers contains printer name.""" from imptune.db.models import Printer resp = client.post( "/printers", data={ "name": "Test Printer", "ip_address": "192.168.1.100", "port_name": "IP_192_168_1_100", }, follow_redirects=False, ) assert resp.status_code == 303 # GET /printers page contains the printer name page = client.get("/printers") assert page.status_code == 200 assert "Test Printer" in page.text # Verify DB persistence count = Printer.select().where(Printer.name == "Test Printer").count() assert count == 1 def test_create_printer_duplex(client: TestClient) -> None: """POST /printers with duplex_mode=LongEdge persists correctly.""" from imptune.db.models import Printer resp = client.post( "/printers", data={ "name": "Duplex Printer", "ip_address": "192.168.1.101", "port_name": "IP_192_168_1_101", "duplex_mode": "LongEdge", }, follow_redirects=False, ) assert resp.status_code == 303 printers = list(Printer.select().where(Printer.name == "Duplex Printer")) assert len(printers) == 1 assert printers[0].duplex_mode == "LongEdge" def test_create_printer_color_mode(client: TestClient) -> None: """POST /printers with color_mode not sent (unchecked) sets color_mode=False.""" from imptune.db.models import Printer resp = client.post( "/printers", data={ "name": "Mono Printer", "ip_address": "192.168.1.102", "port_name": "IP_192_168_1_102", # color_mode intentionally omitted (unchecked checkbox) }, follow_redirects=False, ) assert resp.status_code == 303 printers = list(Printer.select().where(Printer.name == "Mono Printer")) assert len(printers) == 1 assert printers[0].color_mode is False def test_create_printer_paper_size(client: TestClient) -> None: """POST /printers with paper_size=Letter persists correctly.""" from imptune.db.models import Printer resp = client.post( "/printers", data={ "name": "Letter Printer", "ip_address": "192.168.1.103", "port_name": "IP_192_168_1_103", "paper_size": "Letter", }, follow_redirects=False, ) assert resp.status_code == 303 printers = list(Printer.select().where(Printer.name == "Letter Printer")) assert len(printers) == 1 assert printers[0].paper_size == "Letter" def test_create_printer_collate(client: TestClient) -> None: """POST /printers with collate not sent (unchecked) sets collate=False.""" from imptune.db.models import Printer resp = client.post( "/printers", data={ "name": "No Collate Printer", "ip_address": "192.168.1.104", "port_name": "IP_192_168_1_104", # collate intentionally omitted (unchecked checkbox) }, follow_redirects=False, ) assert resp.status_code == 303 printers = list(Printer.select().where(Printer.name == "No Collate Printer")) assert len(printers) == 1 assert printers[0].collate is False def test_create_client(client: TestClient) -> None: """POST /clients with name=Contoso returns 200; GET /clients contains Contoso.""" resp = client.post("/clients", data={"name": "Contoso"}) assert resp.status_code == 200 page = client.get("/clients") assert page.status_code == 200 assert "Contoso" in page.text def test_printer_grouped_by_client(client: TestClient) -> None: """Printer assigned to a client appears under that client group heading.""" from imptune.db.models import Client, Printer # Create client resp = client.post("/clients", data={"name": "Contoso"}) assert resp.status_code == 200 contoso_list = list(Client.select().where(Client.name == "Contoso")) assert len(contoso_list) == 1 contoso = contoso_list[0] # Create printer assigned to that client resp = client.post( "/printers", data={ "name": "Contoso Printer", "ip_address": "10.0.0.1", "port_name": "IP_10_0_0_1", "client_id": str(contoso.id), }, follow_redirects=False, ) assert resp.status_code == 303 # GET /printers should show "Contoso" as a group header page = client.get("/printers") assert page.status_code == 200 html = page.text assert "Contoso" in html # Client name should appear in a heading element assert " None: """POST /printers with empty name returns 400.""" resp = client.post( "/printers", data={ "name": "", "ip_address": "192.168.1.100", "port_name": "IP_192_168_1_100", }, ) assert resp.status_code == 400 def test_create_printer_invalid_ip(client: TestClient) -> None: """POST /printers with empty ip_address returns 400.""" resp = client.post( "/printers", data={ "name": "Valid Name", "ip_address": "", "port_name": "SOME_PORT", }, ) assert resp.status_code == 400 def test_printer_detail_shows_driver(client: TestClient) -> None: """GET /printers/{id} returns 200 with all printer fields and driver name.""" from imptune.db.models import Driver, Printer driver_obj = Driver.create( sha256="abc123", original_filename="hp_universal.zip", size_bytes=1000, driver_desc=json.dumps(["HP Universal"]), ) printer = Printer.create( name="HP Office Printer", ip_address="10.0.1.1", port_name="IP_10_0_1_1", driver=driver_obj, ) resp = client.get(f"/printers/{printer.id}") assert resp.status_code == 200 html = resp.text assert "HP Office Printer" in html assert "10.0.1.1" in html assert "HP Universal" in html def test_printer_detail_not_found(client: TestClient) -> None: """GET /printers/9999 returns 404.""" resp = client.get("/printers/9999") assert resp.status_code == 404 def test_printer_detail_no_driver(client: TestClient) -> None: """GET /printers/{id} for printer with no driver returns 200 with 'No driver assigned'.""" from imptune.db.models import Printer printer = Printer.create( name="Driverless Printer", ip_address="10.0.1.2", port_name="IP_10_0_1_2", driver=None, ) resp = client.get(f"/printers/{printer.id}") assert resp.status_code == 200 html = resp.text assert "No driver assigned" in html def test_delete_printer(client: TestClient) -> None: """DELETE /printers/{id} removes the printer; GET /printers no longer shows it.""" from imptune.db.models import Printer # Create a printer resp = client.post( "/printers", data={ "name": "To Delete", "ip_address": "192.168.1.200", "port_name": "IP_192_168_1_200", }, follow_redirects=False, ) assert resp.status_code == 303 # Find its ID printers = list(Printer.select().where(Printer.name == "To Delete")) assert len(printers) == 1 printer_id = printers[0].id # Delete it resp = client.delete(f"/printers/{printer_id}") assert resp.status_code == 200 # Confirm it's gone from the DB count = Printer.select().where(Printer.id == printer_id).count() assert count == 0 # GET /printers should no longer show it page = client.get("/printers") assert "To Delete" not in page.text # --------------------------------------------------------------------------- # Wave 0 scaffolds — UIE-01 (PATCH), UIE-02 (separated form/list), UIE-03 (client detail) # These tests are RED until plan 01 task 2 and plans 02-03 implement the routes. # --------------------------------------------------------------------------- def test_printers_new_returns_200(client: TestClient) -> None: """GET /printers/new returns 200 with the add printer form markup.""" resp = client.get("/printers/new") assert resp.status_code == 200 html = resp.text # Must contain form fields assert 'name="name"' in html or "Printer Name" in html def test_create_printer_redirects(client: TestClient) -> None: """POST /printers (no HX-Request header) returns 303 redirect to /printers.""" resp = client.post( "/printers", data={ "name": "Redirect Printer", "ip_address": "192.168.2.1", "port_name": "IP_192_168_2_1", }, follow_redirects=False, ) assert resp.status_code == 303 assert resp.headers["location"] == "/printers" def test_printers_library_no_form(client: TestClient) -> None: """GET /printers does NOT contain the inline add-printer form markup.""" resp = client.get("/printers") assert resp.status_code == 200 html = resp.text # The inline form must be gone assert 'hx-post="/printers"' not in html assert "Save Printer" not in html def test_patch_printer(client: TestClient) -> None: """PATCH /printers/{id} with updated name returns 200, updated name in response, DB updated.""" from imptune.db.models import Printer printer = Printer.create( name="Original Name", ip_address="10.0.2.1", port_name="IP_10_0_2_1", ) resp = client.patch( f"/printers/{printer.id}", data={"name": "Updated Name"}, ) assert resp.status_code == 200 assert "Updated Name" in resp.text # Verify DB update updated = Printer.get_by_id(printer.id) assert updated.name == "Updated Name" def test_patch_printer_not_found(client: TestClient) -> None: """PATCH /printers/9999 returns 404.""" resp = client.patch("/printers/9999", data={"name": "Ghost"}) assert resp.status_code == 404 def test_client_detail_returns_200(client: TestClient) -> None: """GET /clients/{id} returns 200 with client name and assigned printer name.""" from imptune.db.models import Client, Printer # Create client cl = Client.create(name="Detail Client") # Create printer assigned to that client Printer.create( name="Client Printer", ip_address="10.0.3.1", port_name="IP_10_0_3_1", client=cl, ) resp = client.get(f"/clients/{cl.id}") assert resp.status_code == 200 html = resp.text assert "Detail Client" in html assert "Client Printer" in html def test_client_detail_not_found(client: TestClient) -> None: """GET /clients/9999 returns 404.""" resp = client.get("/clients/9999") assert resp.status_code == 404 def test_client_links_in_printer_list(client: TestClient) -> None: """GET /printers with a printer assigned to a client contains href to client detail.""" from imptune.db.models import Client, Printer # Create client via POST /clients resp = client.post("/clients", data={"name": "Link Client"}) assert resp.status_code == 200 cl = Client.get(Client.name == "Link Client") # Create printer assigned to that client Printer.create( name="Linked Printer", ip_address="10.0.4.1", port_name="IP_10_0_4_1", client=cl, ) resp = client.get("/printers") assert resp.status_code == 200 assert f'href="/clients/{cl.id}"' in resp.text