@@ -242,6 +242,68 @@ def test_warm_url_combined_with_profile_warms_both(
242242 assert "https://example.org/explicit" in result .output
243243
244244
245+ # ====================================================================
246+ # cache warm: --crate (remote RO-Crate)
247+ # ====================================================================
248+ @pytest .fixture
249+ def mock_network_gzip (monkeypatch ):
250+ """
251+ Same as ``mock_network``, but returns a ``Content-Encoding: gzip`` body.
252+ This encoded response is required to reproduce the warm-crate bug.
253+ """
254+ import gzip
255+
256+ from requests .adapters import HTTPAdapter
257+
258+ body = gzip .compress (b'{"@context": "https://w3id.org/ro/crate/1.2/context"}' )
259+
260+ def fake_send (self , request , ** kwargs ):
261+ raw = urllib3 .HTTPResponse (
262+ body = io .BytesIO (body ),
263+ headers = {
264+ "Content-Type" : "application/json" ,
265+ "Content-Encoding" : "gzip" ,
266+ "Content-Length" : str (len (body )),
267+ },
268+ status = 200 ,
269+ preload_content = False ,
270+ decode_content = False ,
271+ )
272+ return self .build_response (request , raw )
273+
274+ monkeypatch .setattr (HTTPAdapter , "send" , fake_send )
275+
276+
277+ def test_warm_crate_caches_remote_metadata (cli_runner , mock_network_gzip , tmp_cache ):
278+ """
279+ Regression: ``cache warm --crate <url>`` must consume the body via
280+ ``response.content`` rather than streaming ``response.raw``.
281+
282+ With ``stream=True`` + ``shutil.copyfileobj(response.raw, ...)`` the warm-up
283+ crashed with urllib3's "Calling read(decode_content=False) is not supported
284+ after read(decode_content=True) was called": requests_cache buffers the
285+ streamed body (decode_content=True) to store it, after which a raw read
286+ (decode_content=False) is rejected. The body must therefore be touched in a
287+ way that goes through the already-decoded content.
288+ """
289+ url = "https://example.org/ro-crate/ro-crate-metadata.json"
290+ result = cli_runner .invoke (
291+ cli ,
292+ ["cache" , "warm" , "--cache-path" , str (tmp_cache ), "--crate" , url ],
293+ )
294+ assert result .exit_code == 0 , result .output
295+ assert "Fetching remote RO-Crates" in result .output
296+ assert "Summary: 1 cached, 0 failed, 0 skipped" in result .output
297+
298+ # The fetched crate must actually be retrievable from the cache afterwards.
299+ listed = cli_runner .invoke (
300+ cli ,
301+ ["cache" , "list" , "--cache-path" , str (tmp_cache )],
302+ )
303+ assert listed .exit_code == 0 , listed .output
304+ assert "ro-crate-metadata.json" in listed .output
305+
306+
245307# ====================================================================
246308# cache list / ls
247309# ====================================================================
0 commit comments