44import re
55import sys
66from dataclasses import dataclass
7- from typing import Tuple
87
98import click
109from requests import HTTPError
1110
11+ from smart_tests .args4p import typer
1212from smart_tests .args4p .exceptions import BadCmdLineException
1313from smart_tests .utils .smart_tests_client import SmartTestsClient
1414from smart_tests .utils .tracking import Tracking
@@ -23,18 +23,58 @@ class TestSession:
2323 name : str | None = None
2424
2525
26- def get_session (session : str , client : SmartTestsClient ) -> TestSession :
27- build_name , test_session_id = parse_session (session )
28-
29- subpath = f"builds/{ build_name } /test_sessions/{ test_session_id } "
30- res = client .request ("get" , subpath )
26+ class SessionId :
27+ '''Represents the user-specific test session via the --session option.'''
28+
29+ def __init__ (self , id : str ):
30+ '''This is the method in which we parse the user input, so be defensive'''
31+ if id .startswith ('@' ):
32+ file_path = id [1 :]
33+ try :
34+ with open (file_path , 'r' ) as f :
35+ id = f .read ().strip ()
36+ except FileNotFoundError :
37+ raise BadCmdLineException (f"Session file '{ file_path } ' not found." )
38+ except IOError as e :
39+ raise BadCmdLineException (f"Error reading session file '{ file_path } ': { e } " )
40+
41+ match = re .match (r"builds/([^/]+)/test_sessions/(.+)" , id )
42+
43+ if match :
44+ self .id = id
45+ self .build_part = match .group (1 )
46+ self .test_part = int (match .group (2 ))
47+ else :
48+ raise BadCmdLineException (
49+ f"Invalid session ID. Expecting the output from 'smart-tests record session', but got '{ id } '" )
50+
51+ def __str__ (self ):
52+ return self .id
53+
54+ def subpath (self , endpoint : str ) -> str :
55+ return f"{ self .id } /{ endpoint } "
56+
57+ @staticmethod
58+ def as_option ():
59+ '''To promote consistency of the --session option across commands, use this to define an option.'''
60+ return typer .Option (
61+ "--session" ,
62+ help = "Session ID obtained by calling 'smart-tests record session'. It also accepts '@path/to/file' if the session ID is stored in a file " , # noqa E501
63+ required = True ,
64+ metavar = "SESSION" ,
65+ type = SessionId ,
66+ )
67+
68+
69+ def get_session (session : SessionId , client : SmartTestsClient ) -> TestSession :
70+ res = client .request ("get" , session .id )
3171
3272 try :
3373 res .raise_for_status ()
3474 except HTTPError as e :
3575 if e .response .status_code == 404 :
3676 # TODO(Konboi): move subset.print_error_and_die to util and use it
37- msg = f"Session { session } was not found. Make sure to run `smart-tests record session --build { build_name } ` before you run this command" # noqa E501
77+ msg = f"Session { session } was not found."
3878 click .secho (msg , fg = 'red' , err = True )
3979 if client .tracking_client :
4080 client .tracking_client .send_error_event (event_name = Tracking .ErrorEvent .USER_ERROR , stack_trace = msg )
@@ -50,24 +90,3 @@ def get_session(session: str, client: SmartTestsClient) -> TestSession:
5090 observation_mode = test_session .get ("isObservation" ),
5191 name = test_session .get ("name" ),
5292 )
53-
54-
55- def parse_session (session : str ) -> Tuple [str , int ]:
56- """Parse session to extract build name and test session id.
57-
58- Args:
59- session: Session in format "builds/{build_name}/test_sessions/{test_session_id}"
60-
61- Returns:
62- Tuple of (build_name, test_session_id)
63-
64- Raises:
65- ValueError: If session_id format is invalid
66- """
67- match = re .match (r"builds/([^/]+)/test_sessions/(.+)" , session )
68-
69- if match :
70- return match .group (1 ), int (match .group (2 ))
71- else :
72- raise BadCmdLineException (
73- f"Invalid session ID format: { session } . Expected format: builds/{{build_name}}/test_sessions/{{test_session_id}}" )
0 commit comments