import uuid from app.models.property import Property, PropertyPhoto def _make_property(slug: str, featured: bool = True, **kwargs) -> Property: defaults = dict( id=uuid.uuid4(), title=f"Imóvel {slug}", slug=slug, address="Rua Teste, 1", price="500000.00", type="venda", bedrooms=2, bathrooms=1, area_m2=80, is_featured=featured, is_active=True, ) defaults.update(kwargs) return Property(**defaults) def test_get_properties_featured_returns_200_with_array(client, db): """GET /api/v1/properties?featured=true returns 200 with an array.""" prop = _make_property("imovel-featured-1") db.session.add(prop) db.session.flush() photo = PropertyPhoto( property_id=prop.id, url="https://picsum.photos/seed/test/800/450", alt_text="Foto de teste", display_order=0, ) db.session.add(photo) db.session.commit() response = client.get("/api/v1/properties?featured=true") assert response.status_code == 200 data = response.get_json() assert isinstance(data, list) assert len(data) >= 1 def test_get_properties_response_contains_required_fields(client, db): """Response contains all required fields: id, title, slug, price, type, etc.""" prop = _make_property("imovel-fields-check") db.session.add(prop) db.session.flush() photo = PropertyPhoto( property_id=prop.id, url="https://picsum.photos/seed/fields/800/450", alt_text="Foto de campos", display_order=0, ) db.session.add(photo) db.session.commit() response = client.get("/api/v1/properties?featured=true") assert response.status_code == 200 data = response.get_json() assert len(data) >= 1 item = data[0] required_fields = [ "id", "title", "slug", "price", "type", "bedrooms", "bathrooms", "area_m2", "photos", ] for field in required_fields: assert field in item, f"Missing field: {field}" assert isinstance(item["photos"], list) def test_get_properties_featured_empty_returns_empty_array(client, db): """GET /api/v1/properties?featured=true returns [] when no featured properties exist.""" # Add a non-featured active property prop = _make_property("imovel-not-featured", featured=False) db.session.add(prop) db.session.commit() response = client.get("/api/v1/properties?featured=true") assert response.status_code == 200 data = response.get_json() assert isinstance(data, list) assert data == [] # ── Text search (q param) ───────────────────────────────────────────────────── def test_q_matches_title(client, db): """?q= returns properties whose title contains that word.""" p1 = _make_property("casa-praia", title="Casa na Praia", price="400000.00") p2 = _make_property("apto-centro", title="Apartamento Centro", price="300000.00") db.session.add_all([p1, p2]) db.session.commit() response = client.get("/api/v1/properties?q=praia") assert response.status_code == 200 data = response.get_json() ids = [item["id"] for item in data["items"]] assert str(p1.id) in ids assert str(p2.id) not in ids def test_q_is_case_insensitive(client, db): """?q search is case-insensitive.""" p = _make_property("casa-jardins", title="Casa nos Jardins", price="600000.00") db.session.add(p) db.session.commit() for term in ("jardins", "JARDINS", "Jardins"): response = client.get(f"/api/v1/properties?q={term}") assert response.status_code == 200 ids = [item["id"] for item in response.get_json()["items"]] assert str(p.id) in ids, f"Expected to find property with q={term!r}" def test_q_matches_address(client, db): """?q search matches against the address field.""" p = _make_property("rua-flores", address="Rua das Flores, 42", title="Imóvel Especial", price="350000.00") db.session.add(p) db.session.commit() response = client.get("/api/v1/properties?q=Flores") assert response.status_code == 200 ids = [item["id"] for item in response.get_json()["items"]] assert str(p.id) in ids def test_q_no_match_returns_empty(client, db): """?q with a term that matches nothing returns total=0 and empty items.""" p = _make_property("imovel-comum", title="Imóvel Comum", price="200000.00") db.session.add(p) db.session.commit() response = client.get("/api/v1/properties?q=xyznaomatch999") assert response.status_code == 200 data = response.get_json() assert data["total"] == 0 assert data["items"] == [] def test_q_truncated_to_200_chars(client, db): """?q longer than 200 chars is accepted without error (truncated server-side).""" long_q = "a" * 300 response = client.get(f"/api/v1/properties?q={long_q}") assert response.status_code == 200 # ── Sort param ──────────────────────────────────────────────────────────────── def _add_props_for_sort(db): """Helper: add three properties with different price/area for sort tests.""" props = [ _make_property("sort-cheap", title="Barato", price="100000.00", area_m2=50), _make_property("sort-mid", title="Médio", price="300000.00", area_m2=80), _make_property("sort-exp", title="Caro", price="600000.00", area_m2=120), ] db.session.add_all(props) db.session.commit() return props def test_sort_price_asc(client, db): """?sort=price_asc returns properties ordered from cheapest to most expensive.""" _add_props_for_sort(db) response = client.get("/api/v1/properties?sort=price_asc") assert response.status_code == 200 items = response.get_json()["items"] prices = [float(item["price"]) for item in items] assert prices == sorted(prices), "Items should be sorted price ascending" def test_sort_price_desc(client, db): """?sort=price_desc returns properties from most to least expensive.""" _add_props_for_sort(db) response = client.get("/api/v1/properties?sort=price_desc") assert response.status_code == 200 items = response.get_json()["items"] prices = [float(item["price"]) for item in items] assert prices == sorted(prices, reverse=True), "Items should be sorted price descending" def test_sort_area_desc(client, db): """?sort=area_desc returns properties from largest to smallest area.""" _add_props_for_sort(db) response = client.get("/api/v1/properties?sort=area_desc") assert response.status_code == 200 items = response.get_json()["items"] areas = [item["area_m2"] for item in items] assert areas == sorted(areas, reverse=True), "Items should be sorted area descending" def test_sort_unknown_value_falls_back_gracefully(client, db): """?sort= returns 200 (falls back to default sort).""" p = _make_property("sort-fallback", price="200000.00") db.session.add(p) db.session.commit() response = client.get("/api/v1/properties?sort=invalid_value") assert response.status_code == 200 def test_paginated_response_shape(client, db): """Paginated listing endpoint returns items, total, page, per_page, pages.""" p = _make_property("paginated-shape", price="250000.00") db.session.add(p) db.session.commit() response = client.get("/api/v1/properties?per_page=5") assert response.status_code == 200 data = response.get_json() for key in ("items", "total", "page", "per_page", "pages"): assert key in data, f"Missing key in paginated response: {key}" assert isinstance(data["items"], list) assert data["total"] >= 1