Skip to content

Commit 97b691b

Browse files
committed
test(e2e): use [test] extras + poll SC-35 upload
- Workflow back to `pip install -e .[test]` — the upstream pyproject.mustache template now carries `[project.optional-dependencies]` for both `test` and `dev` extras, so explicit dep install is no longer needed. - SC-35 (ISO upload): switch from synchronous read-after-write to `wait_until(...)` polling. The upload endpoint returns a UPID for the imgcopy task; on a busy / cold runner the file isn't yet visible at the immediate next listing call, causing intermittent failures.
1 parent d7742d8 commit 97b691b

2 files changed

Lines changed: 18 additions & 15 deletions

File tree

.github/workflows/e2e.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,7 @@ jobs:
1919
cache: pip
2020

2121
- name: Install package + test deps
22-
# Install test deps explicitly rather than via `[test]` extras: the
23-
# generator overwrites pyproject.toml on each regen and currently drops
24-
# `[project.optional-dependencies]`. Until the upstream Mustache
25-
# template carries the block, the workflow owns the deps list.
26-
run: |
27-
pip install -e .
28-
pip install 'pytest>=8' 'pytest-timeout>=2.3' 'requests>=2.32'
22+
run: pip install -e .[test]
2923

3024
- name: Authenticate to GHCR
3125
uses: docker/login-action@v3

e2e/test_iso_upload.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
)
1515
from e2e.conftest import requires_network
1616
from e2e.helpers.iso import download_boot_iso
17+
from e2e.helpers.poll import wait_until
1718
from e2e.helpers.upload import list_storage_content, upload_iso
1819

1920

@@ -49,13 +50,21 @@ def test_iso_download_upload_list_delete(pve: Pve, node: str, iso_storage: str)
4950
filename = "e2e-boot.iso"
5051
upload_iso(pve, node, iso_storage, filename, data)
5152

52-
listing = list_storage_content(pve, node, iso_storage)
53-
volids = [item.volid for item in listing]
54-
matching = [v for v in volids if v.endswith(f"/{filename}")]
55-
assert matching, f"uploaded ISO not in listing: {volids!r}"
53+
# Upload returns a UPID; the file appears in the storage asynchronously
54+
# once the imgcopy task finishes. Poll until visible (or timeout).
55+
def _find_uploaded() -> str | None:
56+
listing = list_storage_content(pve, node, iso_storage)
57+
for item in listing:
58+
volid = item.volid
59+
if volid.endswith(f"/{filename}"):
60+
return volid
61+
return None
5662

57-
pve.nodesStorage.delete_content(node=node, storage=iso_storage, volume=matching[0])
63+
volid = wait_until(_find_uploaded, timeout_s=30, interval_s=1, label=f"{filename} in listing")
64+
pve.nodesStorage.delete_content(node=node, storage=iso_storage, volume=volid)
5865

59-
listing_after = list_storage_content(pve, node, iso_storage)
60-
volids_after = [item.volid for item in listing_after]
61-
assert not [v for v in volids_after if v.endswith(f"/{filename}")], volids_after
66+
def _gone() -> bool:
67+
listing_after = list_storage_content(pve, node, iso_storage)
68+
return not any(item.volid.endswith(f"/{filename}") for item in listing_after)
69+
70+
wait_until(_gone, timeout_s=30, interval_s=1, label=f"{filename} gone from listing")

0 commit comments

Comments
 (0)