aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_zip_size_limit.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests/test_zip_size_limit.py')
-rw-r--r--packages/meshbay-hub/tests/test_zip_size_limit.py59
1 files changed, 53 insertions, 6 deletions
diff --git a/packages/meshbay-hub/tests/test_zip_size_limit.py b/packages/meshbay-hub/tests/test_zip_size_limit.py
index 203c10c..44ad59e 100644
--- a/packages/meshbay-hub/tests/test_zip_size_limit.py
+++ b/packages/meshbay-hub/tests/test_zip_size_limit.py
@@ -6,11 +6,16 @@ folder as a zip" button — Files' single folder, Files' multi-folder selection,
and the Photos album button (docs/photos.md §3) — so the limit is checked
once, there, and holds for all of them.
-Two things are worth pinning. That an oversized folder is refused *before*
+Three things are worth pinning. That an oversized folder is refused *before*
`_openDownloadTarget`, because a save dialog for an archive that will never be
-written is worse than no dialog at all. And that a folder at exactly the limit
+written is worse than no dialog at all. That a folder at exactly the limit
still goes through, since an off-by-one here silently costs a whole megabyte
-of allowance and nobody would ever notice.
+of allowance and nobody would ever notice. And that the two limits in play do
+not contradict each other: ZIP_MAX_BYTES (512 MB) bounds the archive, while
+MEMORY_CEILING (100 MB, test_memory_ceiling.py) bounds what may be built in the
+page — so a 400 MB zip is allowed when there is somewhere to stream it and
+refused when the only route left is memory. The `confirm()` that offers the
+build-in-memory path therefore only ever appears below the ceiling.
"""
import json
@@ -30,7 +35,7 @@ pytestmark = pytest.mark.skipif(
MIB = 1024 * 1024
-def _run(total_bytes, tmp_path):
+def _run(total_bytes, tmp_path, picker=False):
"""
Call downloadDirectory over one folder holding `total_bytes`, and report
what it did: the errors it set, how many times it put a question to the
@@ -44,6 +49,7 @@ def _run(total_bytes, tmp_path):
(tmp_path / "package.json").write_text('{"type":"module"}')
script = tmp_path / "case.mjs"
+ picker_js = "true" if picker else "false"
script.write_text(f"""
const store = new Map();
globalThis.localStorage = {{
@@ -60,6 +66,17 @@ const out = {{ errors: [], started: 0, asked: 0 }};
// asks first. Answering yes is what lets the at-the-limit case get as far as
// starting a transfer, and `asked` is how the refusal proves it never did.
globalThis.confirm = () => {{ out.asked += 1; return true; }};
+// With `picker`, the browser can stream to a file the person chooses, which is
+// the only legal route for an archive over MEMORY_CEILING. Never exercised —
+// the stubbed `transfers.start` below does not run the job — it just has to be
+// a target rather than null.
+if ({picker_js}) {{
+ window.showSaveFilePicker = async () => ({{
+ name: 'album.zip',
+ createWritable: async () => ({{ write: async () => {{}}, close: async () => {{}},
+ abort: async () => {{}} }}),
+ }});
+}}
const M = await import('{(sandbox / "file-utils.js").as_posix()}');
@@ -101,7 +118,37 @@ def test_an_oversized_folder_is_refused_before_anything_opens(tmp_path):
def test_a_folder_exactly_at_the_limit_still_downloads(tmp_path):
- """The bound is inclusive: `> ZIP_MAX_BYTES`, not `>=`."""
- result = _run(512 * MIB, tmp_path)
+ """The bound is inclusive: `> ZIP_MAX_BYTES`, not `>=`.
+
+ Given somewhere to stream to, because 512 MB is five times MEMORY_CEILING
+ and building it in the page is no longer a route this code will take. That
+ is what the next test is about; this one is still only about the off-by-one.
+ """
+ result = _run(512 * MIB, tmp_path, picker=True)
assert result["errors"] == []
assert result["started"] == 1
+ assert result["asked"] == 0, "nothing is built in memory when it can stream"
+
+
+def test_a_zip_over_the_memory_ceiling_is_refused_when_nothing_streams(tmp_path):
+ """
+ Between the two limits — larger than the page may hold, smaller than the
+ archive limit — and no way to stream it. Before the ceiling existed this
+ asked "build it in memory?" and, on yes, held 400 MB in the tab.
+
+ The refusal names the memory ceiling, not the zip limit: quoting 512 MB at
+ someone whose folder is under 512 MB would be a message about the wrong
+ rule.
+ """
+ result = _run(400 * MIB, tmp_path)
+ assert result["started"] == 0
+ assert result["asked"] == 0, (
+ "the person must not be offered a build-in-memory path above the ceiling")
+ assert result["errors"] and "group.zip_too_large" not in result["errors"][0]
+
+
+def test_a_small_folder_may_still_be_built_in_memory(tmp_path):
+ """The floor is intact below the ceiling — that is what it is for."""
+ result = _run(4 * MIB, tmp_path)
+ assert result["errors"] == []
+ assert result["asked"] == 1 and result["started"] == 1