@xenon-device-management/xenon 2.0.0 → 2.2.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 +3 -4
- package/lib/public/assets/{AnnotationOverlay-BmTPCAds.js → AnnotationOverlay-XpewX5nn.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-B5eB7f8B.js → ApiKeyGate-BQDv3ZCC.js} +1 -1
- package/lib/public/assets/BugReportButton-C7OmS61B.js +10 -0
- package/lib/public/assets/{DeviceMosaicView-9DUoZnQi.js → DeviceMosaicView-wl3l2Zlt.js} +1 -1
- package/lib/public/assets/{EmptyState-lvyNiXev.js → EmptyState-fPxujqqE.js} +1 -1
- package/lib/public/assets/{FieldGroup-BduMPM9K.js → FieldGroup-5bCutWrs.js} +1 -1
- package/lib/public/assets/{FilterMenu-BSqX6I94.js → FilterMenu-owsBiDT1.js} +1 -1
- package/lib/public/assets/{Menu-Ba5cT7jZ.js → Menu-DFhqzPSt.js} +1 -1
- package/lib/public/assets/{Modal-CceoMY7D.js → Modal-Bw-a-E-e.js} +1 -1
- package/lib/public/assets/{RecordingPage-6T9dOidc.js → RecordingPage-WtByf5bC.js} +1 -1
- package/lib/public/assets/{RecordingsPage-qj6FdZXR.js → RecordingsPage-FpWWFNgT.js} +1 -1
- package/lib/public/assets/{SegmentedControl-CSRuZ8TR.js → SegmentedControl-Jzn9c7o8.js} +1 -1
- package/lib/public/assets/{SettingCard-Bp1kz4By.js → SettingCard-BR7nOhpT.js} +1 -1
- package/lib/public/assets/{Table-LV7zFyZg.js → Table-B_Va3V05.js} +1 -1
- package/lib/public/assets/{activity-D17TUloi.js → activity-DxRFWvkN.js} +1 -1
- package/lib/public/assets/{ai-settings-qvbhY89P.js → ai-settings-CQf-9_V6.js} +1 -1
- package/lib/public/assets/{api-keys-BfpvdG7G.js → api-keys-SoE1Kd7N.js} +1 -1
- package/lib/public/assets/{apps-Dbnzd0fW.js → apps-CgGXTlgn.js} +1 -1
- package/lib/public/assets/{arrow-left-DD1VjtC7.js → arrow-left-uQ6WIwiC.js} +1 -1
- package/lib/public/assets/{arrow-right-D0pnM3ma.js → arrow-right-C9RyfzXS.js} +1 -1
- package/lib/public/assets/{arrow-up-right-BIC8JOV6.js → arrow-up-right-DFHi_8yG.js} +1 -1
- package/lib/public/assets/{auth-shell-DWHPXSnI.js → auth-shell-Bii1t4qc.js} +2 -2
- package/lib/public/assets/{builds-page-ClCNBUv_.js → builds-page-Bnv8Z9hi.js} +1 -1
- package/lib/public/assets/{button-xtAhYR-f.js → button-_Y78wXiP.js} +1 -1
- package/lib/public/assets/{calendar-CupwJ7Bm.js → calendar-D4MqSOc2.js} +1 -1
- package/lib/public/assets/{check-CyuEoKkd.js → check-uGHCXe-L.js} +1 -1
- package/lib/public/assets/{chevron-left-DgX-F9xK.js → chevron-left-Dy1OqpzB.js} +1 -1
- package/lib/public/assets/{chevron-right-BKJgJnU9.js → chevron-right-BUsldEli.js} +1 -1
- package/lib/public/assets/{circle-check-CZdg8mMa.js → circle-check-D3Qq0tKp.js} +1 -1
- package/lib/public/assets/{circle-x-CZH6t9z-.js → circle-x-B4IOD1DE.js} +1 -1
- package/lib/public/assets/{clock-Cd-t7xpe.js → clock-BfYfzvSg.js} +1 -1
- package/lib/public/assets/{copy-C6zerX3I.js → copy-DZwYE5Va.js} +1 -1
- package/lib/public/assets/{device-explorer-B1pI6b_M.js → device-explorer-CRG4Utt5.js} +3 -3
- package/lib/public/assets/device-explorer-D2AqrrLN.css +1 -0
- package/lib/public/assets/{download-x9WP7mcL.js → download-CCDYo_TN.js} +1 -1
- package/lib/public/assets/{forgot-password-B0TEH4N-.js → forgot-password-DuLCmVFb.js} +1 -1
- package/lib/public/assets/{funnel-Cb40MfFN.js → funnel-CtKNPqzb.js} +1 -1
- package/lib/public/assets/{index-BR1wTIPC.js → index-3uxl3o7K.js} +43 -43
- package/lib/public/assets/{index-BvH_ekRy.js → index-Zy1Op5C1.js} +1 -1
- package/lib/public/assets/{input-DejcCiTI.js → input-CzTWvUXZ.js} +1 -1
- package/lib/public/assets/{keycodes-Mavx5iGg.js → keycodes-CI-nYDFM.js} +1 -1
- package/lib/public/assets/{layers-BKqYLiHE.js → layers-YZZGFyTX.js} +1 -1
- package/lib/public/assets/{lock-LFsFhc17.js → lock-L8tzWgDo.js} +1 -1
- package/lib/public/assets/{login-x2pkzt-t.js → login-DBbX-fu8.js} +1 -1
- package/lib/public/assets/{maintenance-settings-CYafcoV-.js → maintenance-settings-AJwkOIsE.js} +1 -1
- package/lib/public/assets/{monitor-_7_uJ8d9.js → monitor-Di_66F2m.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-BfLuURKN.js → mouse-pointer-2-Cnab2zdg.js} +1 -1
- package/lib/public/assets/{network-Z_zBhpsD.js → network-DiSumY-1.js} +1 -1
- package/lib/public/assets/{overview-BY5uv08S.js → overview-CwE4gXl5.js} +1 -1
- package/lib/public/assets/{page-header-CP2UkykG.js → page-header-CfR4Sm9V.js} +1 -1
- package/lib/public/assets/{plus-BTD0dnQ0.js → plus-DOS8P9CQ.js} +1 -1
- package/lib/public/assets/{profile-page-BhJZuCS7.js → profile-page-BYpHwQIH.js} +1 -1
- package/lib/public/assets/{reset-password-CMN0SO81.js → reset-password-BRVJU_qV.js} +1 -1
- package/lib/public/assets/{runbook-page-CBpPX9_5.js → runbook-page-Bi3JvAFZ.js} +1 -1
- package/lib/public/assets/{select-Uc9E5pye.js → select-Cqx7pEou.js} +1 -1
- package/lib/public/assets/{selector-detail-page-BSyBnRuU.js → selector-detail-page-C0DsncKj.js} +1 -1
- package/lib/public/assets/{selector-health-DzqxNkzm.js → selector-health-CAhpdilF.js} +1 -1
- package/lib/public/assets/{selector-health-page-CyPLjoD1.js → selector-health-page-CrCVeW2m.js} +1 -1
- package/lib/public/assets/{session-detail-page-BZQZN1P-.js → session-detail-page-F_hwYqEt.js} +1 -1
- package/lib/public/assets/{settings-CkSWiZq8.js → settings-C-s5pypL.js} +1 -1
- package/lib/public/assets/{status-pill-outline-BFYni2qo.js → status-pill-outline-DY5uzv6A.js} +1 -1
- package/lib/public/assets/{tablet-BSd-xSZK.js → tablet-Dli7gwyg.js} +1 -1
- package/lib/public/assets/{teams-PpoEQz31.js → teams-ui4IhHNC.js} +1 -1
- package/lib/public/assets/{trash-2-BorJv5_u.js → trash-2-BV4hSmXJ.js} +1 -1
- package/lib/public/assets/{upload-C3i7sAZl.js → upload-DkGy_J-L.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-CeZQ3EsH.js → use-password-reset-mode-BFtMqQOh.js} +1 -1
- package/lib/public/assets/{users-Drd7ifJx.js → users-BU0nXIVe.js} +1 -1
- package/lib/public/assets/{users-CEJ3fC78.js → users-Nb_rzRmF.js} +1 -1
- package/lib/public/assets/{video-off-CNzn6bPE.js → video-off-CUNzD2kH.js} +1 -1
- package/lib/public/assets/{webhook-settings-BFqX6QXV.js → webhook-settings-Ca3UFXZe.js} +1 -1
- package/lib/public/assets/{zap-4yN8Jdfw.js → zap-B2bH2vKX.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 +11 -104
- package/lib/src/app/routers/grid.js +28 -8
- package/lib/src/app/routers/nodePhoneControl.js +301 -0
- package/lib/src/app/ws/h264StreamWs.js +43 -12
- package/lib/src/app/ws/logcatWs.js +48 -14
- package/lib/src/app/ws/nodeSocketRelay.js +167 -0
- 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 +92 -10
- package/lib/src/data-service/deviceClaims.js +116 -0
- package/lib/src/data-service/deviceFieldOwners.js +32 -10
- package/lib/src/data-service/prisma-store.js +102 -31
- package/lib/src/device-managers/AndroidDeviceManager.js +40 -42
- 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 +13 -5
- package/lib/src/device-managers/android/AndroidStreamService.js +9 -6
- 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 +162 -40
- 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/deviceAccessGuard.js +4 -1
- 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 +63 -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/device-access/ticketActorAccess.js +3 -1
- package/lib/src/services/lease/LeaseOrphanSweeper.js +34 -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/own-node-id.js +29 -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/setup/scratch-default-database.js +32 -0
- package/lib/test/unit/AndroidDeviceManager.spec.js +56 -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 +24 -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 +22 -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 +66 -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 +608 -0
- package/lib/test/unit/hub-node-gateway-appium-server.spec.js +594 -0
- package/lib/test/unit/hub-node-race.spec.js +426 -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 +6 -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 +107 -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 +12 -1
- package/lib/test/unit/loopback-request.spec.js +81 -0
- package/lib/test/unit/mark-node-busy-loki.spec.js +53 -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/node-phone-control.spec.js +131 -0
- package/lib/test/unit/node-socket-relay.spec.js +357 -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 +19 -7
- package/lib/test/unit/stream-own-device-row.spec.js +164 -0
- package/lib/test/unit/stream-stop-ownership.spec.js +17 -5
- package/lib/test/unit/stream-stop-recording.spec.js +17 -5
- package/lib/test/unit/stream-ticket-identity.spec.js +26 -6
- package/lib/test/unit/stream-watchdog-timers.spec.js +46 -0
- 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 +3 -4
- 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
|
@@ -15,7 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
require("reflect-metadata");
|
|
16
16
|
const chai_1 = require("chai");
|
|
17
17
|
const express_1 = __importDefault(require("express"));
|
|
18
|
-
const
|
|
18
|
+
const loopbackRequest_1 = __importDefault(require("../helpers/loopbackRequest"));
|
|
19
19
|
const deviceAccessGuard_1 = require("../../src/middleware/deviceAccessGuard");
|
|
20
20
|
const UDID = 'DEV-1';
|
|
21
21
|
const ALICE = 'usr_alice';
|
|
@@ -66,19 +66,19 @@ function appWith(opts) {
|
|
|
66
66
|
}
|
|
67
67
|
describe('deviceAccessGuard', () => {
|
|
68
68
|
it('lets the lock owner through', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
-
const res = yield (0,
|
|
69
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: ALICE })).post(`/control/${UDID}/tap`);
|
|
70
70
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
71
71
|
(0, chai_1.expect)(res.body.reached).to.equal(true);
|
|
72
72
|
}));
|
|
73
73
|
it('denies a foreign holder with 409 and names them', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
74
|
-
const res = yield (0,
|
|
74
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB })).post(`/control/${UDID}/tap`);
|
|
75
75
|
(0, chai_1.expect)(res.status).to.equal(409);
|
|
76
76
|
(0, chai_1.expect)(res.body.error).to.equal('device_held_by_another_user');
|
|
77
77
|
(0, chai_1.expect)(res.body.message).to.contain('alice@example.com');
|
|
78
78
|
(0, chai_1.expect)(res.body.holder).to.deep.equal({ userId: ALICE, name: 'alice@example.com' });
|
|
79
79
|
}));
|
|
80
80
|
it('never blocks a GET', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
81
|
-
const res = yield (0,
|
|
81
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB })).get(`/control/${UDID}/screenshot`);
|
|
82
82
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
83
83
|
}));
|
|
84
84
|
// The clipboard is the one read that is credential-shaped: it routinely holds
|
|
@@ -88,26 +88,26 @@ describe('deviceAccessGuard', () => {
|
|
|
88
88
|
// reading a stranger's clipboard, so it takes the ownership check despite
|
|
89
89
|
// being a GET.
|
|
90
90
|
it('blocks reading the clipboard of a device another user holds', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
91
|
-
const res = yield (0,
|
|
91
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB })).get(`/control/${UDID}/clipboard`);
|
|
92
92
|
(0, chai_1.expect)(res.status).to.equal(409);
|
|
93
93
|
(0, chai_1.expect)(res.body.error).to.equal('device_held_by_another_user');
|
|
94
94
|
(0, chai_1.expect)(JSON.stringify(res.body)).to.not.contain('hunter2');
|
|
95
95
|
}));
|
|
96
96
|
it('lets the holder read their own clipboard', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
-
const res = yield (0,
|
|
97
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: ALICE })).get(`/control/${UDID}/clipboard`);
|
|
98
98
|
(0, chai_1.expect)(res.status, JSON.stringify(res.body)).to.equal(200);
|
|
99
99
|
(0, chai_1.expect)(res.body.content).to.equal('hunter2');
|
|
100
100
|
}));
|
|
101
101
|
it('lets an admin read the clipboard', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
102
|
-
const res = yield (0,
|
|
102
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, scopes: 'admin' })).get(`/control/${UDID}/clipboard`);
|
|
103
103
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
104
104
|
}));
|
|
105
105
|
it('allows the clipboard read when the device is free', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
106
|
-
const res = yield (0,
|
|
106
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, busy: false })).get(`/control/${UDID}/clipboard`);
|
|
107
107
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
108
108
|
}));
|
|
109
109
|
it('blocks the clipboard of a device running another user’s Appium session', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
110
|
-
const res = yield (0,
|
|
110
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, sessionId: 'appium-1', sessionOwner: ALICE })).get(`/control/${UDID}/clipboard`);
|
|
111
111
|
(0, chai_1.expect)(res.status).to.equal(409);
|
|
112
112
|
(0, chai_1.expect)(res.body.error).to.equal('device_in_use_by_session');
|
|
113
113
|
}));
|
|
@@ -118,28 +118,28 @@ describe('deviceAccessGuard', () => {
|
|
|
118
118
|
// made that check decorative, since a reader refused at the socket could GET
|
|
119
119
|
// the identical bytes here.
|
|
120
120
|
it('blocks reading the logs of a device another user holds', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
121
|
-
const res = yield (0,
|
|
121
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB })).get(`/control/${UDID}/logs`);
|
|
122
122
|
(0, chai_1.expect)(res.status).to.equal(409);
|
|
123
123
|
(0, chai_1.expect)(res.body.error).to.equal('device_held_by_another_user');
|
|
124
124
|
(0, chai_1.expect)(JSON.stringify(res.body)).to.not.contain('SECRETLOGLINE');
|
|
125
125
|
}));
|
|
126
126
|
it('blocks the logs of a device running another user’s Appium session', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
-
const res = yield (0,
|
|
127
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, sessionId: 'appium-1', sessionOwner: ALICE })).get(`/control/${UDID}/logs`);
|
|
128
128
|
(0, chai_1.expect)(res.status).to.equal(409);
|
|
129
129
|
(0, chai_1.expect)(res.body.error).to.equal('device_in_use_by_session');
|
|
130
130
|
(0, chai_1.expect)(JSON.stringify(res.body)).to.not.contain('SECRETLOGLINE');
|
|
131
131
|
}));
|
|
132
132
|
it('lets the holder read their own logs', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
133
|
-
const res = yield (0,
|
|
133
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: ALICE })).get(`/control/${UDID}/logs`);
|
|
134
134
|
(0, chai_1.expect)(res.status, JSON.stringify(res.body)).to.equal(200);
|
|
135
135
|
(0, chai_1.expect)(res.body.logs[0]).to.contain('SECRETLOGLINE');
|
|
136
136
|
}));
|
|
137
137
|
it('lets an admin read the logs', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
138
|
-
const res = yield (0,
|
|
138
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, scopes: 'admin' })).get(`/control/${UDID}/logs`);
|
|
139
139
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
140
140
|
}));
|
|
141
141
|
it('allows the logs read when the device is free', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
142
|
-
const res = yield (0,
|
|
142
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, busy: false })).get(`/control/${UDID}/logs`);
|
|
143
143
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
144
144
|
}));
|
|
145
145
|
// Deliberately NOT extended to the other reads: screenshot and the MJPEG
|
|
@@ -147,39 +147,39 @@ describe('deviceAccessGuard', () => {
|
|
|
147
147
|
// decision for this guard was mutations-only. Clipboard and logs are the
|
|
148
148
|
// documented exceptions, so this pins the boundary.
|
|
149
149
|
it('does not extend the ownership check to other reads', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
150
|
-
const res = yield (0,
|
|
150
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB })).get(`/control/${UDID}/screenshot`);
|
|
151
151
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
152
152
|
}));
|
|
153
153
|
it('skips stream/start, stream/stop, stream/leave and stream/ticket', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
154
154
|
const app = appWith({ actorUserId: BOB });
|
|
155
155
|
for (const p of ['stream/start', 'stream/stop', 'stream/leave', 'stream/ticket']) {
|
|
156
|
-
const res = yield (0,
|
|
156
|
+
const res = yield (0, loopbackRequest_1.default)(app).post(`/control/${UDID}/${p}`);
|
|
157
157
|
(0, chai_1.expect)(res.status, p).to.equal(200);
|
|
158
158
|
}
|
|
159
159
|
}));
|
|
160
160
|
it('lets an admin-scoped key through', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
|
-
const res = yield (0,
|
|
161
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, scopes: 'admin' })).post(`/control/${UDID}/tap`);
|
|
162
162
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
163
163
|
}));
|
|
164
164
|
it('lets a SUPER_ADMIN through', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
165
|
-
const res = yield (0,
|
|
165
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, role: 'SUPER_ADMIN' })).post(`/control/${UDID}/tap`);
|
|
166
166
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
167
167
|
}));
|
|
168
168
|
it('does not treat a scope merely containing "admin" as admin', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
169
|
-
const res = yield (0,
|
|
169
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, scopes: 'nonadmin,devices' })).post(`/control/${UDID}/tap`);
|
|
170
170
|
(0, chai_1.expect)(res.status).to.equal(409);
|
|
171
171
|
}));
|
|
172
172
|
it('401s when there is no actor', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
173
|
-
const res = yield (0,
|
|
173
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({})).post(`/control/${UDID}/tap`);
|
|
174
174
|
(0, chai_1.expect)(res.status).to.equal(401);
|
|
175
175
|
}));
|
|
176
176
|
it('denies a foreign Appium session', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
177
|
-
const res = yield (0,
|
|
177
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB, sessionId: 'appium-1', sessionOwner: ALICE })).post(`/control/${UDID}/tap`);
|
|
178
178
|
(0, chai_1.expect)(res.status).to.equal(409);
|
|
179
179
|
(0, chai_1.expect)(res.body.error).to.equal('device_in_use_by_session');
|
|
180
180
|
}));
|
|
181
181
|
it('allows the owner of the running Appium session', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
182
|
-
const res = yield (0,
|
|
182
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: ALICE, sessionId: 'appium-1', sessionOwner: ALICE })).post(`/control/${UDID}/tap`);
|
|
183
183
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
184
184
|
}));
|
|
185
185
|
it('falls through to the handler when the device is unknown', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -198,7 +198,7 @@ describe('deviceAccessGuard', () => {
|
|
|
198
198
|
router.use((0, deviceAccessGuard_1.deviceAccessGuard)({ findDevice: () => __awaiter(void 0, void 0, void 0, function* () { return null; }) }));
|
|
199
199
|
router.post('/:udid/tap', (_req, res) => res.status(404).send('Device not found'));
|
|
200
200
|
app.use('/control', router);
|
|
201
|
-
const res = yield (0,
|
|
201
|
+
const res = yield (0, loopbackRequest_1.default)(app).post(`/control/${UDID}/tap`);
|
|
202
202
|
(0, chai_1.expect)(res.status).to.equal(404);
|
|
203
203
|
}));
|
|
204
204
|
it('responds instead of hanging on a malformed-percent udid', function () {
|
|
@@ -209,13 +209,13 @@ describe('deviceAccessGuard', () => {
|
|
|
209
209
|
// this test's timeout well under the file default so a regression fails
|
|
210
210
|
// fast instead of silently eating the full 60s.
|
|
211
211
|
this.timeout(5000);
|
|
212
|
-
const res = yield (0,
|
|
212
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: ALICE })).post('/control/100%/tap');
|
|
213
213
|
(0, chai_1.expect)(res.status).to.equal(400);
|
|
214
214
|
(0, chai_1.expect)(res.body.success).to.equal(false);
|
|
215
215
|
});
|
|
216
216
|
});
|
|
217
217
|
it('fails closed with 503 when the device lookup throws, never reaching the handler', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
218
|
-
const res = yield (0,
|
|
218
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({
|
|
219
219
|
actorUserId: ALICE,
|
|
220
220
|
findDeviceImpl: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
221
221
|
throw new Error('store hiccup');
|
|
@@ -230,7 +230,7 @@ describe('deviceAccessGuard', () => {
|
|
|
230
230
|
(0, chai_1.expect)(res.body.reached).to.equal(undefined);
|
|
231
231
|
}));
|
|
232
232
|
it('fails closed with 503 when resolveSessionOwner throws, never reaching the handler', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
233
|
-
const res = yield (0,
|
|
233
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({
|
|
234
234
|
actorUserId: BOB,
|
|
235
235
|
sessionId: 'appium-1',
|
|
236
236
|
resolveSessionOwnerImpl: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -242,7 +242,7 @@ describe('deviceAccessGuard', () => {
|
|
|
242
242
|
(0, chai_1.expect)(res.body.reached).to.equal(undefined);
|
|
243
243
|
}));
|
|
244
244
|
it('still denies with 409 (no holder name) when describeHolder throws', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
245
|
-
const res = yield (0,
|
|
245
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({
|
|
246
246
|
actorUserId: BOB,
|
|
247
247
|
describeHolderImpl: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
248
248
|
throw new Error('lookup hiccup');
|
|
@@ -256,8 +256,46 @@ describe('deviceAccessGuard', () => {
|
|
|
256
256
|
(0, chai_1.expect)(res.body.holder.name).to.equal(undefined);
|
|
257
257
|
}));
|
|
258
258
|
it('skips a mixed-case allowlisted action like Stream/Start', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
259
|
-
const res = yield (0,
|
|
259
|
+
const res = yield (0, loopbackRequest_1.default)(appWith({ actorUserId: BOB })).post(`/control/${UDID}/Stream/Start`);
|
|
260
260
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
261
261
|
(0, chai_1.expect)(res.body.reached).to.equal(true);
|
|
262
262
|
}));
|
|
263
263
|
});
|
|
264
|
+
/**
|
|
265
|
+
* On a hub, a node's phone is busy when its node reports it busy (nodeBusy):
|
|
266
|
+
* a preview the node holds, say. The hub has no holder on record for that:
|
|
267
|
+
* no session and no hold of its own. Failing closed on "busy, owner unknown"
|
|
268
|
+
* refused every non-admin, the preview's own holder included, so the hub
|
|
269
|
+
* leaves that decision to the node, whose own guard knows the holder and
|
|
270
|
+
* judges every call the hub forwards. What the hub holds itself (a claim, a
|
|
271
|
+
* session, a hold) it still judges.
|
|
272
|
+
*/
|
|
273
|
+
describe('deviceAccessGuard on a node’s phone', () => {
|
|
274
|
+
const nodePhone = (row) => appWith({ actorUserId: BOB, findDeviceImpl: () => __awaiter(void 0, void 0, void 0, function* () { return row; }), sessionOwner: ALICE });
|
|
275
|
+
it('leaves a phone busy only by its node’s report to the node', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
276
|
+
const app = nodePhone({ busy: true, nodeBusy: true, session_id: null });
|
|
277
|
+
const tap = yield (0, loopbackRequest_1.default)(app).post(`/control/${UDID}/tap`);
|
|
278
|
+
(0, chai_1.expect)(tap.status).to.equal(200);
|
|
279
|
+
(0, chai_1.expect)(tap.body.reached).to.equal(true);
|
|
280
|
+
const logs = yield (0, loopbackRequest_1.default)(app).get(`/control/${UDID}/logs`);
|
|
281
|
+
(0, chai_1.expect)(logs.status).to.equal(200);
|
|
282
|
+
}));
|
|
283
|
+
it('still judges a session this server routed to the node', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
284
|
+
const tap = yield (0, loopbackRequest_1.default)(nodePhone({
|
|
285
|
+
busy: true,
|
|
286
|
+
nodeBusy: true,
|
|
287
|
+
session_id: 'appium-1',
|
|
288
|
+
claimSessionId: 'appium-1',
|
|
289
|
+
})).post(`/control/${UDID}/tap`);
|
|
290
|
+
(0, chai_1.expect)(tap.status).to.equal(409);
|
|
291
|
+
(0, chai_1.expect)(tap.body.error).to.equal('device_in_use_by_session');
|
|
292
|
+
}));
|
|
293
|
+
it('still refuses while this server’s own create for the phone is pending', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
294
|
+
const tap = yield (0, loopbackRequest_1.default)(nodePhone({ busy: true, nodeBusy: true, session_id: null, claimedAt: 1790000000000 })).post(`/control/${UDID}/tap`);
|
|
295
|
+
(0, chai_1.expect)(tap.status).to.equal(409);
|
|
296
|
+
}));
|
|
297
|
+
it('still refuses this server’s own phone busy with no holder on record', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
298
|
+
const tap = yield (0, loopbackRequest_1.default)(nodePhone({ busy: true, session_id: null })).post(`/control/${UDID}/tap`);
|
|
299
|
+
(0, chai_1.expect)(tap.status).to.equal(409);
|
|
300
|
+
}));
|
|
301
|
+
});
|
|
@@ -25,7 +25,10 @@ const db_1 = require("../../src/data-service/db");
|
|
|
25
25
|
const chai_1 = require("chai");
|
|
26
26
|
const semver_1 = __importDefault(require("semver"));
|
|
27
27
|
const test_container_1 = require("../helpers/test-container");
|
|
28
|
+
const loki_stores_1 = require("../helpers/loki-stores");
|
|
28
29
|
describe('Get device', () => {
|
|
30
|
+
// The in-memory store, also when this file runs on its own.
|
|
31
|
+
(0, loki_stores_1.useLokiStores)();
|
|
29
32
|
before('Set devices in memory', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
30
33
|
var _a, e_1, _b, _c;
|
|
31
34
|
yield (0, test_container_1.resetTestContainer)();
|
|
@@ -146,6 +149,7 @@ describe('Get device', () => {
|
|
|
146
149
|
finally { if (e_1) throw e_1.error; }
|
|
147
150
|
}
|
|
148
151
|
}));
|
|
152
|
+
after(() => (0, test_container_1.resetTestContainer)());
|
|
149
153
|
it('Get android device based on filter with minSDK', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
150
154
|
const filterOptions = {
|
|
151
155
|
platform: 'android',
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
require("reflect-metadata");
|
|
13
|
+
const chai_1 = require("chai");
|
|
14
|
+
const device_store_1 = require("../../src/data-service/device-store");
|
|
15
|
+
/**
|
|
16
|
+
* clearStorage(onlyHosts) on the in-memory (Loki) store, the one the unit
|
|
17
|
+
* suite runs: a hub starting up forgets its own phones and keeps its nodes'.
|
|
18
|
+
* The Prisma store's side is in session-recovery-routing.spec.ts.
|
|
19
|
+
*/
|
|
20
|
+
describe('LokiDeviceStore.clearStorage(onlyHosts)', () => {
|
|
21
|
+
const HUB = 'http://clear-at-boot-hub:4724';
|
|
22
|
+
const NODE = 'http://clear-at-boot-hub:4725';
|
|
23
|
+
let savedStore;
|
|
24
|
+
let store;
|
|
25
|
+
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
savedStore = device_store_1.DeviceStoreFactory._deviceStore;
|
|
27
|
+
device_store_1.DeviceStoreFactory._deviceStore = undefined;
|
|
28
|
+
const env = process.env.NODE_ENV;
|
|
29
|
+
process.env.NODE_ENV = 'test';
|
|
30
|
+
store = device_store_1.DeviceStoreFactory.getStore();
|
|
31
|
+
process.env.NODE_ENV = env;
|
|
32
|
+
(0, chai_1.expect)(store.constructor.name).to.equal('LokiDeviceStore');
|
|
33
|
+
yield store.addDevices([
|
|
34
|
+
{ udid: 'clear-hub-phone', host: HUB, platform: 'android' },
|
|
35
|
+
{ udid: 'clear-node-phone', host: NODE, platform: 'android' },
|
|
36
|
+
]);
|
|
37
|
+
}));
|
|
38
|
+
afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
+
yield store.removeDevices({ udid: 'clear-hub-phone' });
|
|
40
|
+
yield store.removeDevices({ udid: 'clear-node-phone' });
|
|
41
|
+
device_store_1.DeviceStoreFactory._deviceStore = savedStore;
|
|
42
|
+
}));
|
|
43
|
+
it('deletes only the phones filed under the given hosts', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
44
|
+
yield store.clearStorage([HUB]);
|
|
45
|
+
(0, chai_1.expect)(yield store.findDevice({ udid: 'clear-hub-phone' })).to.equal(null);
|
|
46
|
+
(0, chai_1.expect)(yield store.findDevice({ udid: 'clear-node-phone' })).to.include({ host: NODE });
|
|
47
|
+
}));
|
|
48
|
+
});
|
|
@@ -21,10 +21,12 @@ const prisma_store_1 = require("../../src/data-service/prisma-store");
|
|
|
21
21
|
const device_store_1 = require("../../src/data-service/device-store");
|
|
22
22
|
const device_service_1 = require("../../src/data-service/device-service");
|
|
23
23
|
const device_utils_1 = require("../../src/device-utils");
|
|
24
|
+
const localDeviceHosts_1 = require("../../src/device-managers/localDeviceHosts");
|
|
24
25
|
const device_managers_1 = require("../../src/device-managers");
|
|
25
26
|
const SocketServer_1 = require("../../src/services/SocketServer");
|
|
26
27
|
const NotificationService_1 = require("../../src/services/NotificationService");
|
|
27
28
|
const container_registration_1 = require("../helpers/container-registration");
|
|
29
|
+
const fake_device_table_1 = require("../helpers/fake-device-table");
|
|
28
30
|
/**
|
|
29
31
|
* The periodic device sync reads every row, runs discovery (seconds of adb or
|
|
30
32
|
* simctl), then writes each phone back. A write that lands in between (a
|
|
@@ -38,81 +40,7 @@ const container_registration_1 = require("../helpers/container-registration");
|
|
|
38
40
|
*/
|
|
39
41
|
const HOST = 'http://127.0.0.1:4723';
|
|
40
42
|
const BIND = '127.0.0.1';
|
|
41
|
-
|
|
42
|
-
const rows = new Map();
|
|
43
|
-
const key = (udid, host) => `${udid}|${host}`;
|
|
44
|
-
const matches = (row, where) => {
|
|
45
|
-
if (!where)
|
|
46
|
-
return true;
|
|
47
|
-
return Object.entries(where).every(([field, cond]) => {
|
|
48
|
-
var _a;
|
|
49
|
-
if (field === 'OR')
|
|
50
|
-
return cond.some((w) => matches(row, w));
|
|
51
|
-
if (field === 'AND')
|
|
52
|
-
return cond.every((w) => matches(row, w));
|
|
53
|
-
if (cond && typeof cond === 'object' && 'in' in cond)
|
|
54
|
-
return cond.in.includes(row[field]);
|
|
55
|
-
if (cond && typeof cond === 'object' && 'contains' in cond)
|
|
56
|
-
return String((_a = row[field]) !== null && _a !== void 0 ? _a : '').includes(cond.contains);
|
|
57
|
-
return row[field] === cond;
|
|
58
|
-
});
|
|
59
|
-
};
|
|
60
|
-
const write = (row, data) => {
|
|
61
|
-
for (const [field, value] of Object.entries(data))
|
|
62
|
-
if (value !== undefined)
|
|
63
|
-
row[field] = value;
|
|
64
|
-
};
|
|
65
|
-
const defaults = () => ({
|
|
66
|
-
busy: false,
|
|
67
|
-
userBlocked: false,
|
|
68
|
-
offline: false,
|
|
69
|
-
session_id: null,
|
|
70
|
-
teamId: null,
|
|
71
|
-
sessionProgress: '',
|
|
72
|
-
sessionStartTime: 0,
|
|
73
|
-
totalUtilizationTimeMilliSec: 0,
|
|
74
|
-
});
|
|
75
|
-
for (const r of seed)
|
|
76
|
-
rows.set(key(r.udid, r.host), Object.assign(Object.assign({}, defaults()), r));
|
|
77
|
-
return {
|
|
78
|
-
row: (udid) => [...rows.values()].find((r) => r.udid === udid),
|
|
79
|
-
delegate: {
|
|
80
|
-
findMany: (...args_1) => __awaiter(this, [...args_1], void 0, function* (args = {}) { return [...rows.values()].filter((r) => matches(r, args.where)).map((r) => (Object.assign({}, r))); }),
|
|
81
|
-
findUnique: (_a) => __awaiter(this, [_a], void 0, function* ({ where }) {
|
|
82
|
-
const r = rows.get(key(where.udid_host.udid, where.udid_host.host));
|
|
83
|
-
return r ? Object.assign({}, r) : null;
|
|
84
|
-
}),
|
|
85
|
-
upsert: (_a) => __awaiter(this, [_a], void 0, function* ({ where, create, update, }) {
|
|
86
|
-
const k = key(where.udid_host.udid, where.udid_host.host);
|
|
87
|
-
const found = rows.get(k);
|
|
88
|
-
if (found) {
|
|
89
|
-
write(found, update);
|
|
90
|
-
return Object.assign({}, found);
|
|
91
|
-
}
|
|
92
|
-
const created = defaults();
|
|
93
|
-
write(created, create);
|
|
94
|
-
rows.set(k, created);
|
|
95
|
-
return Object.assign({}, created);
|
|
96
|
-
}),
|
|
97
|
-
updateMany: (_a) => __awaiter(this, [_a], void 0, function* ({ where, data }) {
|
|
98
|
-
let count = 0;
|
|
99
|
-
for (const r of rows.values()) {
|
|
100
|
-
if (matches(r, where)) {
|
|
101
|
-
write(r, data);
|
|
102
|
-
count++;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return { count };
|
|
106
|
-
}),
|
|
107
|
-
deleteMany: (_a) => __awaiter(this, [_a], void 0, function* ({ where }) {
|
|
108
|
-
for (const [k, r] of rows)
|
|
109
|
-
if (matches(r, where))
|
|
110
|
-
rows.delete(k);
|
|
111
|
-
return { count: 0 };
|
|
112
|
-
}),
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
}
|
|
43
|
+
const LOCAL = (0, localDeviceHosts_1.localDeviceHosts)({ bindHostOrIp: BIND }, 4723);
|
|
116
44
|
/** What AndroidDeviceManager returns for a phone it already knows (~158-166). */
|
|
117
45
|
const androidEcho = (known) => (Object.assign(Object.assign({}, known), { state: 'device', busy: known.busy || false }));
|
|
118
46
|
/** What IOSDiscoveryService.getSimulators returns for every simulator, known or not. */
|
|
@@ -136,7 +64,7 @@ describe('Device sync keeps writes made while it ran (lost update)', () => {
|
|
|
136
64
|
let restoreContainer;
|
|
137
65
|
let savedStore;
|
|
138
66
|
const seed = (rows) => {
|
|
139
|
-
table = fakeDeviceTable(rows);
|
|
67
|
+
table = (0, fake_device_table_1.fakeDeviceTable)(rows);
|
|
140
68
|
for (const m of Object.keys(table.delegate)) {
|
|
141
69
|
sinon_1.default.stub(prisma_1.prisma.device, m).callsFake(table.delegate[m]);
|
|
142
70
|
}
|
|
@@ -178,7 +106,7 @@ describe('Device sync keeps writes made while it ran (lost update)', () => {
|
|
|
178
106
|
yield (0, device_service_1.blockDevice)('s9', HOST, 'manual_user-1_s9');
|
|
179
107
|
return known.map(androidEcho);
|
|
180
108
|
});
|
|
181
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
109
|
+
yield (0, device_utils_1.updateDeviceList)(LOCAL);
|
|
182
110
|
(0, chai_1.expect)(table.row('s9')).to.include({ busy: true, session_id: 'manual_user-1_s9' });
|
|
183
111
|
}));
|
|
184
112
|
it('keeps a session lock taken while discovery ran, so the phone is not handed out twice', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -190,7 +118,7 @@ describe('Device sync keeps writes made while it ran (lost update)', () => {
|
|
|
190
118
|
(0, chai_1.expect)(locked === null || locked === void 0 ? void 0 : locked.udid).to.equal('s9');
|
|
191
119
|
return known.map(androidEcho);
|
|
192
120
|
});
|
|
193
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
121
|
+
yield (0, device_utils_1.updateDeviceList)(LOCAL);
|
|
194
122
|
(0, chai_1.expect)(table.row('s9')).to.include({ busy: true });
|
|
195
123
|
const second = yield device_store_1.DeviceStoreFactory.getStore().findAndLockDevice({
|
|
196
124
|
platform: 'android',
|
|
@@ -203,7 +131,7 @@ describe('Device sync keeps writes made while it ran (lost update)', () => {
|
|
|
203
131
|
yield device_store_1.DeviceStoreFactory.getStore().updateDevice('s9', HOST, { teamId: 'team-b' });
|
|
204
132
|
return known.map(androidEcho);
|
|
205
133
|
});
|
|
206
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
134
|
+
yield (0, device_utils_1.updateDeviceList)(LOCAL);
|
|
207
135
|
(0, chai_1.expect)(table.row('s9')).to.include({ teamId: 'team-b' });
|
|
208
136
|
}));
|
|
209
137
|
it("keeps a stream's port written while discovery ran", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -212,7 +140,7 @@ describe('Device sync keeps writes made while it ran (lost update)', () => {
|
|
|
212
140
|
yield device_store_1.DeviceStoreFactory.getStore().updateDevice('s9', HOST, { mjpegServerPort: 9200 });
|
|
213
141
|
return known.map(androidEcho);
|
|
214
142
|
});
|
|
215
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
143
|
+
yield (0, device_utils_1.updateDeviceList)(LOCAL);
|
|
216
144
|
(0, chai_1.expect)(table.row('s9')).to.include({ mjpegServerPort: 9200 });
|
|
217
145
|
}));
|
|
218
146
|
it('leaves a simulator in a session busy, though discovery always reports it free', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -232,7 +160,7 @@ describe('Device sync keeps writes made while it ran (lost update)', () => {
|
|
|
232
160
|
},
|
|
233
161
|
]);
|
|
234
162
|
discover = () => __awaiter(void 0, void 0, void 0, function* () { return [simulatorAsDiscovered()]; });
|
|
235
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
163
|
+
yield (0, device_utils_1.updateDeviceList)(LOCAL);
|
|
236
164
|
(0, chai_1.expect)(table.row('sim-1')).to.include({
|
|
237
165
|
busy: true,
|
|
238
166
|
session_id: 'sess-1',
|
|
@@ -268,7 +196,7 @@ describe('Device sync keeps writes made while it ran (lost update)', () => {
|
|
|
268
196
|
Object.assign(Object.assign({}, known.find((d) => d.udid === 'iphone')), { ip: '10.0.0.9' }),
|
|
269
197
|
];
|
|
270
198
|
});
|
|
271
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
199
|
+
yield (0, device_utils_1.updateDeviceList)(LOCAL);
|
|
272
200
|
(0, chai_1.expect)(table.row('sim-1')).to.include({ state: 'Booted', wdaLocalPort: 8101 });
|
|
273
201
|
(0, chai_1.expect)(table.row('iphone')).to.include({ ip: '10.0.0.9' });
|
|
274
202
|
}));
|
|
@@ -276,7 +204,7 @@ describe('Device sync keeps writes made while it ran (lost update)', () => {
|
|
|
276
204
|
seed([S9]);
|
|
277
205
|
const pixel = Object.assign(Object.assign({}, S9), { udid: 'pixel', name: 'Pixel 8' });
|
|
278
206
|
discover = (known) => __awaiter(void 0, void 0, void 0, function* () { return [...known.map(androidEcho), pixel]; });
|
|
279
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
207
|
+
yield (0, device_utils_1.updateDeviceList)(LOCAL);
|
|
280
208
|
(0, chai_1.expect)(emits.filter(([e]) => e === 'device_added').map(([, d]) => d.udid)).to.deep.equal([
|
|
281
209
|
'pixel',
|
|
282
210
|
]);
|
|
@@ -297,16 +225,16 @@ describe('Device sync keeps writes made while it ran (lost update)', () => {
|
|
|
297
225
|
});
|
|
298
226
|
(0, chai_1.expect)(emits.filter(([e]) => e === 'device_added')).to.have.length(0);
|
|
299
227
|
}));
|
|
300
|
-
it("the hub
|
|
228
|
+
it("the hub takes a node's report of its phone busy as the node's, not a session of its own", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
301
229
|
seed([Object.assign(Object.assign({}, S9), { host: 'http://10.0.0.2:4723' })]);
|
|
302
|
-
yield (0, device_service_1.addNewDevice)([Object.assign(Object.assign({}, S9), { host: 'http://10.0.0.2:4723', busy: true, session_id: 'node-sess' })], undefined, {
|
|
303
|
-
(0, chai_1.expect)(table.row('s9')).to.include({ busy: true, session_id:
|
|
230
|
+
yield (0, device_service_1.addNewDevice)([Object.assign(Object.assign({}, S9), { host: 'http://10.0.0.2:4723', busy: true, session_id: 'node-sess' })], undefined, { nodeReport: true });
|
|
231
|
+
(0, chai_1.expect)(table.row('s9')).to.include({ busy: true, nodeBusy: true, session_id: null });
|
|
304
232
|
(0, chai_1.expect)(emits.filter(([e]) => e === 'device_added')).to.have.length(0);
|
|
305
233
|
}));
|
|
306
234
|
});
|
|
307
235
|
describe('Device sync keeps writes made while it ran, on the Loki test store', () => {
|
|
308
236
|
const LOKI_HOST = 'http://loki-sync-test:4723';
|
|
309
|
-
const
|
|
237
|
+
const LOKI_LOCAL = (0, localDeviceHosts_1.localDeviceHosts)({ bindHostOrIp: 'loki-sync-test' }, 4723);
|
|
310
238
|
const UDIDS = ['loki-s9', 'loki-sim', 'loki-pixel'];
|
|
311
239
|
let emits;
|
|
312
240
|
let discover;
|
|
@@ -358,7 +286,7 @@ describe('Device sync keeps writes made while it ran, on the Loki test store', (
|
|
|
358
286
|
yield (0, device_service_1.blockDevice)('loki-s9', LOKI_HOST, 'manual_user-1_loki-s9');
|
|
359
287
|
return known.filter((d) => d.host === LOKI_HOST).map(androidEcho);
|
|
360
288
|
});
|
|
361
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
289
|
+
yield (0, device_utils_1.updateDeviceList)(LOKI_LOCAL);
|
|
362
290
|
(0, chai_1.expect)(yield row('loki-s9')).to.include({ busy: true, session_id: 'manual_user-1_loki-s9' });
|
|
363
291
|
}));
|
|
364
292
|
it('leaves a simulator in a session busy, and still takes its new state', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -371,7 +299,7 @@ describe('Device sync keeps writes made while it ran, on the Loki test store', (
|
|
|
371
299
|
simulatorAsDiscovered({ udid: 'loki-sim', host: LOKI_HOST, state: 'Booted' }),
|
|
372
300
|
];
|
|
373
301
|
});
|
|
374
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
302
|
+
yield (0, device_utils_1.updateDeviceList)(LOKI_LOCAL);
|
|
375
303
|
(0, chai_1.expect)(yield row('loki-sim')).to.include({
|
|
376
304
|
busy: true,
|
|
377
305
|
session_id: 'sess-1',
|