From 2378414b88909dce468879f07465af900dacaf43 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Fri, 4 Sep 2026 12:57:34 -0700 Subject: [PATCH] [eric] swarm: a Tisusa-shaped bundle (507 files, eight deep, dotfiles, spaces, a 1.2 MB file) round-trips through the real exporter and importer, drilled on windows-latest Co-Authored-By: Claude Fable 5.1 --- .github/workflows/swarm-import-drill.yml | 41 +++++++++++++ backend/tests/test_swarm_tisusa_shape.py | 73 ++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 .github/workflows/swarm-import-drill.yml create mode 100644 backend/tests/test_swarm_tisusa_shape.py diff --git a/.github/workflows/swarm-import-drill.yml b/.github/workflows/swarm-import-drill.yml new file mode 100644 index 00000000..e4cbd0e4 --- /dev/null +++ b/.github/workflows/swarm-import-drill.yml @@ -0,0 +1,41 @@ +name: swarm-import-drill + +# A Tisusa-shaped .swarm app (507 files, eight deep, dotfiles, spaces, an 80-char path, a 1.2 MB file) +# exported, staged and imported by the real code on Windows and macOS. Windows is the arm that matters: +# the staging reader keyed files with backslashes there and every app import saved a hollow app (ENG-463). +on: + push: + paths: + - 'backend/apps/swarm/**' + - 'backend/apps/outputs/workspace_io.py' + - 'backend/tests/test_swarm_tisusa_shape.py' + - 'backend/tests/test_swarm_windows_paths.py' + - '.github/workflows/swarm-import-drill.yml' + workflow_dispatch: + +jobs: + drill: + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + cache: pip + cache-dependency-path: backend/requirements.lock + + - name: Install the backend's locked dependencies plus the test runner + run: | + pip install -r backend/requirements.lock + pip install -r backend/requirements-dev.txt + + - name: Export, stage and import the Tisusa-shaped bundle with the real code + env: + OSW_NEVER_KILL_ROUTER: '1' + run: python -m pytest backend/tests/test_swarm_tisusa_shape.py backend/tests/test_swarm_windows_paths.py -q -p no:cacheprovider diff --git a/backend/tests/test_swarm_tisusa_shape.py b/backend/tests/test_swarm_tisusa_shape.py new file mode 100644 index 00000000..4b495901 --- /dev/null +++ b/backend/tests/test_swarm_tisusa_shape.py @@ -0,0 +1,73 @@ +"""The Tisusa client's bundle, by shape (2026-09-04, ENG-463): 507 workspace files nested up to eight +deep, seven dotfiles, two names with spaces, an 80-character path, one 1.2 MB file. The real bundle +is the client's code, so this rebuilds its SHAPE with placeholder contents and drives it through the +real exporter, the real staging reader and the real app importer, on whatever OS runs the test. On +Windows this is the path that used to save a hollow app; `.github/workflows/swarm-import-drill.yml` +runs it on windows-latest.""" + +import os +import zipfile + +from backend.apps.outputs.models import Output +from backend.apps.outputs.workspace_io import load_output, save +from backend.apps.swarm import closure +from backend.apps.swarm.entities import apps as appmod +from backend.apps.swarm.models import EntityType + +# depth -> how many files sit at that depth, read off the client's bundle +TISUSA_DEPTHS = {0: 28, 1: 29, 2: 27, 3: 130, 4: 107, 5: 60, 6: 72, 7: 49, 8: 5} +DOTFILES = [".env.example", ".gitattributes", ".gitignore", ".prettierrc", ".eslintrc.json", ".npmrc", ".nvmrc"] +SPACED = ["docs/Design Notes.md", "public/brand assets/logo.svg"] +LONG_PATH = "app/features/settings/integrations/providers/components/ProviderCredential.tsx" +BIG_FILE = "public/hero.png" + + +def build_tisusa_shaped_tree(root: str) -> list[str]: + rels: list[str] = list(DOTFILES) + SPACED + [LONG_PATH, BIG_FILE] + fixed_by_depth = {} + for r in rels: + fixed_by_depth[r.count("/")] = fixed_by_depth.get(r.count("/"), 0) + 1 + for depth, count in TISUSA_DEPTHS.items(): + for i in range(count - fixed_by_depth.get(depth, 0)): + parts = [f"d{depth}_{j}" for j in range(depth)] + [f"f{i}.ts"] + rels.append("/".join(parts)) + assert len(rels) == sum(TISUSA_DEPTHS.values()) == 507 + assert len(LONG_PATH) == 80 + for rel in rels: + full = os.path.join(root, *rel.split("/")) + os.makedirs(os.path.dirname(full), exist_ok=True) + with open(full, "wb") as f: + f.write(b"\x89PNG" + b"\0" * (1_262_398 - 4) if rel == BIG_FILE else f"// {rel}\n".encode()) + return rels + + +def test_a_tisusa_shaped_bundle_round_trips_with_every_file_on_this_os(): + ws = "tisusa-shaped-src" + src = os.path.join(appmod.OUTPUTS_WORKSPACE_DIR, ws) + rels = build_tisusa_shaped_tree(src) + o = Output(name="Tisusa shaped", workspace_id=ws) + save(o) + + raw, name = closure.build_bundle(EntityType.app, o.id) + with zipfile.ZipFile(__import__("io").BytesIO(raw)) as zf: + members = [n for n in zf.namelist() if "/files/workspace/" in n] + assert len(members) == 507 + assert not any("\\" in m for m in members), "a bundle member name carried a backslash" + + sandbox, manifest, warnings = closure.stage_upload(raw, f"{name}.swarm") + # The staging reader is the line that broke on Windows: its keys must be slash-separated on THIS OS. + ref = next(e for e in manifest.entities if e.type == EntityType.app) + keys = list(closure.p_read_files(sandbox, ref)) + assert len(keys) == 507 and all(k.startswith("workspace/") for k in keys), f"bad keys: {[k for k in keys if not k.startswith('workspace/')][:3]}" + + root_type, root_id, created, unresolved = closure.commit(sandbox, manifest, []) + assert root_type == EntityType.app + imported = load_output(root_id) + assert imported is not None and imported.workspace_id, "the app imported without a workspace (the hollow-app shape)" + dest = os.path.join(appmod.OUTPUTS_WORKSPACE_DIR, imported.workspace_id) + on_disk = sorted(os.path.relpath(os.path.join(r, f), dest).replace(os.sep, "/") for r, _, fs in os.walk(dest) for f in fs) + assert len(on_disk) == 507, f"{len(on_disk)} files landed, expected 507" + for rel in DOTFILES + SPACED + [LONG_PATH, BIG_FILE]: + assert rel in on_disk, f"{rel} did not land" + assert os.path.getsize(os.path.join(dest, *BIG_FILE.split("/"))) == 1_262_398 + assert sorted(rels) == on_disk