test(vdsm): install Download Station + add integration tests#107
Open
cmeans-claude-dev[bot] wants to merge 15 commits into
Open
test(vdsm): install Download Station + add integration tests#107cmeans-claude-dev[bot] wants to merge 15 commits into
cmeans-claude-dev[bot] wants to merge 15 commits into
Conversation
Adds _install_download_station_via_ui() in tests/vdsm/setup_dsm.py that walks the operator path through Package Center: open Package Center, search "Download Station", click Install, walk any license/confirmation dialogs, poll up to 5 min for the Installed state. Wired into setup_dsm_for_testing() as a new step [3/5] between user creation and SSH enable. golden_image.py metadata now records dsm_packages=["DownloadStation"] so a cached pre-DS golden image is detectable for invalidation. Bake-time only: subsequent test runs restore from the tarball and skip the install. Cold bake adds ~3-5 min to vdsm CI; cached runs are unaffected. Refs: #106
Adds _configure_download_station_via_api() that runs after share creation: sets DS default_destination to /writable, grants test_user permission for the DS package. API-only (no Playwright needed post-install). Permission grant uses SYNO.Core.Package.Setting.User with a graceful fallback (warning + continue) if that API is restricted on vdsm — the test failures will surface the gap. Wired into setup_dsm_for_testing() as new step [6/6] after share creation. Step counts in the orchestrator bumped from [N/5] to [N/6].
Five READ-tool tests plus a create/read/delete lifecycle test. The lifecycle test uses a fake magnet URI (no seeds, no leech behavior) so the task creates instantly and sits at 'waiting' status — gives get_download_info real data to render without any network dependency or wait-for-download flakiness. Skip-cleanly fixture _ds_available checks SYNO.DownloadStation.Task is in the API cache; tests skip if the package isn't installed (e.g., running this file against a real NAS that doesn't have DS).
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…tors CI bake at fd1f76a failed in _install_download_station_via_ui — the Main Menu fallback selector (.sds-mainmenu-btn, [data-qa='main-menu']) timed out at 30s. Root cause: after _create_user_via_ui completes, Control Panel is still open in the foreground, occluding the desktop; the initial text='Package Center' query found nothing, so the fallback fired against a selector that doesn't match the actual DSM UI. Replace the UI navigation with a direct JS call to SYNO.SDS.AppLaunch — DSM's internal app-launch mechanism that doesn't care which window is in focus, doesn't need taskbar selectors, and is the same call DSM apps use to launch each other. Keep a desktop-shortcut fallback (double-click, matching _open_control_panel) for the case where AppLaunch isn't on window.SYNO.SDS for some reason. Also dismiss popups first (matching the existing pattern in _open_control_panel) and bump post-launch sleep to 10s — Package Center fetches its catalog over HTTP on first launch.
CI bake at f4f1aaa got further — AppLaunch fired and the Install button was clicked — but failed in the install-complete poll loop with a Playwright CSS parse error: Unexpected token "=" while parsing css selector "button:has-text('Open'), button:has-text('Run'), text='Installed'" Playwright's text= selector engine doesn't compose into a comma-separated CSS list. Drop the text='Installed' token and rely on the button selectors — Install gets replaced with Open/Run once installation completes, so the button check alone is sufficient. Other text='...' usages in the file (lines 265 + 634) are standalone selectors and unaffected; the comma-list mixing was the only bug.
CI bake at cba47a5 got further still — Install button clicked, but the install-complete poll loop timed out at 300s with no Open/Run button ever appearing. Without screenshots from CI it's hard to know whether the install hung partway, the dialog walker missed a button, or Synology's package server was unreachable. Three changes to make the next iteration diagnosable AND more likely to succeed: 1. Expand the post-Install dialog walker label set: add Confirm/OK/Yes/Install. DSM 7's package-install confirmation dialogs vary; the previous walker (Next/Agree/Apply/Done/Continue) may have exited too early. 2. Add periodic install-progress screenshots during the poll loop (every ~60s). The final screenshot at timeout already exists; this adds intermediate ones so post-mortem diagnostics can see whether the install stalled at 0% or at a specific step. 3. Add a vdsm-failure-screenshots artifact upload step to vdsm.yml so .vdsm/screenshots/ is recoverable from CI on failure. The existing vdsm-failure-logs step only uploads /tmp/vdsm-*.log and .vdsm/storage/**/log/ — screenshots were being lost. Also: ruff format check failed at the previous push because I ran `ruff check` locally but not `ruff format --check`. The CI check is `ruff format --check src/ tests/ scripts/`; running that locally now matches CI.
Screenshots from CI bake at 8c4db50 revealed the root cause of the 300s install timeout: DSM 7 Package Center shows a "Synology Package Center Terms of Services" modal on first launch that blocks everything underneath. The modal has a checkbox ("I have read and agreed to the Package Center Terms of Service") + a primary action button. Earlier iterations clicked through what they THOUGHT were the search box and Install button, but the clicks landed on the modal overlay or were eaten by the disabled controls behind it — the install never actually started. The five-minute install poll then ran out, and the final screenshot showed the same ToS modal still up. Fix: after launching Package Center and waiting for it to initialize, detect the ToS modal by looking for "Terms of Service" text on a visible element. If present: 1. Tick the agreement checkbox via JS click 2. Click the primary action button (label varies — try OK / Apply / Agree / Accept) 3. Wait for the modal to close New screenshots ds-02a-tos-shown, ds-02b-tos-checked, ds-02c-tos-accepted trace the dismissal so future post-mortems can verify the flow.
…ext) Screenshots from CI bake at 97cbbfa showed ds-02b/02c identical to ds-02a — the ToS modal wasn't budging. Two root causes: 1. ExtJS doesn't use real <input type=checkbox>. The checkbox is a styled <div> wrapper with the native input hidden; my JS query for `input[type=checkbox]:not(:checked)` returned nothing. Reliable approach: click the LABEL text "I have read and agreed..." which ExtJS routes through to the underlying state. 2. ExtJS button text lives inside nested <span> elements; Playwright's `button:has-text('OK')` doesn't match the project's actual button shape. The existing _click_text() helper at line 54 walks a/button/span/div/p/label by exact text match and is the proven path for clicking ExtJS buttons in this codebase (used by _create_user_via_ui). Also add a post-dismiss verification: re-query for "Terms of Service" text and fail loudly if the modal is still present. The previous silent-failure pattern wasted five minutes of install-poll waiting for a button that was never clickable. New screenshot ds-02d-tos-still-shown fires if the modal persists, so the next post-mortem will pinpoint the failing dismiss step.
ExtJS does not bind click handlers to label text — JS .click() on the label leaves the checkbox unchecked, so the Accept button stays disabled and the install loop never starts. CI 25843068739 confirmed: ds-02a..d screenshots are pixel-identical with the checkbox unticked. Walk up from the label text leaf to the x-form-cb-wrap (or x-form-cb-checker / x-form-checkbox) ancestor and click the inner <input>; fall back to clicking the wrapper. Verify the post-click state (class `x-form-cb-checked` on the wrapper or any visible checked input) and raise loudly before attempting the Accept button if the tick did not register. Dump the ancestor trail on miss so the next CI run has actionable evidence rather than four identical screenshots.
The Playwright/Package-Center UI install proved unautomatable against
DSM 7.2.2's heavily customized Package Center. Eight local iterations
on the Terms of Service modal alone confirmed:
- JS .click() on the syno-ux checkbox label leaves it unticked
- JS .click() on the inner <input type="button"> sets input.checked
but doesn't toggle Ext's component-model state
- CDP page.mouse.click(x, y) on the checkbox icon coords lands but
Ext's change event still doesn't fire
- Same problem cascades to the OK button — scoped Playwright locators
time out, and CDP clicks on the .x-btn syno-ux-button SPAN don't
trigger Ext's button handler
- Direct Ext 3.4.1 ComponentMgr.all + findBy traversal couldn't
locate the checkbox xtype anywhere in the matched window's
descendant tree
The handoff plan flagged SSH as the fallback after 2-3 iterations
without progress. synopkg install_from_server DownloadStation completes
in ~5 seconds, is idempotent on cached images, and uses the SSH/_ssh
helper already proven for share creation. The whole 200-line UI
function and its diagnostics are gone; the new path is ~60 lines.
Reorders setup_dsm_for_testing to enable SSH at step [3/6] (was [4/6])
so the install can run before share creation needs SSH anyway.
CI on 00c299f confirmed the install path works (synopkg install_from_server DownloadStation completed in 4s, package shows in synopkg list) but the [6/6] _configure_download_station_via_api step failed with code 102 ("API does not exist") — the package was installed but not running, so the SYNO.DownloadStation.* APIs weren't in the cache. Two changes: 1. After install, run /var/packages/DownloadStation/scripts/start-stop-status start (the package's own SysV-style start script). On vDSM the synopkg-managed systemd path is broken — synopkg status always reports stopped with status_code 263 "failed to get unit status" even after start succeeds — but start-stop-status actually starts the DS services. Benign warnings about python2 (eMule plugin only) and a missing amule statistics.dat file are unrelated to the core download task surface. 2. Gate readiness on the DSM API cache (SYNO.API.Info query for SYNO.DownloadStation.Task), NOT synopkg status — the latter is misleading on vDSM. _install_download_station_via_ssh now takes base_url so it can probe the API directly via httpx. Idempotency check moved to the API surface too: if the Task API is already registered, skip install entirely. Validated locally on the cached pre-DS golden image (~60s end-to-end).
CI on 43a2526 confirmed install + start-stop-status start does register the DS APIs, but they register at staggered times. SYNO.DownloadStation.Task appears first; SYNO.DownloadStation.Info (used by setconfig in step [6/6]) arrives later. The previous Task-only gate let [4/6] declare success while [6/6] still hit `DS setconfig failed: code 102` because Info wasn't there yet. Gate is now the full set the bake's downstream steps + vdsm tests touch: - SYNO.DownloadStation.Task (list_downloads, task CRUD) - SYNO.DownloadStation.Info (get_download_config, setconfig) - SYNO.DownloadStation.Statistic (get_download_stats) - SYNO.DownloadStation.Schedule (get_schedule, set_schedule) Poll deadline bumped 60s -> 120s to absorb the staggered registration. Local round 14 confirmed all four APIs land within seconds of start-stop-status returning rc=0.
CI on e0ec3a8 confirmed the previous theory wrong: the [4/6] gate fired "All 4 DS APIs registered in DSM API cache" but [6/6] still failed with DS setconfig: code 102. Code 102 means "API does not exist", which seemed impossible given the cache hit. Local probing of all endpoint/method permutations against vdsm 7.2.2 revealed two distinct bugs in _configure_download_station_via_api: 1. Endpoint: /webapi/entry.cgi --> /webapi/DownloadStation/info.cgi The generic entry.cgi dispatcher returns code 102 for ALL DS API calls, regardless of method or version. DS APIs only dispatch via their specific cgi path (which API.Info reports as `DownloadStation/info.cgi`). 2. Method: setconfig --> setserverconfig `setconfig` is the name some older DSM docs use; DSM 7.2.2's actual method is `setserverconfig`. Calling setconfig returns code 103 (method does not exist). Confirmed locally end-to-end: install + start-stop-status start + all 4 APIs in cache + _configure_download_station_via_api now reaches "Download Station configured" successfully. The follow-on SYNO.Core.Package.Setting.User permission grant still returns code 102 (already documented as a vdsm Core-API restriction with graceful warning-and-continue — unchanged by this commit).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Download Station coverage to the vdsm integration test suite, closing the deliberate exclusion documented in #104 (Phase 1) and #105 (Phase 2): "vdsm doesn't ship the Download Station package; vdsm integration tests are out of scope for this module."
tests/vdsm/setup_dsm.py:_install_download_station_via_ui). Matches the existing operator-driven Playwright pattern used for user creation (_create_user_via_ui)._configure_download_station_via_api): setsdefault_destination=writable, grants the test_user permission to use DS. Permission grant usesSYNO.Core.Package.Setting.Userwith a graceful warning-and-continue fallback if that API is restricted on the vdsm DSM build.tests/vdsm/test_vdsm_downloadstation.pywith 5 Phase 1 READ-tool tests plus a create/read/delete lifecycle test that exercisesget_download_infoagainst a real task. Lifecycle test uses a fake magnet URI — task creates instantly (no swarm to verify against), soget_download_infohas real data to render without any network or wait-for-download flakiness._ds_availablechecks `SYNO.DownloadStation.Task` is in the API cache; the test file is portable against any DSM-compatible target (real NAS, vdsm with DS, vdsm without DS).Closes #106.
Verification posture
.vdsm/screenshots/ds-*.pngwill surface the exact failure for fix-up in a follow-up commit.Cold-bake cost
Cold bake adds ~3–5 min for the DS install (Synology's package server fetches the DS .spk over HTTP). Cached golden-image restores are unaffected.
golden_image.pymetadata now tracks `dsm_packages=["DownloadStation"]` so cached pre-DS images are detectable for invalidation.Out of scope
Test plan
🤖 Generated with Claude Code