Skip to content

Commit b466a3b

Browse files
committed
refactor: use starlette.testclient.TestClient, simplify tests via parametrize
1 parent 938b598 commit b466a3b

File tree

1 file changed

+40
-60
lines changed

1 file changed

+40
-60
lines changed

tests/api/test_links_with_root_path.py

Lines changed: 40 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import importlib
22

33
import pytest
4-
from httpx import ASGITransport, AsyncClient
4+
from starlette.testclient import TestClient
55

66
from stac_fastapi.pgstac.db import close_db_connection, connect_to_db
77

8-
root_path_value = "/stac/v1"
9-
10-
# Append the root path to the base URL, this is key to reproducing the issue where the root path appears twice in some links
11-
base_url = f"http://api.acme.com{root_path_value}"
8+
BASE_URL = "http://api.acme.com"
9+
ROOT_PATH = "/stac/v1"
1210

1311

1412
@pytest.fixture(scope="function")
@@ -18,7 +16,7 @@ async def app_with_root_path(database, monkeypatch):
1816
specific ROOT_PATH environment variable and connected to the test database.
1917
"""
2018

21-
monkeypatch.setenv("ROOT_PATH", root_path_value)
19+
monkeypatch.setenv("ROOT_PATH", ROOT_PATH)
2220
monkeypatch.setenv("PGUSER", database.user)
2321
monkeypatch.setenv("PGPASSWORD", database.password)
2422
monkeypatch.setenv("PGHOST", database.host)
@@ -35,108 +33,90 @@ async def app_with_root_path(database, monkeypatch):
3533

3634
# Ensure the app's root_path is configured as expected
3735
assert (
38-
app.root_path == root_path_value
39-
), f"app_with_root_path fixture: app.root_path is '{app.root_path}', expected '{root_path_value}'"
36+
app.root_path == ROOT_PATH
37+
), f"app_with_root_path fixture: app.root_path is '{app.root_path}', expected '{ROOT_PATH}'"
4038

4139
await connect_to_db(app, add_write_connection_pool=with_transactions)
4240
yield app
4341
await close_db_connection(app)
4442

4543

4644
@pytest.fixture(scope="function")
47-
async def client_with_root_path(app_with_root_path):
48-
async with AsyncClient(
49-
transport=ASGITransport(app=app_with_root_path),
50-
base_url=base_url,
45+
def client_with_root_path(app_with_root_path):
46+
with TestClient(
47+
app_with_root_path,
48+
base_url=BASE_URL,
49+
root_path=ROOT_PATH,
5150
) as c:
5251
yield c
5352

5453

5554
@pytest.fixture(scope="function")
56-
async def loaded_client(client_with_root_path, load_test_data):
55+
def loaded_client(client_with_root_path, load_test_data):
5756
col = load_test_data("test_collection.json")
58-
resp = await client_with_root_path.post(
57+
resp = client_with_root_path.post(
5958
"/collections",
6059
json=col,
6160
)
6261
assert resp.status_code == 201
6362
item = load_test_data("test_item.json")
64-
resp = await client_with_root_path.post(
63+
resp = client_with_root_path.post(
6564
f"/collections/{col['id']}/items",
6665
json=item,
6766
)
6867
assert resp.status_code == 201
6968
item = load_test_data("test_item2.json")
70-
resp = await client_with_root_path.post(
69+
resp = client_with_root_path.post(
7170
f"/collections/{col['id']}/items",
7271
json=item,
7372
)
7473
assert resp.status_code == 201
7574
yield client_with_root_path
7675

7776

78-
async def test_search_links_are_valid(loaded_client):
79-
resp = await loaded_client.get("/search?limit=1")
80-
assert resp.status_code == 200
81-
response_json = resp.json()
82-
assert_links_href(response_json.get("links", []), base_url)
83-
84-
85-
async def test_collection_links_are_valid(loaded_client):
86-
resp = await loaded_client.get("/collections?limit=1")
77+
@pytest.mark.parametrize(
78+
"path",
79+
[
80+
"/search?limit=1",
81+
"/collections?limit=1",
82+
"/collections/test-collection/items?limit=1",
83+
],
84+
)
85+
def test_search_links_are_valid(loaded_client, path):
86+
resp = loaded_client.get(path)
8787
assert resp.status_code == 200
8888
response_json = resp.json()
89-
assert_links_href(response_json.get("links", []), base_url)
90-
91-
92-
async def test_items_collection_links_are_valid(loaded_client):
93-
resp = await loaded_client.get("/collections/test-collection/items?limit=1")
94-
assert resp.status_code == 200
95-
response_json = resp.json()
96-
assert_links_href(response_json.get("links", []), base_url)
97-
98-
99-
def assert_links_href(links, url_prefix):
100-
"""
101-
Ensure all links start with the expected URL prefix and check that
102-
there is no root_path duplicated in the URL.
103-
104-
Args:
105-
links: List of link dictionaries with 'href' keys
106-
url_prefix: Expected URL prefix (e.g., 'http://test/stac/v1')
107-
"""
108-
from urllib.parse import urlparse
10989

90+
# Ensure all links start with the expected URL prefix and check that
91+
# there is no root_path duplicated in the URL.
11092
failed_links = []
111-
parsed_prefix = urlparse(url_prefix)
112-
root_path = parsed_prefix.path # e.g., '/stac/v1'
93+
expected_prefix = f"{BASE_URL}{ROOT_PATH}"
11394

114-
for link in links:
95+
for link in response_json.get("links", []):
11596
href = link["href"]
11697
rel = link.get("rel", "unknown")
11798

11899
# Check if link starts with the expected prefix
119-
if not href.startswith(url_prefix):
100+
if not href.startswith(expected_prefix):
120101
failed_links.append(
121102
{
122103
"rel": rel,
123104
"href": href,
124-
"error": f"does not start with expected prefix '{url_prefix}'",
105+
"error": f"does not start with expected prefix '{expected_prefix}'",
125106
}
126107
)
127108
continue
128109

129110
# Check for duplicated root path
130-
if root_path and root_path != "/":
131-
remainder = href[len(url_prefix) :]
132-
if remainder.startswith(root_path):
133-
failed_links.append(
134-
{
135-
"rel": rel,
136-
"href": href,
137-
"error": f"contains duplicated root path '{root_path}'",
138-
}
139-
)
111+
remainder = href[len(expected_prefix) :]
112+
if remainder.startswith(ROOT_PATH):
113+
failed_links.append(
114+
{
115+
"rel": rel,
116+
"href": href,
117+
"error": f"contains duplicated root path '{ROOT_PATH}'",
118+
}
119+
)
140120

141121
# If there are failed links, create a detailed error report
142122
if failed_links:

0 commit comments

Comments
 (0)