Skip to content

test(vdsm): install Download Station + add integration tests#107

Open
cmeans-claude-dev[bot] wants to merge 15 commits into
mainfrom
feat/vdsm-downloadstation
Open

test(vdsm): install Download Station + add integration tests#107
cmeans-claude-dev[bot] wants to merge 15 commits into
mainfrom
feat/vdsm-downloadstation

Conversation

@cmeans-claude-dev
Copy link
Copy Markdown
Contributor

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."

  • Install DS into the vdsm golden image at bake time via Playwright through Package Center UI (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 DS post-install via DSM API (_configure_download_station_via_api): sets default_destination=writable, grants the test_user permission to use DS. Permission grant uses SYNO.Core.Package.Setting.User with a graceful warning-and-continue fallback if that API is restricted on the vdsm DSM build.
  • New tests/vdsm/test_vdsm_downloadstation.py with 5 Phase 1 READ-tool tests plus a create/read/delete lifecycle test that exercises get_download_info against a real task. Lifecycle test uses a fake magnet URI — task creates instantly (no swarm to verify against), so get_download_info has real data to render without any network or wait-for-download flakiness.
  • Skip-cleanly fixture _ds_available checks `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

  • Local: lint + ruff + mypy pass; 727 unit tests pass; 117 vdsm/integration tests deselected (will run in CI).
  • Real Playwright + DS-install verification happens in CI when the bake rebuilds the golden image. Neither the Playwright Package Center flow nor the vdsm tests can be exercised in the agent's local environment (no KVM).
  • One likely gotcha to watch in CI: the post-install detection selector mixes button + text selectors. If Package Center renders the installed state differently than expected, screenshots in .vdsm/screenshots/ds-*.png will 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.py metadata now tracks `dsm_packages=["DownloadStation"]` so cached pre-DS images are detectable for invalidation.

Out of scope

  • Full Phase 2 WRITE-tool vdsm coverage beyond the lifecycle test
  • Phase 3 (BT search + RSS) vdsm coverage — would land with Phase 3 itself

Test plan

  • CI vdsm job rebuilds the golden image with the new install step
  • All 5 READ tests pass against the freshly-installed DS
  • Lifecycle test creates / reads / deletes a magnet task cleanly
  • No regression in FileStation vdsm tests (cached restore path unchanged)
  • If the install fails: `.vdsm/screenshots/ds-*.png` artifacts surface the failure

🤖 Generated with Claude Code

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).
@cmeans-claude-dev cmeans-claude-dev Bot added the Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA label May 14, 2026
@github-actions github-actions Bot added Ready for QA Dev work complete — QA can begin review and removed Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
@codecov-commenter
Copy link
Copy Markdown

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.
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA Ready for QA Dev work complete — QA can begin review and removed Ready for QA Dev work complete — QA can begin review Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
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.
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA CI Failed CI failed — dev needs to fix and removed Ready for QA Dev work complete — QA can begin review Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
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.
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA Ready for QA Dev work complete — QA can begin review and removed CI Failed CI failed — dev needs to fix Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
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.
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA Ready for QA Dev work complete — QA can begin review and removed Ready for QA Dev work complete — QA can begin review Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
…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.
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA Ready for QA Dev work complete — QA can begin review and removed Ready for QA Dev work complete — QA can begin review Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
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.
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA CI Failed CI failed — dev needs to fix and removed Ready for QA Dev work complete — QA can begin review Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
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.
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA Ready for QA Dev work complete — QA can begin review and removed CI Failed CI failed — dev needs to fix Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
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).
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA Ready for QA Dev work complete — QA can begin review and removed Ready for QA Dev work complete — QA can begin review Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
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.
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA Ready for QA Dev work complete — QA can begin review and removed Ready for QA Dev work complete — QA can begin review Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
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).
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA Ready for QA Dev work complete — QA can begin review and removed Ready for QA Dev work complete — QA can begin review Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for QA Dev work complete — QA can begin review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vdsm: add Download Station integration test coverage

1 participant