22
33from __future__ import annotations
44
5+ import re
6+
57import httpx
68import pytest
79from starlette .applications import Starlette
@@ -59,10 +61,16 @@ def test_build_server_card_requires_description() -> None:
5961 build_server_card (server , name = "example/no-desc" )
6062
6163
62- async def _get (app : Starlette , path : str ) -> httpx .Response :
64+ async def _get (app : Starlette , path : str , headers : dict [str , str ] | None = None ) -> httpx .Response :
65+ transport = httpx .ASGITransport (app = app )
66+ async with httpx .AsyncClient (transport = transport , base_url = "https://dice.example.com" ) as client :
67+ return await client .get (path , headers = headers )
68+
69+
70+ async def _head (app : Starlette , path : str ) -> httpx .Response :
6371 transport = httpx .ASGITransport (app = app )
6472 async with httpx .AsyncClient (transport = transport , base_url = "https://dice.example.com" ) as client :
65- return await client .get (path )
73+ return await client .head (path )
6674
6775
6876async def test_server_card_route_serves_card_with_discovery_headers () -> None :
@@ -76,9 +84,33 @@ async def test_server_card_route_serves_card_with_discovery_headers() -> None:
7684 assert response .headers ["access-control-allow-methods" ] == "GET"
7785 assert response .headers ["access-control-allow-headers" ] == "Content-Type"
7886 assert response .headers ["cache-control" ] == "public, max-age=3600"
87+ etag = response .headers ["etag" ]
88+ assert re .fullmatch (r'"[0-9a-f]{64}"' , etag )
89+ assert (await _get (app , CARD_PATH )).headers ["etag" ] == etag
90+ assert (await _head (app , CARD_PATH )).headers ["etag" ] == etag
7991 assert response .text == card .model_dump_json (by_alias = True , exclude_none = True )
8092 assert ServerCard .model_validate (response .json ()) == card
8193
94+ not_modified = await _get (app , CARD_PATH , headers = {"If-None-Match" : etag })
95+ assert not_modified .status_code == 304
96+ assert not_modified .headers ["etag" ] == etag
97+ assert not_modified .headers ["cache-control" ] == "public, max-age=3600"
98+ assert not_modified .content == b""
99+
100+ weak_match = await _get (app , CARD_PATH , headers = {"If-None-Match" : f'"not-it", W/{ etag } ' })
101+ assert weak_match .status_code == 304
102+ assert weak_match .content == b""
103+
104+ wildcard = await _get (app , CARD_PATH , headers = {"If-None-Match" : "*" })
105+ assert wildcard .status_code == 304
106+ assert wildcard .headers ["etag" ] == etag
107+ assert wildcard .content == b""
108+
109+ non_matching = await _get (app , CARD_PATH , headers = {"If-None-Match" : '"not-it"' })
110+ assert non_matching .status_code == 200
111+ assert non_matching .headers ["etag" ] == etag
112+ assert non_matching .text == card .model_dump_json (by_alias = True , exclude_none = True )
113+
82114
83115async def test_mount_server_card_on_existing_app_and_client_fetch () -> None :
84116 card = build_server_card (
0 commit comments