@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,255 @@
|
|
|
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
|
+
const chai_1 = require("chai");
|
|
16
|
+
const express_1 = __importDefault(require("express"));
|
|
17
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
18
|
+
const loopbackRequest_1 = __importDefault(require("../helpers/loopbackRequest"));
|
|
19
|
+
const base_driver_1 = require("@appium/base-driver");
|
|
20
|
+
const sessionCreate_1 = require("../../src/gateway/sessionCreate");
|
|
21
|
+
const createHandoff_1 = require("../../src/gateway/createHandoff");
|
|
22
|
+
const hubSessionToken_1 = require("../../src/gateway/hubSessionToken");
|
|
23
|
+
const loopbackServer_1 = require("../helpers/loopbackServer");
|
|
24
|
+
/**
|
|
25
|
+
* `POST <basePath>/session` in the session gateway: allocation happens here,
|
|
26
|
+
* before Appium's route. A remote phone's session is created on its node and
|
|
27
|
+
* answered here; a local phone's allocation goes on to the plugin through the
|
|
28
|
+
* request's async context, and is given back if nothing takes it.
|
|
29
|
+
*
|
|
30
|
+
* Appium's route is stood in for by a plain route, and the plugin by a
|
|
31
|
+
* function that takes the handoff, the way XenonPlugin.createSession does.
|
|
32
|
+
*/
|
|
33
|
+
describe('the session gateway’s create layer', () => {
|
|
34
|
+
const loopback = (0, loopbackServer_1.loopbackServers)();
|
|
35
|
+
const quiet = { info: () => undefined, warn: () => undefined, error: () => undefined };
|
|
36
|
+
let lifecycle;
|
|
37
|
+
let routeHits;
|
|
38
|
+
let pluginTakes;
|
|
39
|
+
let base;
|
|
40
|
+
const grant = { userId: 'alice', udid: 'phone-1', host: 'http://node:4725' };
|
|
41
|
+
const caps = () => ({
|
|
42
|
+
capabilities: { alwaysMatch: { platformName: 'Android' }, firstMatch: [{}] },
|
|
43
|
+
});
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
routeHits = [];
|
|
46
|
+
pluginTakes = true;
|
|
47
|
+
lifecycle = {
|
|
48
|
+
prepareSession: sinon_1.default.stub().resolves({ remote: false, device: { udid: 'phone-1' } }),
|
|
49
|
+
completeRemoteSession: sinon_1.default
|
|
50
|
+
.stub()
|
|
51
|
+
.resolves({ protocol: 'W3C', value: ['node-s1', { platformName: 'Android' }, 'W3C'] }),
|
|
52
|
+
releaseAllocation: sinon_1.default.stub().resolves(),
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
56
|
+
sinon_1.default.restore();
|
|
57
|
+
yield loopback.closeAll();
|
|
58
|
+
}));
|
|
59
|
+
function serve() {
|
|
60
|
+
return __awaiter(this, arguments, void 0, function* (opts = {}) {
|
|
61
|
+
var _a;
|
|
62
|
+
const basePath = (_a = opts.basePath) !== null && _a !== void 0 ? _a : '/wd/hub';
|
|
63
|
+
const app = (0, express_1.default)();
|
|
64
|
+
app.use(express_1.default.json());
|
|
65
|
+
app.use(`${basePath}/session`, (0, sessionCreate_1.createSessionCreateLayer)({
|
|
66
|
+
lifecycle: () => lifecycle,
|
|
67
|
+
hubGrants: opts.hubGrants,
|
|
68
|
+
authDisabled: () => !!opts.authDisabled,
|
|
69
|
+
logger: quiet,
|
|
70
|
+
}));
|
|
71
|
+
if (opts.beforeRoute)
|
|
72
|
+
app.use(opts.beforeRoute);
|
|
73
|
+
// Appium's route, with the plugin inside it.
|
|
74
|
+
app.post(`${basePath}/session`, (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
var _a;
|
|
76
|
+
const hit = { headers: Object.assign({}, req.headers), body: req.body };
|
|
77
|
+
routeHits.push(hit);
|
|
78
|
+
yield new Promise((resolve) => setTimeout(resolve, 1));
|
|
79
|
+
if (pluginTakes)
|
|
80
|
+
hit.took = (_a = (0, createHandoff_1.currentCreateHandoff)()) === null || _a === void 0 ? void 0 : _a.take();
|
|
81
|
+
res.json({ value: { sessionId: 'local-s1', capabilities: {} } });
|
|
82
|
+
}));
|
|
83
|
+
app.all('*', (_req, res) => res.status(404).json({ value: { error: 'unknown command' } }));
|
|
84
|
+
const server = yield loopback.serve(app);
|
|
85
|
+
base = `http://127.0.0.1:${server.address().port}`;
|
|
86
|
+
return base;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
describe('what it handles', () => {
|
|
90
|
+
it('only a POST of exactly the create path', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
91
|
+
yield serve();
|
|
92
|
+
yield (0, loopbackRequest_1.default)(base).get('/wd/hub/session').expect(404);
|
|
93
|
+
yield (0, loopbackRequest_1.default)(base).post('/wd/hub/session/abc/url').send({}).expect(404);
|
|
94
|
+
yield (0, loopbackRequest_1.default)(base).post('/wd/hub/sessionx').send(caps()).expect(404);
|
|
95
|
+
(0, chai_1.expect)(lifecycle.prepareSession.called).to.equal(false);
|
|
96
|
+
}));
|
|
97
|
+
it('a body without a capabilities object goes straight on, for Appium to answer', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
98
|
+
yield serve();
|
|
99
|
+
yield (0, loopbackRequest_1.default)(base).post('/wd/hub/session').send({ desiredCapabilities: {} }).expect(200);
|
|
100
|
+
yield (0, loopbackRequest_1.default)(base).post('/wd/hub/session').send({ capabilities: 'no' }).expect(200);
|
|
101
|
+
(0, chai_1.expect)(lifecycle.prepareSession.called).to.equal(false);
|
|
102
|
+
}));
|
|
103
|
+
it('matches the path as the router does: letter case, a trailing slash, any base path', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
+
yield serve({ basePath: '' });
|
|
105
|
+
yield (0, loopbackRequest_1.default)(base).post('/SESSION/').send(caps()).expect(200);
|
|
106
|
+
(0, chai_1.expect)(lifecycle.prepareSession.calledOnce).to.equal(true);
|
|
107
|
+
}));
|
|
108
|
+
it('fills in an omitted firstMatch, as the plugin used to', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
109
|
+
yield serve();
|
|
110
|
+
yield (0, loopbackRequest_1.default)(base)
|
|
111
|
+
.post('/wd/hub/session')
|
|
112
|
+
.send({ capabilities: { alwaysMatch: { platformName: 'Android' } } })
|
|
113
|
+
.expect(200);
|
|
114
|
+
(0, chai_1.expect)(lifecycle.prepareSession.firstCall.args[0].firstMatch).to.deep.equal([{}]);
|
|
115
|
+
(0, chai_1.expect)(routeHits[0].body.capabilities.firstMatch).to.deep.equal([{}]);
|
|
116
|
+
}));
|
|
117
|
+
});
|
|
118
|
+
describe('a remote phone', () => {
|
|
119
|
+
beforeEach(() => lifecycle.prepareSession.resolves({ remote: true }));
|
|
120
|
+
it('is created on its node and answered here, as Appium answers a new session', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
121
|
+
yield serve();
|
|
122
|
+
const res = yield (0, loopbackRequest_1.default)(base).post('/wd/hub/session').send(caps());
|
|
123
|
+
(0, chai_1.expect)(res.status).to.equal(200);
|
|
124
|
+
(0, chai_1.expect)(res.headers['content-type']).to.equal('application/json; charset=utf-8');
|
|
125
|
+
(0, chai_1.expect)(res.body).to.deep.equal({
|
|
126
|
+
value: { capabilities: { platformName: 'Android' }, sessionId: 'node-s1' },
|
|
127
|
+
});
|
|
128
|
+
(0, chai_1.expect)(routeHits, 'Appium’s route').to.deep.equal([]);
|
|
129
|
+
(0, chai_1.expect)(lifecycle.completeRemoteSession.calledOnce).to.equal(true);
|
|
130
|
+
}));
|
|
131
|
+
it('a failure is answered as Appium answers the error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
132
|
+
lifecycle.completeRemoteSession.rejects(new base_driver_1.errors.SessionNotCreatedError('node said no'));
|
|
133
|
+
yield serve();
|
|
134
|
+
const res = yield (0, loopbackRequest_1.default)(base).post('/wd/hub/session').send(caps());
|
|
135
|
+
(0, chai_1.expect)(res.status).to.equal(500);
|
|
136
|
+
(0, chai_1.expect)(res.body.value.error).to.equal('session not created');
|
|
137
|
+
(0, chai_1.expect)(res.body.value.message).to.include('node said no');
|
|
138
|
+
(0, chai_1.expect)(routeHits).to.deep.equal([]);
|
|
139
|
+
}));
|
|
140
|
+
});
|
|
141
|
+
describe('a refusal before any phone is allocated', () => {
|
|
142
|
+
it('is answered as Appium answers the error, and Appium never sees the request', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
143
|
+
lifecycle.prepareSession.rejects(new base_driver_1.errors.InvalidArgumentError('session rejected: no'));
|
|
144
|
+
yield serve();
|
|
145
|
+
const res = yield (0, loopbackRequest_1.default)(base).post('/wd/hub/session').send(caps());
|
|
146
|
+
(0, chai_1.expect)(res.status).to.equal(400);
|
|
147
|
+
(0, chai_1.expect)(res.body.value).to.include({ error: 'invalid argument' });
|
|
148
|
+
(0, chai_1.expect)(res.body.value.message).to.include('session rejected: no');
|
|
149
|
+
(0, chai_1.expect)(routeHits).to.deep.equal([]);
|
|
150
|
+
}));
|
|
151
|
+
it('a plain error is an unknown error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
152
|
+
lifecycle.prepareSession.rejects(new Error('No device matching request.'));
|
|
153
|
+
yield serve();
|
|
154
|
+
const res = yield (0, loopbackRequest_1.default)(base).post('/wd/hub/session').send(caps());
|
|
155
|
+
(0, chai_1.expect)(res.status).to.equal(500);
|
|
156
|
+
(0, chai_1.expect)(res.body.value.error).to.equal('unknown error');
|
|
157
|
+
}));
|
|
158
|
+
});
|
|
159
|
+
describe('a local phone', () => {
|
|
160
|
+
it('its allocation reaches the plugin through Appium’s route, and is not given back', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
|
+
yield serve();
|
|
162
|
+
yield (0, loopbackRequest_1.default)(base).post('/wd/hub/session').send(caps()).expect(200);
|
|
163
|
+
(0, chai_1.expect)(routeHits[0].took).to.deep.equal({ remote: false, device: { udid: 'phone-1' } });
|
|
164
|
+
(0, chai_1.expect)(lifecycle.prepareSession.calledOnce).to.equal(true);
|
|
165
|
+
(0, chai_1.expect)(lifecycle.releaseAllocation.called).to.equal(false);
|
|
166
|
+
}));
|
|
167
|
+
it('is given back once when Appium answers without the plugin taking it', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
168
|
+
pluginTakes = false;
|
|
169
|
+
yield serve();
|
|
170
|
+
yield (0, loopbackRequest_1.default)(base).post('/wd/hub/session').send(caps()).expect(200);
|
|
171
|
+
yield new Promise((resolve) => setImmediate(resolve));
|
|
172
|
+
(0, chai_1.expect)(lifecycle.releaseAllocation.callCount).to.equal(1);
|
|
173
|
+
(0, chai_1.expect)(lifecycle.releaseAllocation.firstCall.args[0].device.udid).to.equal('phone-1');
|
|
174
|
+
}));
|
|
175
|
+
it('is given back, and Appium never called, when the client left while it waited', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
176
|
+
let allocated;
|
|
177
|
+
lifecycle.prepareSession.callsFake(() => new Promise((resolve) => {
|
|
178
|
+
allocated = () => resolve({ remote: false, device: { udid: 'phone-1' } });
|
|
179
|
+
}));
|
|
180
|
+
yield serve();
|
|
181
|
+
const req = (0, loopbackRequest_1.default)(base).post('/wd/hub/session').send(caps()).timeout(50);
|
|
182
|
+
yield req.then(() => undefined, () => undefined);
|
|
183
|
+
// The server sees the connection close, then the phone turns up.
|
|
184
|
+
yield new Promise((resolve) => setTimeout(resolve, 20));
|
|
185
|
+
allocated();
|
|
186
|
+
yield new Promise((resolve) => setTimeout(resolve, 20));
|
|
187
|
+
(0, chai_1.expect)(lifecycle.releaseAllocation.callCount).to.equal(1);
|
|
188
|
+
(0, chai_1.expect)(routeHits).to.deep.equal([]);
|
|
189
|
+
}));
|
|
190
|
+
});
|
|
191
|
+
describe('on a node: the hub’s token', () => {
|
|
192
|
+
it('a valid one is the grant the node allocates by; the header goes no further', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
193
|
+
const verifyCreate = sinon_1.default.stub().resolves(grant);
|
|
194
|
+
yield serve({ hubGrants: { verifyCreate } });
|
|
195
|
+
yield (0, loopbackRequest_1.default)(base)
|
|
196
|
+
.post('/wd/hub/session')
|
|
197
|
+
.set(hubSessionToken_1.HUB_TOKEN_HEADER, 'good-token')
|
|
198
|
+
.send(caps())
|
|
199
|
+
.expect(200);
|
|
200
|
+
(0, chai_1.expect)(verifyCreate.calledOnceWith('good-token')).to.equal(true);
|
|
201
|
+
(0, chai_1.expect)(lifecycle.prepareSession.firstCall.args[1]).to.deep.equal({ hubGrant: grant });
|
|
202
|
+
(0, chai_1.expect)(routeHits[0].headers[hubSessionToken_1.HUB_TOKEN_HEADER]).to.equal(undefined);
|
|
203
|
+
}));
|
|
204
|
+
it('an invalid one is refused, and nothing is allocated', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
205
|
+
yield serve({ hubGrants: { verifyCreate: () => __awaiter(void 0, void 0, void 0, function* () { return null; }) } });
|
|
206
|
+
const res = yield (0, loopbackRequest_1.default)(base)
|
|
207
|
+
.post('/wd/hub/session')
|
|
208
|
+
.set(hubSessionToken_1.HUB_TOKEN_HEADER, 'forged')
|
|
209
|
+
.send(caps());
|
|
210
|
+
(0, chai_1.expect)(res.status).to.equal(400);
|
|
211
|
+
(0, chai_1.expect)(res.body.value.error).to.equal('invalid argument');
|
|
212
|
+
(0, chai_1.expect)(res.body.value.message).to.include('session rejected');
|
|
213
|
+
(0, chai_1.expect)(lifecycle.prepareSession.called).to.equal(false);
|
|
214
|
+
(0, chai_1.expect)(routeHits).to.deep.equal([]);
|
|
215
|
+
}));
|
|
216
|
+
it('one that cannot be checked is a 503, and nothing is allocated', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
217
|
+
yield serve({
|
|
218
|
+
hubGrants: {
|
|
219
|
+
verifyCreate: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
220
|
+
throw new hubSessionToken_1.HubTokenUnavailableError('hub down');
|
|
221
|
+
}),
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
const res = yield (0, loopbackRequest_1.default)(base)
|
|
225
|
+
.post('/wd/hub/session')
|
|
226
|
+
.set(hubSessionToken_1.HUB_TOKEN_HEADER, 'token')
|
|
227
|
+
.send(caps());
|
|
228
|
+
(0, chai_1.expect)(res.status).to.equal(503);
|
|
229
|
+
(0, chai_1.expect)(res.body.value.error).to.equal('unknown error');
|
|
230
|
+
(0, chai_1.expect)(lifecycle.prepareSession.called).to.equal(false);
|
|
231
|
+
}));
|
|
232
|
+
it('with auth disabled it is not checked, only removed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
233
|
+
const verifyCreate = sinon_1.default.stub().resolves(null);
|
|
234
|
+
yield serve({ hubGrants: { verifyCreate }, authDisabled: true });
|
|
235
|
+
yield (0, loopbackRequest_1.default)(base)
|
|
236
|
+
.post('/wd/hub/session')
|
|
237
|
+
.set(hubSessionToken_1.HUB_TOKEN_HEADER, 'anything')
|
|
238
|
+
.send(caps())
|
|
239
|
+
.expect(200);
|
|
240
|
+
(0, chai_1.expect)(verifyCreate.called).to.equal(false);
|
|
241
|
+
(0, chai_1.expect)(lifecycle.prepareSession.firstCall.args[1]).to.deep.equal({});
|
|
242
|
+
(0, chai_1.expect)(routeHits[0].headers[hubSessionToken_1.HUB_TOKEN_HEADER]).to.equal(undefined);
|
|
243
|
+
}));
|
|
244
|
+
it('a hub, which checks no hub tokens, ignores and removes one', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
245
|
+
yield serve();
|
|
246
|
+
yield (0, loopbackRequest_1.default)(base)
|
|
247
|
+
.post('/wd/hub/session')
|
|
248
|
+
.set(hubSessionToken_1.HUB_TOKEN_HEADER, 'anything')
|
|
249
|
+
.send(caps())
|
|
250
|
+
.expect(200);
|
|
251
|
+
(0, chai_1.expect)(lifecycle.prepareSession.firstCall.args[1]).to.deep.equal({});
|
|
252
|
+
(0, chai_1.expect)(routeHits[0].headers[hubSessionToken_1.HUB_TOKEN_HEADER]).to.equal(undefined);
|
|
253
|
+
}));
|
|
254
|
+
});
|
|
255
|
+
});
|
|
@@ -54,12 +54,15 @@ const PluginContext_1 = require("../../src/PluginContext");
|
|
|
54
54
|
const ApiKeyService_1 = require("../../src/services/ApiKeyService");
|
|
55
55
|
const UserService_1 = require("../../src/services/UserService");
|
|
56
56
|
const JwtKeyService_1 = require("../../src/services/token/JwtKeyService");
|
|
57
|
+
const hubSessionToken_1 = require("../../src/gateway/hubSessionToken");
|
|
57
58
|
const pendingSessions = __importStar(require("../../src/data-service/pending-sessions-service"));
|
|
58
59
|
const deviceService = __importStar(require("../../src/data-service/device-service"));
|
|
59
60
|
const deviceUtils = __importStar(require("../../src/device-utils"));
|
|
60
61
|
const IPluginArgs_1 = require("../../src/interfaces/IPluginArgs");
|
|
61
62
|
const config_1 = require("../../src/config");
|
|
63
|
+
const prisma_1 = require("../../src/prisma");
|
|
62
64
|
const container_registration_1 = require("../helpers/container-registration");
|
|
65
|
+
const loki_stores_1 = require("../helpers/loki-stores");
|
|
63
66
|
/**
|
|
64
67
|
* createSession end to end: the credentials a session presents in
|
|
65
68
|
* `xe:options` (or its alias `xenon:options`) are read once, then taken out of
|
|
@@ -85,6 +88,10 @@ const caps = (always, firstMatch = [{}]) => ({
|
|
|
85
88
|
});
|
|
86
89
|
const leaked = (value) => SECRETS.filter((s) => JSON.stringify(value !== null && value !== void 0 ? value : null).includes(s));
|
|
87
90
|
describe('createSession — credentials never reach the driver or storage', () => {
|
|
91
|
+
// The in-memory stores, also when this file runs on its own: without
|
|
92
|
+
// NODE_ENV=test the factory handed out the Prisma stores, which are the
|
|
93
|
+
// developer's ~/.cache/xenon/xenon.db.
|
|
94
|
+
(0, loki_stores_1.useLokiStores)();
|
|
88
95
|
let svc;
|
|
89
96
|
let device;
|
|
90
97
|
let context;
|
|
@@ -125,7 +132,7 @@ describe('createSession — credentials never reach the driver or storage', () =
|
|
|
125
132
|
context.pluginArgs = Object.assign({}, IPluginArgs_1.DefaultPluginArgs, { bindHostOrIp: '127.0.0.1' });
|
|
126
133
|
context.nodeId = 'node-hub';
|
|
127
134
|
device = { udid: 'u1', host: 'h1', platform: 'android', nodeId: 'node-hub', teamId: null };
|
|
128
|
-
restore = (0, container_registration_1.saveRegistrations)(ApiKeyService_1.ApiKeyService, UserService_1.UserService, JwtKeyService_1.JwtKeyService);
|
|
135
|
+
restore = (0, container_registration_1.saveRegistrations)(ApiKeyService_1.ApiKeyService, UserService_1.UserService, JwtKeyService_1.JwtKeyService, hubSessionToken_1.HubSessionTokenIssuer);
|
|
129
136
|
typedi_1.Container.set(ApiKeyService_1.ApiKeyService, {
|
|
130
137
|
verifyPair: sinon_1.default
|
|
131
138
|
.stub()
|
|
@@ -142,6 +149,8 @@ describe('createSession — credentials never reach the driver or storage', () =
|
|
|
142
149
|
return { sub: t.slice('jwt:'.length), teamId: null };
|
|
143
150
|
}),
|
|
144
151
|
});
|
|
152
|
+
// The owner's teams: none, and not read from the developer's database.
|
|
153
|
+
sinon_1.default.stub(prisma_1.prisma.teamMember, 'findMany').resolves([]);
|
|
145
154
|
seen = {};
|
|
146
155
|
sinon_1.default.stub(pendingSessions, 'addNewPendingSession').callsFake((c) => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
156
|
seen.pending = JSON.parse(JSON.stringify(c));
|
|
@@ -246,32 +255,39 @@ describe('createSession — credentials never reach the driver or storage', () =
|
|
|
246
255
|
}));
|
|
247
256
|
});
|
|
248
257
|
describe('when the device is on another node', () => {
|
|
258
|
+
let grants;
|
|
249
259
|
beforeEach(() => {
|
|
250
260
|
sinon_1.default.stub(svc, 'finalizeSession').callsFake((...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
251
261
|
seen.sessionResponse = { desired: JSON.parse(JSON.stringify(args[2])) };
|
|
252
262
|
}));
|
|
253
|
-
sinon_1.default.stub(svc, 'forwardSessionRequest').callsFake((_d, c) => __awaiter(void 0, void 0, void 0, function* () {
|
|
263
|
+
sinon_1.default.stub(svc, 'forwardSessionRequest').callsFake((_d, c, t) => __awaiter(void 0, void 0, void 0, function* () {
|
|
254
264
|
seen.forwarded = JSON.parse(JSON.stringify(c));
|
|
265
|
+
seen.hubToken = t;
|
|
255
266
|
return { protocol: 'W3C', value: ['sess-1', {}, 'W3C'] };
|
|
256
267
|
}));
|
|
268
|
+
grants = sinon_1.default.stub().resolves('HUB-CREATE-TOKEN');
|
|
269
|
+
typedi_1.Container.set(hubSessionToken_1.HubSessionTokenIssuer, {
|
|
270
|
+
createTokenFor: grants,
|
|
271
|
+
forget: () => undefined,
|
|
272
|
+
});
|
|
257
273
|
});
|
|
258
|
-
it('a peer Xenon node gets them
|
|
274
|
+
it('a peer Xenon node never gets them: it gets the hub’s token for the verified owner', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
259
275
|
device.nodeId = 'node-peer';
|
|
260
276
|
yield create(caps({ 'xe:options': { accessKey: KEY, token: TOKEN, sessionToken: JWT } }));
|
|
261
|
-
(0, chai_1.expect)(seen.forwarded
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
sessionToken: JWT,
|
|
265
|
-
});
|
|
277
|
+
(0, chai_1.expect)(leaked(seen.forwarded), 'forwarded').to.deep.equal([]);
|
|
278
|
+
(0, chai_1.expect)(seen.hubToken).to.equal('HUB-CREATE-TOKEN');
|
|
279
|
+
(0, chai_1.expect)(grants.firstCall.args[0]).to.deep.equal({ userId: 'usr_key', udid: 'u1', host: 'h1' });
|
|
266
280
|
for (const where of ['pending', 'sent', 'sessionResponse']) {
|
|
267
281
|
(0, chai_1.expect)(leaked(seen[where]), where).to.deep.equal([]);
|
|
268
282
|
}
|
|
269
283
|
}));
|
|
270
|
-
it('a cloud provider
|
|
284
|
+
it('a cloud provider gets neither', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
271
285
|
device.nodeId = undefined;
|
|
272
286
|
device.cloud = true;
|
|
273
287
|
yield create(caps({ 'xe:options': { accessKey: KEY, token: TOKEN, sessionToken: JWT } }));
|
|
274
288
|
(0, chai_1.expect)(leaked(seen.forwarded)).to.deep.equal([]);
|
|
289
|
+
(0, chai_1.expect)(seen.hubToken).to.equal(null);
|
|
290
|
+
(0, chai_1.expect)(grants.called).to.equal(false);
|
|
275
291
|
}));
|
|
276
292
|
});
|
|
277
293
|
});
|
|
@@ -100,50 +100,3 @@ describe('takeSessionCredentials', () => {
|
|
|
100
100
|
(0, chai_1.expect)(() => (0, sessionCredentials_1.takeSessionCredentials)({ alwaysMatch: null, firstMatch: 'x' })).to.not.throw();
|
|
101
101
|
});
|
|
102
102
|
});
|
|
103
|
-
describe('capsWithCredentials — the copy a peer Xenon node gets', () => {
|
|
104
|
-
// A peer node re-runs createSession: it authenticates and attributes the
|
|
105
|
-
// session and checks the lease itself, so it needs what the hub read. It
|
|
106
|
-
// takes the credentials out again before its own driver sees them.
|
|
107
|
-
const take = (caps) => ({ caps, credentials: (0, sessionCredentials_1.takeSessionCredentials)(caps) });
|
|
108
|
-
it('puts every credential back, so the node reads the same ones the hub did', () => {
|
|
109
|
-
const { caps, credentials } = take({
|
|
110
|
-
alwaysMatch: {
|
|
111
|
-
'xenon:options': { leaseId: 'lse_1', accessKey: 'ak_old', token: 'tk_old' },
|
|
112
|
-
'xe:options': { token: SECRETS.token, sessionToken: SECRETS.sessionToken },
|
|
113
|
-
},
|
|
114
|
-
firstMatch: [{}],
|
|
115
|
-
});
|
|
116
|
-
const forwarded = (0, sessionCredentials_1.capsWithCredentials)(caps, credentials);
|
|
117
|
-
(0, chai_1.expect)((0, sessionCredentials_1.takeSessionCredentials)(JSON.parse(JSON.stringify(forwarded)))).to.deep.equal(credentials);
|
|
118
|
-
});
|
|
119
|
-
it('writes them into the namespace that wins, in the bucket it is in', () => {
|
|
120
|
-
const { caps, credentials } = take({
|
|
121
|
-
alwaysMatch: { 'xenon:options': { leaseId: 'lse_1', leaseToken: SECRETS.leaseToken } },
|
|
122
|
-
firstMatch: [{ 'xe:options': { accessKey: SECRETS.accessKey, token: SECRETS.token } }],
|
|
123
|
-
});
|
|
124
|
-
const forwarded = (0, sessionCredentials_1.capsWithCredentials)(caps, credentials);
|
|
125
|
-
(0, chai_1.expect)(forwarded.firstMatch[0]['xe:options']).to.deep.equal({
|
|
126
|
-
accessKey: SECRETS.accessKey,
|
|
127
|
-
token: SECRETS.token,
|
|
128
|
-
leaseToken: SECRETS.leaseToken,
|
|
129
|
-
});
|
|
130
|
-
(0, chai_1.expect)(forwarded.alwaysMatch['xenon:options']).to.deep.equal({ leaseId: 'lse_1' });
|
|
131
|
-
});
|
|
132
|
-
it('does not modify the caps it copies', () => {
|
|
133
|
-
const { caps, credentials } = take({
|
|
134
|
-
alwaysMatch: { 'xe:options': Object.assign({ leaseId: 'lse_1' }, SECRETS) },
|
|
135
|
-
firstMatch: [{}],
|
|
136
|
-
});
|
|
137
|
-
const before = JSON.stringify(caps);
|
|
138
|
-
(0, sessionCredentials_1.capsWithCredentials)(caps, credentials);
|
|
139
|
-
(0, chai_1.expect)(JSON.stringify(caps)).to.equal(before);
|
|
140
|
-
(0, chai_1.expect)(leaks(caps)).to.deep.equal([]);
|
|
141
|
-
});
|
|
142
|
-
it('returns the caps unchanged when there is nothing to put back', () => {
|
|
143
|
-
const { caps, credentials } = take({
|
|
144
|
-
alwaysMatch: { 'xe:options': { leaseId: 'lse_1' } },
|
|
145
|
-
firstMatch: [{}],
|
|
146
|
-
});
|
|
147
|
-
(0, chai_1.expect)((0, sessionCredentials_1.capsWithCredentials)(caps, credentials)).to.equal(caps);
|
|
148
|
-
});
|
|
149
|
-
});
|