-
Notifications
You must be signed in to change notification settings - Fork 8
[KOB-52290][KOB-52412] Fix 5 bugs in generated Python pytest template #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,33 +1,201 @@ | ||||||||||||||||||||||||||||||
| import socket | ||||||||||||||||||||||||||||||
| import threading | ||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||
| from http.server import HTTPServer, BaseHTTPRequestHandler | ||||||||||||||||||||||||||||||
| from urllib.request import urlopen, Request | ||||||||||||||||||||||||||||||
| import requests | ||||||||||||||||||||||||||||||
| from config import Config | ||||||||||||||||||||||||||||||
| from constants import DEVICE_SOURCES | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # 15-minute timeout (matching Java) | ||||||||||||||||||||||||||||||
| SOCKET_TIMEOUT_SECONDS = 15 * 60 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| class ProxyHandler(BaseHTTPRequestHandler): | ||||||||||||||||||||||||||||||
| current_command_id = 0 | ||||||||||||||||||||||||||||||
| server_instance = None | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def do_request(self, method, body=None): | ||||||||||||||||||||||||||||||
| target_url = Config.APPIUM_SERVER_URL.replace('/wd/hub', '') | ||||||||||||||||||||||||||||||
| url = f"{target_url}{self.path}" | ||||||||||||||||||||||||||||||
| force_w3c = False | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| base_url = Config.get_appium_server_url_with_auth() | ||||||||||||||||||||||||||||||
| print(f"[PROXY] Base URL from config: {base_url}", file=sys.stderr, flush=True) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Strip /wd/hub from self.path if present | ||||||||||||||||||||||||||||||
| path = self.path | ||||||||||||||||||||||||||||||
| if path.startswith('/wd/hub'): | ||||||||||||||||||||||||||||||
| path = path[len('/wd/hub'):] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| print(f"[PROXY] Incoming path: {self.path}", file=sys.stderr, flush=True) | ||||||||||||||||||||||||||||||
| print(f"[PROXY] Stripped path: {path}", file=sys.stderr, flush=True) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| url = f"{base_url.rstrip('/')}{path}" | ||||||||||||||||||||||||||||||
| print(f"[PROXY] Final URL being called: {url}", file=sys.stderr, flush=True) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if self.current_command_id: | ||||||||||||||||||||||||||||||
| # Get current_command_id from server instance | ||||||||||||||||||||||||||||||
| current_command_id = self.server_instance.current_command_id if self.server_instance else 0 | ||||||||||||||||||||||||||||||
| if Config.DEVICE_SOURCE == DEVICE_SOURCES['KOBITON'] and current_command_id > 0: | ||||||||||||||||||||||||||||||
| separator = '&' if '?' in url else '?' | ||||||||||||||||||||||||||||||
| url = f"{url}{separator}baseCommandId={self.current_command_id}" | ||||||||||||||||||||||||||||||
| url = f"{url}{separator}baseCommandId={current_command_id}" | ||||||||||||||||||||||||||||||
| print(f"[PROXY] URL with baseCommandId: {url}", file=sys.stderr, flush=True) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| headers = {key: val for key, val in self.headers.items()} | ||||||||||||||||||||||||||||||
| # Remove Host header to avoid conflicts | ||||||||||||||||||||||||||||||
| headers.pop('Host', None) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Add Authorization header (matching Java approach) | ||||||||||||||||||||||||||||||
| headers['Authorization'] = Config.get_basic_auth_string() | ||||||||||||||||||||||||||||||
| req = Request(url, data=body, headers=headers, method=method) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Log the Authorization header | ||||||||||||||||||||||||||||||
| auth_header = headers.get('Authorization', 'NOT SET') | ||||||||||||||||||||||||||||||
| masked_auth = "Basic ***" if auth_header.startswith("Basic ") else (auth_header if auth_header == "NOT SET" else "***") | ||||||||||||||||||||||||||||||
| print(f"[PROXY] Authorization header: {masked_auth}", file=sys.stderr, flush=True) | ||||||||||||||||||||||||||||||
| print(f"[PROXY] All request headers: {headers}", file=sys.stderr, flush=True) | ||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+52
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||
| with urlopen(req) as response: | ||||||||||||||||||||||||||||||
| self.send_response(response.status) | ||||||||||||||||||||||||||||||
| for key, val in response.headers.items(): | ||||||||||||||||||||||||||||||
| print(f"[PROXY] Making {method} request to: {url}", file=sys.stderr, flush=True) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Make the request using requests library with timeout (matching Java 15 min timeout) | ||||||||||||||||||||||||||||||
| if method == 'GET': | ||||||||||||||||||||||||||||||
| response = requests.get(url, headers=headers, verify=False, timeout=SOCKET_TIMEOUT_SECONDS) | ||||||||||||||||||||||||||||||
| elif method == 'POST': | ||||||||||||||||||||||||||||||
| response = requests.post(url, data=body, headers=headers, verify=False, timeout=SOCKET_TIMEOUT_SECONDS) | ||||||||||||||||||||||||||||||
| elif method == 'DELETE': | ||||||||||||||||||||||||||||||
| response = requests.delete(url, headers=headers, verify=False, timeout=SOCKET_TIMEOUT_SECONDS) | ||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| response = requests.request(method, url, data=body, headers=headers, verify=False, timeout=SOCKET_TIMEOUT_SECONDS) | ||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
+65
|
||||||||||||||||||||||||||||||
| response = requests.get(url, headers=headers, verify=False, timeout=SOCKET_TIMEOUT_SECONDS) | |
| elif method == 'POST': | |
| response = requests.post(url, data=body, headers=headers, verify=False, timeout=SOCKET_TIMEOUT_SECONDS) | |
| elif method == 'DELETE': | |
| response = requests.delete(url, headers=headers, verify=False, timeout=SOCKET_TIMEOUT_SECONDS) | |
| else: | |
| response = requests.request(method, url, data=body, headers=headers, verify=False, timeout=SOCKET_TIMEOUT_SECONDS) | |
| response = requests.get(url, headers=headers, timeout=SOCKET_TIMEOUT_SECONDS) | |
| elif method == 'POST': | |
| response = requests.post(url, data=body, headers=headers, timeout=SOCKET_TIMEOUT_SECONDS) | |
| elif method == 'DELETE': | |
| response = requests.delete(url, headers=headers, timeout=SOCKET_TIMEOUT_SECONDS) | |
| else: | |
| response = requests.request(method, url, data=body, headers=headers, timeout=SOCKET_TIMEOUT_SECONDS) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
| import base64 | ||
| import requests | ||
| from appium import webdriver | ||
| from appium.options import AppiumOptions | ||
| from appium.options.android import UiAutomator2Options | ||
| from appium.webdriver.common.appiumby import AppiumBy | ||
| from selenium.webdriver.support.ui import WebDriverWait | ||
| from selenium.webdriver.support import expected_conditions as EC | ||
|
|
@@ -51,7 +51,7 @@ def setup(self, desired_caps, retina_scale=1): | |
|
|
||
| print(f"Initialize Appium driver with desiredCaps: {desired_caps}") | ||
| server_url = self._proxy.get_server_url() + '/wd/hub' | ||
| options = AppiumOptions.load_capabilities(desired_caps) | ||
| options = UiAutomator2Options().load_capabilities(desired_caps) | ||
| self._driver = webdriver.Remote(server_url, options=options) | ||
|
Comment on lines
52
to
+55
|
||
|
|
||
| def cleanup(self): | ||
|
|
@@ -204,8 +204,12 @@ def find_online_device(self, capabilities): | |
| 'isBooked': False | ||
| } | ||
| ) | ||
| if response.status_code == 200 and response.json().get('deviceListData', []): | ||
| return | ||
| if response.status_code == 200: | ||
| data = response.json() or {} | ||
| device_keys = ('deviceListData', 'privateDevices', 'favoriteDevices', | ||
| 'cloudDevices', 'itaTrialCloudDevices', 'virtualDevices') | ||
| if any(data.get(k) for k in device_keys): | ||
| return | ||
| except Exception as e: | ||
| print(f"Error checking device availability: {e}") | ||
| print(f"Device not available, retrying ({attempt + 1}/{Config.DEVICE_WAITING_MAX_TRY_TIMES})...") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base_urlis built fromget_appium_server_url_with_auth()(which embedsusername:apiKey@...) and then printed. This leaks credentials into logs. Avoid embedding secrets in URLs where possible, and never log URLs/strings that contain credentials.