@xenon-device-management/xenon 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/lib/package.json +1 -2
- package/lib/public/assets/{AnnotationOverlay-BmTPCAds.js → AnnotationOverlay-C-Pw_hvw.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-B5eB7f8B.js → ApiKeyGate-DmYQ0oSk.js} +1 -1
- package/lib/public/assets/BugReportButton-n8T1dFUp.js +10 -0
- package/lib/public/assets/{DeviceMosaicView-9DUoZnQi.js → DeviceMosaicView-D-rGOBbJ.js} +1 -1
- package/lib/public/assets/{EmptyState-lvyNiXev.js → EmptyState-DExETevp.js} +1 -1
- package/lib/public/assets/{FieldGroup-BduMPM9K.js → FieldGroup-CCdFlZm6.js} +1 -1
- package/lib/public/assets/{FilterMenu-BSqX6I94.js → FilterMenu-DiUSOSL-.js} +1 -1
- package/lib/public/assets/{Menu-Ba5cT7jZ.js → Menu-ZOfpH6gh.js} +1 -1
- package/lib/public/assets/{Modal-CceoMY7D.js → Modal-CZNtoB-1.js} +1 -1
- package/lib/public/assets/{RecordingPage-6T9dOidc.js → RecordingPage-BKp6h8Jl.js} +1 -1
- package/lib/public/assets/{RecordingsPage-qj6FdZXR.js → RecordingsPage-Bf881FX4.js} +1 -1
- package/lib/public/assets/{SegmentedControl-CSRuZ8TR.js → SegmentedControl-EUoyizvW.js} +1 -1
- package/lib/public/assets/{SettingCard-Bp1kz4By.js → SettingCard-ClT4hf4_.js} +1 -1
- package/lib/public/assets/{Table-LV7zFyZg.js → Table-ucR4bnmc.js} +1 -1
- package/lib/public/assets/{activity-D17TUloi.js → activity-K-hdylwq.js} +1 -1
- package/lib/public/assets/{ai-settings-qvbhY89P.js → ai-settings-BLhdwI3c.js} +1 -1
- package/lib/public/assets/{api-keys-BfpvdG7G.js → api-keys-BbiN7rBI.js} +1 -1
- package/lib/public/assets/{apps-Dbnzd0fW.js → apps-Dez1petz.js} +1 -1
- package/lib/public/assets/{arrow-left-DD1VjtC7.js → arrow-left-UjmFHLAJ.js} +1 -1
- package/lib/public/assets/{arrow-right-D0pnM3ma.js → arrow-right-BVyEigbu.js} +1 -1
- package/lib/public/assets/{arrow-up-right-BIC8JOV6.js → arrow-up-right-B-ZlBof3.js} +1 -1
- package/lib/public/assets/{auth-shell-DWHPXSnI.js → auth-shell-DAQjAgUv.js} +2 -2
- package/lib/public/assets/{builds-page-ClCNBUv_.js → builds-page-CmIPa5JK.js} +1 -1
- package/lib/public/assets/{button-xtAhYR-f.js → button-Dd-IhMC1.js} +1 -1
- package/lib/public/assets/{calendar-CupwJ7Bm.js → calendar-CAIeJwa5.js} +1 -1
- package/lib/public/assets/{check-CyuEoKkd.js → check-C4Jrjg8f.js} +1 -1
- package/lib/public/assets/{chevron-left-DgX-F9xK.js → chevron-left-BGzlf-4i.js} +1 -1
- package/lib/public/assets/{chevron-right-BKJgJnU9.js → chevron-right-_wYqDjDC.js} +1 -1
- package/lib/public/assets/{circle-check-CZdg8mMa.js → circle-check-Ckn00yMB.js} +1 -1
- package/lib/public/assets/{circle-x-CZH6t9z-.js → circle-x-pdVkomLa.js} +1 -1
- package/lib/public/assets/{clock-Cd-t7xpe.js → clock-B8vmwkHj.js} +1 -1
- package/lib/public/assets/{copy-C6zerX3I.js → copy-BAqnFI-F.js} +1 -1
- package/lib/public/assets/{device-explorer-B1pI6b_M.js → device-explorer-CYq_4F_N.js} +3 -3
- package/lib/public/assets/device-explorer-D2AqrrLN.css +1 -0
- package/lib/public/assets/{download-x9WP7mcL.js → download-B6UEAp_1.js} +1 -1
- package/lib/public/assets/{forgot-password-B0TEH4N-.js → forgot-password-CLqNvmrN.js} +1 -1
- package/lib/public/assets/{funnel-Cb40MfFN.js → funnel-Da90BBbT.js} +1 -1
- package/lib/public/assets/{index-BR1wTIPC.js → index-BuIIcmGp.js} +2 -2
- package/lib/public/assets/{index-BvH_ekRy.js → index-MX7IzqyW.js} +1 -1
- package/lib/public/assets/{input-DejcCiTI.js → input-Bq9aY-We.js} +1 -1
- package/lib/public/assets/{keycodes-Mavx5iGg.js → keycodes-CIhXIJyP.js} +1 -1
- package/lib/public/assets/{layers-BKqYLiHE.js → layers-bXjpswQI.js} +1 -1
- package/lib/public/assets/{lock-LFsFhc17.js → lock-pJ72y5Fy.js} +1 -1
- package/lib/public/assets/{login-x2pkzt-t.js → login-UOcHALV2.js} +1 -1
- package/lib/public/assets/{maintenance-settings-CYafcoV-.js → maintenance-settings-XnJgp5yb.js} +1 -1
- package/lib/public/assets/{monitor-_7_uJ8d9.js → monitor-QkAhI1Jx.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-BfLuURKN.js → mouse-pointer-2-B42Da0m4.js} +1 -1
- package/lib/public/assets/{network-Z_zBhpsD.js → network-CKVqcyp6.js} +1 -1
- package/lib/public/assets/{overview-BY5uv08S.js → overview-BORGkN3N.js} +1 -1
- package/lib/public/assets/{page-header-CP2UkykG.js → page-header-CKWFOjOI.js} +1 -1
- package/lib/public/assets/{plus-BTD0dnQ0.js → plus-BpFKVdC8.js} +1 -1
- package/lib/public/assets/{profile-page-BhJZuCS7.js → profile-page-CcbKBicY.js} +1 -1
- package/lib/public/assets/{reset-password-CMN0SO81.js → reset-password-BA0W9bxl.js} +1 -1
- package/lib/public/assets/{runbook-page-CBpPX9_5.js → runbook-page-Cp7ZBq3s.js} +1 -1
- package/lib/public/assets/{select-Uc9E5pye.js → select-DIZWIoVR.js} +1 -1
- package/lib/public/assets/{selector-detail-page-BSyBnRuU.js → selector-detail-page-C_4YuvAp.js} +1 -1
- package/lib/public/assets/{selector-health-DzqxNkzm.js → selector-health-CXjD_Xth.js} +1 -1
- package/lib/public/assets/{selector-health-page-CyPLjoD1.js → selector-health-page-CTqIHeoG.js} +1 -1
- package/lib/public/assets/{session-detail-page-BZQZN1P-.js → session-detail-page-B0KYkk8z.js} +1 -1
- package/lib/public/assets/{settings-CkSWiZq8.js → settings-BcRBKUXd.js} +1 -1
- package/lib/public/assets/{status-pill-outline-BFYni2qo.js → status-pill-outline-Dh2PXSf_.js} +1 -1
- package/lib/public/assets/{tablet-BSd-xSZK.js → tablet-_sXqZztt.js} +1 -1
- package/lib/public/assets/{teams-PpoEQz31.js → teams-DGHT8MKm.js} +1 -1
- package/lib/public/assets/{trash-2-BorJv5_u.js → trash-2-BV88cV3Z.js} +1 -1
- package/lib/public/assets/{upload-C3i7sAZl.js → upload-CIDH9wkj.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-CeZQ3EsH.js → use-password-reset-mode-DYGuSNwl.js} +1 -1
- package/lib/public/assets/{users-CEJ3fC78.js → users-7yviqflc.js} +1 -1
- package/lib/public/assets/{users-Drd7ifJx.js → users-BUE7MoTq.js} +1 -1
- package/lib/public/assets/{video-off-CNzn6bPE.js → video-off-BTRxtlGo.js} +1 -1
- package/lib/public/assets/{webhook-settings-BFqX6QXV.js → webhook-settings-DMKD9Pgm.js} +1 -1
- package/lib/public/assets/{zap-4yN8Jdfw.js → zap-CWE94qcK.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/src/InternalHttpClient.js +5 -1
- package/lib/src/XenonCapabilityManager.js +15 -1
- package/lib/src/app/appiumBasePath.js +32 -0
- package/lib/src/app/index.js +12 -0
- package/lib/src/app/insertBeforeRoutes.js +24 -4
- package/lib/src/app/registerCommandAuth.js +152 -33
- package/lib/src/app/routers/control.js +57 -13
- package/lib/src/app/routers/grid.js +28 -8
- package/lib/src/app/ws/h264StreamWs.js +13 -8
- package/lib/src/app/ws/logcatWs.js +12 -7
- package/lib/src/app/ws/upgradeRouter.js +215 -0
- package/lib/src/dashboard/event-manager.js +7 -6
- package/lib/src/data-service/device-service.js +152 -49
- package/lib/src/data-service/device-store.js +81 -10
- package/lib/src/data-service/deviceClaims.js +98 -0
- package/lib/src/data-service/deviceFieldOwners.js +32 -10
- package/lib/src/data-service/prisma-store.js +94 -31
- package/lib/src/device-managers/AndroidDeviceManager.js +13 -15
- package/lib/src/device-managers/HealthMonitorService.js +17 -2
- package/lib/src/device-managers/IOSDeviceManager.js +5 -1
- package/lib/src/device-managers/NodeDevices.js +7 -2
- package/lib/src/device-managers/android/AndroidH264StreamService.js +3 -3
- package/lib/src/device-managers/android/AndroidStreamService.js +6 -5
- package/lib/src/device-managers/android/previewHold.js +2 -2
- package/lib/src/device-managers/ios/IOSDiscoveryService.js +19 -8
- package/lib/src/device-managers/ios/IOSStreamService.js +158 -38
- package/lib/src/device-managers/localDeviceHosts.js +86 -0
- package/lib/src/device-managers/ownDeviceRow.js +38 -0
- package/lib/src/device-utils.js +78 -24
- package/lib/src/gateway/createHandoff.js +73 -0
- package/lib/src/gateway/defaultGateway.js +80 -0
- package/lib/src/gateway/forwardToNode.js +147 -0
- package/lib/src/gateway/hubSessionToken.js +356 -0
- package/lib/src/gateway/internalCall.js +90 -0
- package/lib/src/gateway/nodeSessionStatus.js +140 -0
- package/lib/src/gateway/nodeWebDriverUrl.js +139 -0
- package/lib/src/gateway/sessionCreate.js +136 -0
- package/lib/src/gateway/sessionGateway.js +201 -0
- package/lib/src/gateway/sessionLocator.js +100 -0
- package/lib/src/generated/client/edge.js +7 -4
- package/lib/src/generated/client/index-browser.js +4 -1
- package/lib/src/generated/client/index.d.ts +112 -1
- package/lib/src/generated/client/index.js +7 -4
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +9 -0
- package/lib/src/generated/client/wasm.js +4 -1
- package/lib/src/helpers/index.js +5 -1
- package/lib/src/logger.js +5 -0
- package/lib/src/middleware/authMiddleware.js +80 -0
- package/lib/src/middleware/commandAuth.js +31 -17
- package/lib/src/middleware/csrfMiddleware.js +3 -2
- package/lib/src/middleware/sessionListingFilter.js +153 -0
- package/lib/src/middleware/sessionUpgradeGuard.js +203 -0
- package/lib/src/plugin.js +27 -1
- package/lib/src/prisma.js +2 -1
- package/lib/src/services/DeviceReconciler.js +5 -2
- package/lib/src/services/OrphanSweeper.js +12 -19
- package/lib/src/services/PortAllocator.js +75 -16
- package/lib/src/services/ServerManager.js +50 -30
- package/lib/src/services/SessionLifecycleService.js +335 -76
- package/lib/src/services/ShutdownCoordinator.js +7 -1
- package/lib/src/services/SocketServer.js +11 -2
- package/lib/src/services/device-access/LiveSessionOwners.js +53 -0
- package/lib/src/services/device-access/SessionOwnerResolver.js +74 -0
- package/lib/src/services/lease/LeaseOrphanSweeper.js +32 -6
- package/lib/src/services/lease/LeaseService.js +19 -5
- package/lib/src/services/session/nodeCreateCaps.js +55 -0
- package/lib/src/services/session/sessionCredentials.js +7 -59
- package/lib/src/sessions/CloudSession.js +12 -0
- package/lib/src/sessions/LocalSession.js +52 -26
- package/lib/src/sessions/RemoteSession.js +116 -9
- package/lib/src/sessions/SessionManager.js +49 -6
- package/lib/src/sessions/appiumUmbrella.js +32 -0
- package/lib/test/helpers/appium-umbrella.js +84 -0
- package/lib/test/helpers/fake-device-table.js +97 -0
- package/lib/test/helpers/loki-stores.js +41 -0
- package/lib/test/helpers/loopbackRequest.js +77 -0
- package/lib/test/helpers/scratch-database.js +100 -0
- package/lib/test/helpers/scratch-port-leases.js +119 -0
- package/lib/test/helpers/test-container.js +35 -27
- package/lib/test/integration/androidDevices.spec.js +5 -4
- package/lib/test/integration/ios/01iOSSimulator.spec.js +6 -5
- package/lib/test/integration/ios/02iOSDevices.spec.js +3 -2
- package/lib/test/integration/prisma-device-store-add-devices.spec.js +14 -4
- package/lib/test/unit/AndroidDeviceManager.spec.js +8 -1
- package/lib/test/unit/DeviceModel.spec.js +9 -9
- package/lib/test/unit/HealingCascade.test.js +5 -1
- package/lib/test/unit/OrphanSweeper.test.js +17 -13
- package/lib/test/unit/SessionHeartbeatWrite.test.js +6 -7
- package/lib/test/unit/StreamTicketService.spec.js +4 -1
- package/lib/test/unit/TokenEndpoint.spec.js +17 -3
- package/lib/test/unit/aggregate-hotspots-session-scope.spec.js +5 -5
- package/lib/test/unit/android-h264-service.spec.js +6 -0
- package/lib/test/unit/android-stream-stall-eof.spec.js +10 -0
- package/lib/test/unit/apps-team-visibility.spec.js +16 -16
- package/lib/test/unit/auth-me-auth-disabled.test.js +7 -4
- package/lib/test/unit/authMiddleware.appTicket.spec.js +5 -5
- package/lib/test/unit/authMiddleware.bearer.spec.js +4 -1
- package/lib/test/unit/authMiddleware.mcpAudience.spec.js +4 -1
- package/lib/test/unit/authMiddleware.streamTicket.spec.js +19 -7
- package/lib/test/unit/authMiddleware.test.js +3 -0
- package/lib/test/unit/bug-report/route.spec.js +9 -9
- package/lib/test/unit/cliargs-admin-only.spec.js +3 -3
- package/lib/test/unit/clipboard-route.spec.js +18 -8
- package/lib/test/unit/command-auth-appium-server.spec.js +187 -32
- package/lib/test/unit/command-auth.spec.js +31 -31
- package/lib/test/unit/control-device-lookup.spec.js +9 -9
- package/lib/test/unit/control-forward-decision.spec.js +115 -0
- package/lib/test/unit/control-lookup-failure.spec.js +3 -3
- package/lib/test/unit/create-error-message.spec.js +43 -0
- package/lib/test/unit/create-handoff.spec.js +86 -0
- package/lib/test/unit/dashboard-events-team-scope.spec.js +6 -3
- package/lib/test/unit/device-access-guard.spec.js +28 -28
- package/lib/test/unit/device-service.spec.js +4 -0
- package/lib/test/unit/device-store-clear-at-boot.spec.js +48 -0
- package/lib/test/unit/device-sync-lost-update.spec.js +17 -89
- package/lib/test/unit/device-team-guard.spec.js +20 -20
- package/lib/test/unit/device-utils.spec.js +8 -0
- package/lib/test/unit/extract-team-cap.spec.js +65 -0
- package/lib/test/unit/grid-admin-only.spec.js +3 -3
- package/lib/test/unit/healing-tier-gate.spec.js +5 -1
- package/lib/test/unit/health-monitor-own-phones.spec.js +102 -0
- package/lib/test/unit/health-monitor-stream-lookup.spec.js +11 -1
- package/lib/test/unit/helpers.spec.js +27 -0
- package/lib/test/unit/hub-boot-sweep-recovery.spec.js +192 -0
- package/lib/test/unit/hub-create-once.spec.js +190 -0
- package/lib/test/unit/hub-create-session-umbrella.spec.js +160 -0
- package/lib/test/unit/hub-gateway-create-appium-server.spec.js +591 -0
- package/lib/test/unit/hub-node-control-forward.spec.js +291 -0
- package/lib/test/unit/hub-node-gateway-appium-server.spec.js +594 -0
- package/lib/test/unit/hub-node-race.spec.js +401 -0
- package/lib/test/unit/hub-node-same-host.spec.js +356 -0
- package/lib/test/unit/hub-register-removal.spec.js +165 -0
- package/lib/test/unit/hub-remote-session-heartbeat.spec.js +277 -0
- package/lib/test/unit/hub-routed-session-recovery.spec.js +151 -0
- package/lib/test/unit/hub-session-token.spec.js +265 -0
- package/lib/test/unit/insert-before-routes.spec.js +5 -5
- package/lib/test/unit/install-repository-app-team.spec.js +2 -2
- package/lib/test/unit/interceptor/MitmProxyHost.spec.js +23 -10
- package/lib/test/unit/internal-call.spec.js +181 -0
- package/lib/test/unit/ios-discovery-same-host.spec.js +193 -0
- package/lib/test/unit/ios-stream-appium-tunnel.spec.js +317 -0
- package/lib/test/unit/ios-stream-port-leases.spec.js +299 -0
- package/lib/test/unit/ios-wda-status-identity.spec.js +84 -0
- package/lib/test/unit/is-xenon-running.spec.js +69 -0
- package/lib/test/unit/lease/LeaseOrphanSweeper.spec.js +2 -2
- package/lib/test/unit/lease/LeaseService.spec.js +28 -1
- package/lib/test/unit/lease/lease-reap-held-phone.spec.js +154 -0
- package/lib/test/unit/lease/lease-sweeper-expiry.spec.js +92 -0
- package/lib/test/unit/lease/lease-team-visibility.spec.js +2 -2
- package/lib/test/unit/lease/lease-token-log-filter.spec.js +242 -64
- package/lib/test/unit/lease/lease-token-oracle.spec.js +20 -7
- package/lib/test/unit/lease/session-lease-proof.spec.js +8 -3
- package/lib/test/unit/logcat-ws.spec.js +4 -1
- package/lib/test/unit/loopback-request.spec.js +81 -0
- package/lib/test/unit/node-create-caps.spec.js +54 -0
- package/lib/test/unit/node-gateway-create-appium-server.spec.js +354 -0
- package/lib/test/unit/node-hub-session-owner.spec.js +223 -0
- package/lib/test/unit/omni-vision.spec.js +7 -3
- package/lib/test/unit/plugin.spec.js +45 -4
- package/lib/test/unit/port-allocator-os-probe.spec.js +151 -0
- package/lib/test/unit/profile-router.test.js +11 -11
- package/lib/test/unit/queue-requester.spec.js +5 -0
- package/lib/test/unit/queue-team-scope-routes.spec.js +17 -16
- package/lib/test/unit/queue-visibility.spec.js +1 -1
- package/lib/test/unit/recording-downloads-visibility.spec.js +2 -2
- package/lib/test/unit/recording-instrumentation.spec.js +5 -0
- package/lib/test/unit/recording-source-ended.spec.js +5 -0
- package/lib/test/unit/recording-store.spec.js +9 -3
- package/lib/test/unit/recordings-library-routes.spec.js +51 -51
- package/lib/test/unit/recordings-write-visibility.spec.js +2 -2
- package/lib/test/unit/remote-session-create.spec.js +130 -0
- package/lib/test/unit/reservation-team-visibility.spec.js +5 -5
- package/lib/test/unit/reset-link.test.js +14 -14
- package/lib/test/unit/session-app-resolution.spec.js +14 -10
- package/lib/test/unit/session-assets.spec.js +9 -9
- package/lib/test/unit/session-attribution-wiring.spec.js +29 -2
- package/lib/test/unit/session-create-layer.spec.js +255 -0
- package/lib/test/unit/session-credential-strip.spec.js +25 -9
- package/lib/test/unit/session-credentials.spec.js +0 -47
- package/lib/test/unit/session-listing-auth.spec.js +345 -0
- package/lib/test/unit/session-locator.spec.js +250 -0
- package/lib/test/unit/session-node-calls.spec.js +174 -0
- package/lib/test/unit/session-owner-resolver.spec.js +107 -0
- package/lib/test/unit/session-recovery-routing.spec.js +118 -0
- package/lib/test/unit/session-upgrade-guard.spec.js +571 -0
- package/lib/test/unit/shutdown-drain-remote.spec.js +58 -0
- package/lib/test/unit/socketServer.bearer.spec.js +3 -1
- package/lib/test/unit/stream-leave-route.spec.js +15 -7
- package/lib/test/unit/stream-own-device-row.spec.js +164 -0
- package/lib/test/unit/stream-stop-ownership.spec.js +13 -5
- package/lib/test/unit/stream-stop-recording.spec.js +13 -5
- package/lib/test/unit/stream-ticket-identity.spec.js +15 -5
- package/lib/test/unit/upgrade-router.spec.js +446 -0
- package/lib/test/unit/users-router.test.js +11 -8
- package/lib/test/unit/verify-credential.spec.js +4 -1
- package/lib/test/unit/webdriver-info-route.spec.js +64 -0
- package/lib/test/unit/ws-upgrade-routing-appium-server.spec.js +500 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -2
- package/prisma/migrations/20260929120000_device_claims/migration.sql +8 -0
- package/prisma/schema.prisma +9 -0
- package/lib/public/assets/BugReportButton-DKYsOdGV.js +0 -10
- package/lib/public/assets/device-explorer-DV8rWRxL.css +0 -1
- package/lib/src/proxy/wd-command-proxy.js +0 -221
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
require("reflect-metadata");
|
|
16
|
+
const chai_1 = require("chai");
|
|
17
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
18
|
+
const express_1 = __importDefault(require("express"));
|
|
19
|
+
const typedi_1 = require("typedi");
|
|
20
|
+
const RemoteSession_1 = require("../../src/sessions/RemoteSession");
|
|
21
|
+
const CloudSession_1 = require("../../src/sessions/CloudSession");
|
|
22
|
+
const LocalSession_1 = require("../../src/sessions/LocalSession");
|
|
23
|
+
const XenonSession_1 = require("../../src/sessions/XenonSession");
|
|
24
|
+
const hubSessionToken_1 = require("../../src/gateway/hubSessionToken");
|
|
25
|
+
const internalCall_1 = require("../../src/gateway/internalCall");
|
|
26
|
+
const nodeSessionStatus_1 = require("../../src/gateway/nodeSessionStatus");
|
|
27
|
+
const container_registration_1 = require("../helpers/container-registration");
|
|
28
|
+
const loopbackServer_1 = require("../helpers/loopbackServer");
|
|
29
|
+
/**
|
|
30
|
+
* The calls a session object makes about its session over HTTP.
|
|
31
|
+
*
|
|
32
|
+
* A RemoteSession's go to the node, with the hub's session token for that
|
|
33
|
+
* session, so a node with per-command auth on accepts them (the heartbeat's
|
|
34
|
+
* probe among them). A cloud provider is not a Xenon node and never gets one.
|
|
35
|
+
* A LocalSession's go to this server's own `/wd-internal` path with the
|
|
36
|
+
* per-process secret, never through an HTTP proxy, and its heartbeat asks the
|
|
37
|
+
* in-process driver instead of sending a command.
|
|
38
|
+
*/
|
|
39
|
+
describe('session calls to the server that runs the session', () => {
|
|
40
|
+
const loopback = (0, loopbackServer_1.loopbackServers)();
|
|
41
|
+
let seen;
|
|
42
|
+
let port;
|
|
43
|
+
let restore;
|
|
44
|
+
const env = Object.assign({}, process.env);
|
|
45
|
+
const device = { udid: 'u1', host: 'http://10.0.0.7:4725', platform: 'android' };
|
|
46
|
+
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
|
+
seen = [];
|
|
48
|
+
const app = (0, express_1.default)();
|
|
49
|
+
app.use(express_1.default.json());
|
|
50
|
+
app.all('*', (req, res) => {
|
|
51
|
+
seen.push({ method: req.method, url: req.originalUrl, headers: req.headers });
|
|
52
|
+
res.json({ value: req.path.endsWith('/source') ? '<hierarchy/>' : 'ok' });
|
|
53
|
+
});
|
|
54
|
+
const server = yield loopback.serve(app);
|
|
55
|
+
port = server.address().port;
|
|
56
|
+
restore = (0, container_registration_1.saveRegistrations)(hubSessionToken_1.HubSessionTokenIssuer, nodeSessionStatus_1.NodeSessionProbeSupport);
|
|
57
|
+
const probes = new nodeSessionStatus_1.NodeSessionProbeSupport();
|
|
58
|
+
probes.logger = { warn: () => undefined };
|
|
59
|
+
typedi_1.Container.set(nodeSessionStatus_1.NodeSessionProbeSupport, probes);
|
|
60
|
+
const issuer = new hubSessionToken_1.HubSessionTokenIssuer();
|
|
61
|
+
issuer.useSigner({ sign: (claims) => __awaiter(void 0, void 0, void 0, function* () { return `hub-token-for-${claims.sid}`; }) });
|
|
62
|
+
typedi_1.Container.set(hubSessionToken_1.HubSessionTokenIssuer, issuer);
|
|
63
|
+
}));
|
|
64
|
+
afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
|
+
process.env = Object.assign({}, env);
|
|
66
|
+
sinon_1.default.restore();
|
|
67
|
+
restore();
|
|
68
|
+
yield loopback.closeAll();
|
|
69
|
+
}));
|
|
70
|
+
function remote(Type = RemoteSession_1.RemoteSession) {
|
|
71
|
+
return new Type({
|
|
72
|
+
sessionId: 's-1',
|
|
73
|
+
device,
|
|
74
|
+
sessionResponse: {},
|
|
75
|
+
xenonOption: {},
|
|
76
|
+
baseUrl: `http://127.0.0.1:${port}/node-base`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
describe('RemoteSession (hub to node)', () => {
|
|
80
|
+
it('sends the hub’s session token on every call, the heartbeat probe included', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
81
|
+
const session = remote();
|
|
82
|
+
yield session.getScreenShot();
|
|
83
|
+
yield session.getPageSource();
|
|
84
|
+
yield session.startVideoRecording({ resolution: '720x1280' });
|
|
85
|
+
yield session.stopVideoRecording();
|
|
86
|
+
yield session.startPerformanceRecording();
|
|
87
|
+
yield session.stopPerformanceRecording();
|
|
88
|
+
const health = yield session.checkHealth();
|
|
89
|
+
(0, chai_1.expect)(health.isHealthy).to.equal(true);
|
|
90
|
+
(0, chai_1.expect)(seen.map((r) => `${r.method} ${r.url}`)).to.deep.equal([
|
|
91
|
+
'GET /node-base/session/s-1/screenshot',
|
|
92
|
+
'GET /node-base/session/s-1/source',
|
|
93
|
+
'POST /node-base/session/s-1/appium/start_recording_screen',
|
|
94
|
+
'POST /node-base/session/s-1/appium/stop_recording_screen',
|
|
95
|
+
'POST /node-base/session/s-1/execute/sync',
|
|
96
|
+
'POST /node-base/session/s-1/execute/sync',
|
|
97
|
+
// This node has no session-status route (no NODE_SESSION_STATUS_HEADER
|
|
98
|
+
// on its answer), so the heartbeat falls back to the WebDriver probe.
|
|
99
|
+
'GET /xenon/api/node/sessions/s-1',
|
|
100
|
+
'GET /node-base/session/s-1/timeouts',
|
|
101
|
+
]);
|
|
102
|
+
for (const r of seen)
|
|
103
|
+
(0, chai_1.expect)(r.headers[hubSessionToken_1.HUB_TOKEN_HEADER]).to.equal('hub-token-for-s-1');
|
|
104
|
+
for (const r of seen)
|
|
105
|
+
(0, chai_1.expect)(r.headers[internalCall_1.INTERNAL_CALL_HEADER]).to.equal(undefined);
|
|
106
|
+
}));
|
|
107
|
+
it('goes without a token when the hub cannot sign one', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
108
|
+
typedi_1.Container.get(hubSessionToken_1.HubSessionTokenIssuer).useSigner({
|
|
109
|
+
sign: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
110
|
+
throw new Error('JWT key service not initialized');
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
sinon_1.default.stub(typedi_1.Container.get(hubSessionToken_1.HubSessionTokenIssuer).logger, 'warn');
|
|
114
|
+
yield remote().getPageSource();
|
|
115
|
+
(0, chai_1.expect)(seen).to.have.length(1);
|
|
116
|
+
(0, chai_1.expect)(seen[0].headers[hubSessionToken_1.HUB_TOKEN_HEADER]).to.equal(undefined);
|
|
117
|
+
}));
|
|
118
|
+
it('says the node’s WebDriver URL it was made with', () => {
|
|
119
|
+
(0, chai_1.expect)(remote().getWebDriverUrl()).to.equal(`http://127.0.0.1:${port}/node-base`);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('CloudSession (hub to a cloud provider)', () => {
|
|
123
|
+
it('never sends a hub token to a cloud provider', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
124
|
+
yield remote(CloudSession_1.CloudSession).getPageSource();
|
|
125
|
+
yield remote(CloudSession_1.CloudSession).checkHealth();
|
|
126
|
+
(0, chai_1.expect)(seen).to.have.length(2);
|
|
127
|
+
for (const r of seen)
|
|
128
|
+
(0, chai_1.expect)(r.headers[hubSessionToken_1.HUB_TOKEN_HEADER]).to.equal(undefined);
|
|
129
|
+
}));
|
|
130
|
+
});
|
|
131
|
+
describe('LocalSession (this server)', () => {
|
|
132
|
+
function local(driver = {}) {
|
|
133
|
+
return new LocalSession_1.LocalSession({
|
|
134
|
+
sessionId: 's-1',
|
|
135
|
+
device: Object.assign(Object.assign({}, device), { host: `http://127.0.0.1:${port}` }),
|
|
136
|
+
sessionResponse: {},
|
|
137
|
+
xenonOption: {},
|
|
138
|
+
driver: Object.assign({ opts: { address: '0.0.0.0', port, basePath: '/wd/hub' }, sessions: {} }, driver),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
it('falls back to /wd-internal with the secret, and never through a proxy', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
142
|
+
// A proxy in the environment would receive the secret; it must be skipped.
|
|
143
|
+
process.env.HTTP_PROXY = 'http://127.0.0.1:1';
|
|
144
|
+
process.env.http_proxy = 'http://127.0.0.1:1';
|
|
145
|
+
delete process.env.NO_PROXY;
|
|
146
|
+
delete process.env.no_proxy;
|
|
147
|
+
const session = local();
|
|
148
|
+
(0, chai_1.expect)(yield session.getPageSource()).to.equal('<hierarchy/>');
|
|
149
|
+
yield session.stopVideoRecording();
|
|
150
|
+
(0, chai_1.expect)(seen.map((r) => `${r.method} ${r.url}`)).to.deep.equal([
|
|
151
|
+
'GET /wd/hub/wd-internal/session/s-1/source',
|
|
152
|
+
'POST /wd/hub/wd-internal/session/s-1/appium/stop_recording_screen',
|
|
153
|
+
]);
|
|
154
|
+
const secret = (0, internalCall_1.internalCallHeaders)()[internalCall_1.INTERNAL_CALL_HEADER];
|
|
155
|
+
for (const r of seen) {
|
|
156
|
+
(0, chai_1.expect)(r.headers[internalCall_1.INTERNAL_CALL_HEADER]).to.equal(secret);
|
|
157
|
+
(0, chai_1.expect)(r.headers[hubSessionToken_1.HUB_TOKEN_HEADER]).to.equal(undefined);
|
|
158
|
+
}
|
|
159
|
+
}));
|
|
160
|
+
it('checks its health in process: no command, so no new-command timeout is reset', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
|
+
const exists = sinon_1.default.stub().returns(true);
|
|
162
|
+
const healthy = yield local({ sessionExists: exists }).checkHealth();
|
|
163
|
+
(0, chai_1.expect)(healthy).to.deep.include({ isHealthy: true, errorType: XenonSession_1.HealthErrorType.NONE });
|
|
164
|
+
exists.returns(false);
|
|
165
|
+
const gone = yield local({ sessionExists: exists }).checkHealth();
|
|
166
|
+
(0, chai_1.expect)(gone).to.deep.include({
|
|
167
|
+
isHealthy: false,
|
|
168
|
+
errorType: XenonSession_1.HealthErrorType.SESSION_NOT_FOUND,
|
|
169
|
+
});
|
|
170
|
+
(0, chai_1.expect)(exists.alwaysCalledWith('s-1')).to.equal(true);
|
|
171
|
+
(0, chai_1.expect)(seen).to.deep.equal([]);
|
|
172
|
+
}));
|
|
173
|
+
});
|
|
174
|
+
});
|
|
@@ -139,3 +139,110 @@ describe('SessionOwnerResolver.displayName', () => {
|
|
|
139
139
|
(0, chai_1.expect)(db.calls.user).to.equal(1);
|
|
140
140
|
}));
|
|
141
141
|
});
|
|
142
|
+
// Prisma-shaped findMany delegates over fixed tables, recording each query so
|
|
143
|
+
// "one query for the whole list" is observable.
|
|
144
|
+
function tableDb(opts) {
|
|
145
|
+
const queries = [];
|
|
146
|
+
const pick = (rows, where) => rows.filter((row) => where.id.in.includes(row.id));
|
|
147
|
+
return {
|
|
148
|
+
queries,
|
|
149
|
+
session: {
|
|
150
|
+
findUnique: () => __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
throw new Error('ownersOf must not look sessions up one by one');
|
|
152
|
+
}),
|
|
153
|
+
findMany: (_a) => __awaiter(this, [_a], void 0, function* ({ where }) {
|
|
154
|
+
queries.push({ table: 'session', ids: [...where.id.in] });
|
|
155
|
+
return pick(opts.sessions, where);
|
|
156
|
+
}),
|
|
157
|
+
},
|
|
158
|
+
apiKey: {
|
|
159
|
+
findUnique: () => __awaiter(this, void 0, void 0, function* () {
|
|
160
|
+
throw new Error('ownersOf must not look keys up one by one');
|
|
161
|
+
}),
|
|
162
|
+
findMany: (_a) => __awaiter(this, [_a], void 0, function* ({ where }) {
|
|
163
|
+
var _b;
|
|
164
|
+
queries.push({ table: 'apiKey', ids: [...where.id.in] });
|
|
165
|
+
return pick((_b = opts.apiKeys) !== null && _b !== void 0 ? _b : [], where);
|
|
166
|
+
}),
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
describe('SessionOwnerResolver.ownersOf', () => {
|
|
171
|
+
it("resolves every session's owner with one query, by ownerOf's rule", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
172
|
+
const db = tableDb({
|
|
173
|
+
sessions: [
|
|
174
|
+
{ id: 's1', api_key_id: 'k1', user_id: 'usr_direct' },
|
|
175
|
+
{ id: 's2', api_key_id: null, user_id: null },
|
|
176
|
+
],
|
|
177
|
+
});
|
|
178
|
+
const r = new SessionOwnerResolver_1.SessionOwnerResolver(db);
|
|
179
|
+
const owners = yield r.ownersOf(['s1', 's2', 's-missing']);
|
|
180
|
+
(0, chai_1.expect)([...owners.entries()]).to.deep.equal([
|
|
181
|
+
['s1', 'usr_direct'],
|
|
182
|
+
['s2', null],
|
|
183
|
+
['s-missing', null],
|
|
184
|
+
]);
|
|
185
|
+
(0, chai_1.expect)(db.queries).to.deep.equal([{ table: 'session', ids: ['s1', 's2', 's-missing'] }]);
|
|
186
|
+
}));
|
|
187
|
+
it('takes the ApiKey hop in one more query, only for rows written before user_id', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
188
|
+
const db = tableDb({
|
|
189
|
+
sessions: [
|
|
190
|
+
{ id: 's1', api_key_id: 'k1', user_id: null },
|
|
191
|
+
{ id: 's2', api_key_id: 'k2', user_id: null },
|
|
192
|
+
{ id: 's3', api_key_id: 'k1', user_id: 'usr_direct' },
|
|
193
|
+
],
|
|
194
|
+
apiKeys: [
|
|
195
|
+
{ id: 'k1', userId: 'usr_legacy_1' },
|
|
196
|
+
{ id: 'k2', userId: 'usr_legacy_2' },
|
|
197
|
+
],
|
|
198
|
+
});
|
|
199
|
+
const r = new SessionOwnerResolver_1.SessionOwnerResolver(db);
|
|
200
|
+
const owners = yield r.ownersOf(['s1', 's2', 's3']);
|
|
201
|
+
(0, chai_1.expect)(owners.get('s1')).to.equal('usr_legacy_1');
|
|
202
|
+
(0, chai_1.expect)(owners.get('s2')).to.equal('usr_legacy_2');
|
|
203
|
+
(0, chai_1.expect)(owners.get('s3')).to.equal('usr_direct');
|
|
204
|
+
(0, chai_1.expect)(db.queries).to.have.length(2);
|
|
205
|
+
(0, chai_1.expect)(db.queries[1].table).to.equal('apiKey');
|
|
206
|
+
(0, chai_1.expect)(db.queries[1].ids).to.have.members(['k1', 'k2']);
|
|
207
|
+
}));
|
|
208
|
+
it('agrees with ownerOf and shares its cache of resolved owners', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
209
|
+
const db = tableDb({ sessions: [{ id: 's1', api_key_id: null, user_id: 'usr_a' }] });
|
|
210
|
+
const r = new SessionOwnerResolver_1.SessionOwnerResolver(db);
|
|
211
|
+
(0, chai_1.expect)((yield r.ownersOf(['s1'])).get('s1')).to.equal('usr_a');
|
|
212
|
+
// Resolved above, so ownerOf answers from the cache; its findUnique would throw.
|
|
213
|
+
(0, chai_1.expect)(yield r.ownerOf('s1')).to.equal('usr_a');
|
|
214
|
+
// And a cached owner is not queried again.
|
|
215
|
+
yield r.ownersOf(['s1']);
|
|
216
|
+
(0, chai_1.expect)(db.queries).to.have.length(1);
|
|
217
|
+
}));
|
|
218
|
+
it('does not cache an unresolved owner', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
219
|
+
const db = tableDb({ sessions: [] });
|
|
220
|
+
const r = new SessionOwnerResolver_1.SessionOwnerResolver(db);
|
|
221
|
+
yield r.ownersOf(['s1']);
|
|
222
|
+
yield r.ownersOf(['s1']);
|
|
223
|
+
(0, chai_1.expect)(db.queries).to.have.length(2);
|
|
224
|
+
}));
|
|
225
|
+
it('makes no query for an empty list, and asks for a repeated id once', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
226
|
+
const db = tableDb({ sessions: [{ id: 's1', api_key_id: null, user_id: 'usr_a' }] });
|
|
227
|
+
const r = new SessionOwnerResolver_1.SessionOwnerResolver(db);
|
|
228
|
+
(0, chai_1.expect)((yield r.ownersOf([])).size).to.equal(0);
|
|
229
|
+
(0, chai_1.expect)(db.queries).to.deep.equal([]);
|
|
230
|
+
yield r.ownersOf(['s1', 's1']);
|
|
231
|
+
(0, chai_1.expect)(db.queries).to.deep.equal([{ table: 'session', ids: ['s1'] }]);
|
|
232
|
+
}));
|
|
233
|
+
it('lets a failed query reject, so the caller can fail closed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
234
|
+
const db = tableDb({ sessions: [] });
|
|
235
|
+
db.session.findMany = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
236
|
+
throw new Error('database is locked');
|
|
237
|
+
});
|
|
238
|
+
const r = new SessionOwnerResolver_1.SessionOwnerResolver(db);
|
|
239
|
+
let caught;
|
|
240
|
+
try {
|
|
241
|
+
yield r.ownersOf(['s1']);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
caught = err;
|
|
245
|
+
}
|
|
246
|
+
(0, chai_1.expect)(caught === null || caught === void 0 ? void 0 : caught.message).to.equal('database is locked');
|
|
247
|
+
}));
|
|
248
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
require("reflect-metadata");
|
|
16
|
+
const chai_1 = require("chai");
|
|
17
|
+
const express_1 = __importDefault(require("express"));
|
|
18
|
+
const typedi_1 = require("typedi");
|
|
19
|
+
const SessionManager_1 = require("../../src/sessions/SessionManager");
|
|
20
|
+
const device_store_1 = require("../../src/data-service/device-store");
|
|
21
|
+
const prisma_store_1 = require("../../src/data-service/prisma-store");
|
|
22
|
+
const localDeviceHosts_1 = require("../../src/device-managers/localDeviceHosts");
|
|
23
|
+
const SessionType_1 = __importDefault(require("../../src/enums/SessionType"));
|
|
24
|
+
const nodeWebDriverUrl_1 = require("../../src/gateway/nodeWebDriverUrl");
|
|
25
|
+
const container_registration_1 = require("../helpers/container-registration");
|
|
26
|
+
const loopbackServer_1 = require("../helpers/loopbackServer");
|
|
27
|
+
const scratch_database_1 = require("../helpers/scratch-database");
|
|
28
|
+
/**
|
|
29
|
+
* After a hub restart, SessionManager rebuilds the sessions still running on
|
|
30
|
+
* nodes from the database, and the session gateway routes them from there.
|
|
31
|
+
* A recovered session must point at its node's own base path, and a session
|
|
32
|
+
* that ran on the hub's own phone (its driver died with the hub) must never be
|
|
33
|
+
* rebuilt as a remote session pointing back at the hub.
|
|
34
|
+
*/
|
|
35
|
+
describe('recovering remote sessions after a hub restart', function () {
|
|
36
|
+
this.timeout(60000);
|
|
37
|
+
const scratch = (0, scratch_database_1.useScratchDatabase)();
|
|
38
|
+
const loopback = (0, loopbackServer_1.loopbackServers)();
|
|
39
|
+
let restore;
|
|
40
|
+
let savedStore;
|
|
41
|
+
let nodeOrigin;
|
|
42
|
+
const HUB = 'http://10.0.0.1:4724';
|
|
43
|
+
beforeEach(() => __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
restore = (0, container_registration_1.saveRegistrations)(nodeWebDriverUrl_1.NodeBasePathResolver);
|
|
45
|
+
typedi_1.Container.set(nodeWebDriverUrl_1.NodeBasePathResolver, new nodeWebDriverUrl_1.NodeBasePathResolver());
|
|
46
|
+
savedStore = device_store_1.DeviceStoreFactory._deviceStore;
|
|
47
|
+
device_store_1.DeviceStoreFactory._deviceStore = new prisma_store_1.PrismaDeviceStore();
|
|
48
|
+
yield scratch.db.session.deleteMany({});
|
|
49
|
+
yield scratch.db.device.deleteMany({});
|
|
50
|
+
const node = (0, express_1.default)();
|
|
51
|
+
node.get('/xenon/api/webdriver', (0, nodeWebDriverUrl_1.webdriverInfoHandler)(() => '/node-base'));
|
|
52
|
+
const server = yield loopback.serve(node);
|
|
53
|
+
nodeOrigin = `http://127.0.0.1:${server.address().port}`;
|
|
54
|
+
}));
|
|
55
|
+
afterEach(() => __awaiter(this, void 0, void 0, function* () {
|
|
56
|
+
device_store_1.DeviceStoreFactory._deviceStore = savedStore;
|
|
57
|
+
restore();
|
|
58
|
+
yield loopback.closeAll();
|
|
59
|
+
}));
|
|
60
|
+
function running(id, udid, host, nodeId) {
|
|
61
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
yield scratch.db.device.create({
|
|
63
|
+
data: { udid, host, nodeId, platform: 'android', busy: true, session_id: id },
|
|
64
|
+
});
|
|
65
|
+
yield scratch.db.session.create({
|
|
66
|
+
data: {
|
|
67
|
+
id,
|
|
68
|
+
desired_capabilities: '{}',
|
|
69
|
+
session_capabilities: '{}',
|
|
70
|
+
node_id: nodeId,
|
|
71
|
+
has_live_video: false,
|
|
72
|
+
device_udid: udid,
|
|
73
|
+
device_platform: 'android',
|
|
74
|
+
device_version: '14',
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
it("rebuilds a node's session under the node's own base path", () => __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
yield running('on-node', 'phone-n', nodeOrigin, 'node-1');
|
|
81
|
+
const manager = new SessionManager_1.SessionManager();
|
|
82
|
+
const recovered = yield manager.recoverActiveSessions('hub-new-id', '/wd/hub', (h) => h === HUB);
|
|
83
|
+
(0, chai_1.expect)(recovered).to.equal(1);
|
|
84
|
+
const session = manager.getSession('on-node');
|
|
85
|
+
(0, chai_1.expect)(session.getType()).to.equal(SessionType_1.default.REMOTE);
|
|
86
|
+
(0, chai_1.expect)(session.getWebDriverUrl()).to.equal(`${nodeOrigin}/node-base`);
|
|
87
|
+
}));
|
|
88
|
+
describe("the hub's boot", () => {
|
|
89
|
+
const hubHosts = { origin: HUB, hosts: new Set([HUB]) };
|
|
90
|
+
it("keeps a node's phone, so the session on it is recovered rather than failed", () => __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
yield running('on-node', 'phone-n', nodeOrigin, 'node-1');
|
|
92
|
+
yield running('on-hub', 'phone-h', HUB, 'hub-old-id');
|
|
93
|
+
// What ServerManager does at boot, in order: clear, then recover.
|
|
94
|
+
const cleared = (0, localDeviceHosts_1.devicesClearedAtBoot)({ hub: undefined }, hubHosts);
|
|
95
|
+
yield device_store_1.DeviceStoreFactory.getStore().clearStorage(cleared);
|
|
96
|
+
const left = yield scratch.db.device.findMany({ select: { udid: true } });
|
|
97
|
+
(0, chai_1.expect)(left.map((d) => d.udid)).to.deep.equal(['phone-n']);
|
|
98
|
+
const manager = new SessionManager_1.SessionManager();
|
|
99
|
+
const recovered = yield manager.recoverActiveSessions('hub-new-id', '/wd/hub', (h) => h === HUB);
|
|
100
|
+
(0, chai_1.expect)(recovered).to.equal(1);
|
|
101
|
+
(0, chai_1.expect)(manager.getSession('on-node')).to.not.equal(undefined);
|
|
102
|
+
const hubRow = yield scratch.db.session.findUnique({ where: { id: 'on-hub' } });
|
|
103
|
+
(0, chai_1.expect)(hubRow === null || hubRow === void 0 ? void 0 : hubRow.status).to.equal('failed');
|
|
104
|
+
}));
|
|
105
|
+
it('a node still clears every phone it has, as before', () => {
|
|
106
|
+
(0, chai_1.expect)((0, localDeviceHosts_1.devicesClearedAtBoot)({ hub: 'http://hub:4724' }, hubHosts)).to.equal(undefined);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
it("marks a session on the hub's own phone failed, though its node id changed with the restart", () => __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
yield running('on-hub', 'phone-h', HUB, 'hub-old-id');
|
|
111
|
+
const manager = new SessionManager_1.SessionManager();
|
|
112
|
+
const recovered = yield manager.recoverActiveSessions('hub-new-id', '/wd/hub', (h) => h === HUB);
|
|
113
|
+
(0, chai_1.expect)(recovered).to.equal(0);
|
|
114
|
+
(0, chai_1.expect)(manager.getSession('on-hub')).to.equal(undefined);
|
|
115
|
+
const row = yield scratch.db.session.findUnique({ where: { id: 'on-hub' } });
|
|
116
|
+
(0, chai_1.expect)(row === null || row === void 0 ? void 0 : row.status).to.equal('failed');
|
|
117
|
+
}));
|
|
118
|
+
});
|