diff --git a/README.md b/README.md index 3eed4e0..3fdd505 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ For JSON respond, add `--json` to the command. ### create-project -Run `./cli.sh create-project --name {project name} --dut_type {dut type}` to create a new project. DUT Type must be one of `Controller, Accessory`. +Run `./cli.sh create-project --name {project name} --config {config file}` to create a new project. Project name is required. ### list-projects diff --git a/app/api_lib_autogen/models.py b/app/api_lib_autogen/models.py index c5a996a..adfe9d1 100644 --- a/app/api_lib_autogen/models.py +++ b/app/api_lib_autogen/models.py @@ -22,11 +22,7 @@ class BodyCreateTestRunExecutionApiV1TestRunExecutionsPost(BaseModel): test_run_execution_in: "TestRunExecutionCreate" = Field(..., alias="test_run_execution_in") - selected_tests: "Optional[Dict[str, Dict[str, Dict[str, int]]]]" = Field(None, alias="selected_tests") - - -# class BodyUploadFileApiV1TestRunExecutionsFileUploadPost(BaseModel): -# file: "IO[Any]" = Field(..., alias="file") + selected_tests: "SelectedTests" = Field(..., alias="selected_tests") class DutConfig(BaseModel): @@ -169,7 +165,6 @@ class TestRunExecution(BaseModel): class TestRunExecutionCreate(BaseModel): title: "str" = Field(..., alias="title") - test_run_config_id: "Optional[int]" = Field(None, alias="test_run_config_id") project_id: "Optional[int]" = Field(None, alias="project_id") description: "Optional[str]" = Field(None, alias="description") operator_id: "Optional[int]" = Field(None, alias="operator_id") @@ -311,3 +306,22 @@ class ValidationError(BaseModel): class WiFiConfig(BaseModel): ssid: "str" = Field(..., alias="ssid") password: "str" = Field(..., alias="password") + + +class SelectedTests(BaseModel): + collections: "list[SelectedCollection]" = Field(..., alias="collections") + + +class SelectedCollection(BaseModel): + public_id: "str" = Field(..., alias="public_id") + test_suites: "list[SelectedTestSuite]" = Field(..., alias="test_suites") + + +class SelectedTestSuite(BaseModel): + public_id: "str" = Field(..., alias="public_id") + test_cases: "list[SelectedTestCase]" = Field(..., alias="test_cases") + + +class SelectedTestCase(BaseModel): + public_id: "str" = Field(..., alias="public_id") + iterations: "int" = Field(..., alias="iterations") diff --git a/app/commands/project.py b/app/commands/project.py index a71009f..bd2b411 100644 --- a/app/commands/project.py +++ b/app/commands/project.py @@ -54,6 +54,7 @@ def create_project(name: str, config: Optional[str]) -> None: test_environment_config = TestEnvironmentConfig(**config_dict) projectCreate = ProjectCreate(name=name, config=test_environment_config) response = sync_apis.projects_api.create_project_api_v1_projects_post(project_create=projectCreate) + click.echo(f"Project {response.name} created with id {response.id}.") except json.JSONDecodeError as e: click.echo(f"Failed to parse JSON parameter: {e.msg}", err=True) raise Exit(code=1) @@ -66,9 +67,8 @@ def create_project(name: str, config: Optional[str]) -> None: except UnexpectedResponse as e: click.echo(f"Failed to create project {name}: {e.status_code} {e.content}", err=True) raise Exit(code=1) - - click.echo(f"Project {response.name} created with id {response.id}.") - client.close() + finally: + client.close() @click.command() @@ -82,11 +82,12 @@ def delete_project(id: int) -> None: """Delete a project""" try: sync_apis.projects_api.delete_project_api_v1_projects_id_delete(id=id) + click.echo(f"Project {id} is deleted.") except UnexpectedResponse as e: click.echo(f"Failed to delete project {id}: {e.status_code} {e.content}", err=True) raise Exit(code=1) - click.echo(f"Project {id} is deleted.") - client.close() + finally: + client.close() @click.command() @@ -172,20 +173,22 @@ def __print_project(project: dict) -> None: ) ) - if id is not None: - projects = __list_project_by_id(id) - else: - projects = __list_project_by_batch(archived, skip, limit) + try: + if id is not None: + projects = __list_project_by_id(id) + else: + projects = __list_project_by_batch(archived, skip, limit) - if projects is None or len(projects) == 0: - click.echo("Server did not return any project", err=True) - raise Exit(code=1) + if projects is None or (isinstance(projects, list) and len(projects) == 0): + click.echo("Server did not return any project", err=True) + raise Exit(code=1) - if json: - __print_json(projects) - else: - __print_table(projects) - client.close() + if json: + __print_json(projects) + else: + __print_table(projects) + finally: + client.close() @click.command() @@ -209,7 +212,6 @@ def update_project(id: int, config: str): projectUpdate = ProjectUpdate(**config_dict) response = sync_apis.projects_api.update_project_api_v1_projects_id_put(id=id, project_update=projectUpdate) click.echo(f"Project {response.name} is updated with the new config.") - client.close() except json.JSONDecodeError as e: click.echo(f"Failed to parse JSON parameter: {e.msg}", err=True) raise Exit(code=1) @@ -222,3 +224,5 @@ def update_project(id: int, config: str): except UnexpectedResponse as e: click.echo(f"Failed to update project {id}: {e.status_code} {e.content}", err=True) raise Exit(code=1) + finally: + client.close() diff --git a/app/commands/run_tests.py b/app/commands/run_tests.py index 354a8d9..5d8d09f 100644 --- a/app/commands/run_tests.py +++ b/app/commands/run_tests.py @@ -56,14 +56,18 @@ async def run_tests(selected_tests: str, title: str, file: str, project_id: int) selected_tests_dict = __parse_selected_tests(selected_tests, file) - new_test_run = await __create_new_test_run(selected_tests=selected_tests_dict, title=title, project_id=project_id) - socket = TestRunSocket(new_test_run) - socket_task = asyncio.create_task(socket.connect_websocket()) - new_test_run = await __start_test_run(new_test_run) - socket.run = new_test_run - await socket_task - await client.aclose() - click.echo(f"Log output in: '{log_path}'") + try: + new_test_run = await __create_new_test_run( + selected_tests=selected_tests_dict, title=title, project_id=project_id + ) + socket = TestRunSocket(new_test_run) + socket_task = asyncio.create_task(socket.connect_websocket()) + new_test_run = await __start_test_run(new_test_run) + socket.run = new_test_run + await socket_task + click.echo(f"Log output in: '{log_path}'") + finally: + await client.aclose() async def __create_new_test_run(selected_tests: dict, title: str, project_id: int) -> None: diff --git a/app/commands/test_run_execution_history.py b/app/commands/test_run_execution_history.py index 3d461f8..94b1b79 100644 --- a/app/commands/test_run_execution_history.py +++ b/app/commands/test_run_execution_history.py @@ -57,13 +57,15 @@ def test_run_execution_history( id: Optional[int], skip: Optional[int], limit: Optional[int], json: Optional[bool] ) -> None: """Read test run execution history""" - if id is not None: - __test_run_execution_by_id(id, json) - elif skip is not None or limit is not None: - __test_run_execution_batch(json, skip, limit) - else: - __test_run_execution_batch(json) - client.close() + try: + if id is not None: + __test_run_execution_by_id(id, json) + elif skip is not None or limit is not None: + __test_run_execution_batch(json, skip, limit) + else: + __test_run_execution_batch(json) + finally: + client.close() def __test_run_execution_by_id(id: int, json: bool) -> None: diff --git a/app/test_run/socket_schemas.py b/app/test_run/socket_schemas.py index 796657e..555c4ce 100644 --- a/app/test_run/socket_schemas.py +++ b/app/test_run/socket_schemas.py @@ -63,15 +63,15 @@ class TestRunUpdate(TestUpdateBase): class TestSuiteUpdate(TestUpdateBase): - test_suite_execution_id: int + test_suite_execution_index: int class TestCaseUpdate(TestSuiteUpdate): - test_case_execution_id: int + test_case_execution_index: int class TestStepUpdate(TestCaseUpdate): - test_step_execution_id: int + test_step_execution_index: int class TestUpdate(BaseModel): diff --git a/app/test_run/websocket.py b/app/test_run/websocket.py index 06aae3d..b6b63b5 100644 --- a/app/test_run/websocket.py +++ b/app/test_run/websocket.py @@ -104,20 +104,20 @@ def __log_test_run_update(self, update: TestRunUpdate) -> None: click.echo(f"Test Run [{str(update.state.name)}]") def __log_test_suite_update(self, update: TestSuiteUpdate) -> None: - suite = self.__suite(update.test_suite_execution_id) + suite = self.__suite(update.test_suite_execution_index) title = suite.test_suite_metadata.title click.echo(f" - {title} [{str(update.state.name)}]") def __log_test_case_update(self, update: TestCaseUpdate) -> None: - case = self.__case(id=update.test_case_execution_id, suite_id=update.test_suite_execution_id) + case = self.__case(index=update.test_case_execution_index, suite_index=update.test_suite_execution_index) title = case.test_case_metadata.title click.echo(f" - {title} [{str(update.state.name)}]") def __log_test_step_update(self, update: TestStepUpdate) -> None: step = self.__step( - id=update.test_step_execution_id, - case_id=update.test_case_execution_id, - suite_id=update.test_suite_execution_id, + index=update.test_step_execution_index, + case_index=update.test_case_execution_index, + suite_index=update.test_suite_execution_index, ) if step is not None: title = step.title @@ -127,13 +127,13 @@ def __handle_log_record(self, records: List[TestLogRecord]) -> None: for record in records: logger.log(record.level, record.message) - def __suite(self, id: int) -> TestSuiteExecution: - return next((s for s in self.run.test_suite_executions if s.id == id)) + def __suite(self, index: int) -> TestSuiteExecution: + return self.run.test_suite_executions[index] - def __case(self, id: int, suite_id: int) -> TestCaseExecution: - suite = self.__suite(suite_id) - return next((c for c in suite.test_case_executions if c.id == id)) + def __case(self, index: int, suite_index: int) -> TestCaseExecution: + suite = self.__suite(index=suite_index) + return suite.test_case_executions[index] - def __step(self, id: int, case_id: int, suite_id: int) -> Optional[TestStepExecution]: - case = self.__case(id=case_id, suite_id=suite_id) - return next((s for s in case.test_step_executions if s.id == id), None) + def __step(self, index: int, case_index: int, suite_index: int) -> Optional[TestStepExecution]: + case = self.__case(index=case_index, suite_index=suite_index) + return case.test_step_executions[index] diff --git a/openapi.json b/openapi.json index 7017b1a..f73f7d0 100644 --- a/openapi.json +++ b/openapi.json @@ -1399,7 +1399,8 @@ "Body_create_test_run_execution_api_v1_test_run_executions__post": { "title": "Body_create_test_run_execution_api_v1_test_run_executions__post", "required": [ - "test_run_execution_in" + "test_run_execution_in", + "selected_tests" ], "type": "object", "properties": { @@ -1407,20 +1408,71 @@ "$ref": "#/components/schemas/TestRunExecutionCreate" }, "selected_tests": { - "title": "Selected Tests", - "type": "object", - "additionalProperties": { - "type": "object", - "additionalProperties": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - } + "$ref": "#/components/schemas/SelectedTests" + } + } + }, + "SelectedTests": { + "title": "Selected Tests", + "type": "object", + "properties":{ + "collections": { + "title": "Collections", + "type": "array", + "items": { + "$ref": "#/components/schemas/SelectedCollection" } } } }, + "SelectedCollection": { + "title": "Selected Test Collection", + "type": "object", + "properties": { + "public_id": { + "title": "Collection Public ID", + "type": "string" + }, + "test_suites": { + "title": "Collection Test Suites", + "type": "array", + "items": { + "$ref": "#/components/schemas/SelectedSuite" + } + } + } + }, + "SelectedSuite": { + "title": "Selected Test Suite", + "type": "object", + "properties": { + "public_id": { + "title": "Test Suite Public ID", + "type": "string" + }, + "test_cases": { + "title": "Suite Test Cases", + "type": "array", + "items": { + "$ref": "#/components/schemas/SelectedCase" + } + } + } + }, + "SelectedCase": { + "title": "Selected Test Case", + "type": "object", + "properties": { + "public_id": { + "title": "Test Case Public ID", + "type": "string" + }, + "iterations": { + "title": "Iterations", + "type": "integer" + } + } + }, "Body_upload_file_api_v1_test_run_executions_file_upload__post": { "title": "Body_upload_file_api_v1_test_run_executions_file_upload__post", "required": [ @@ -1979,10 +2031,6 @@ "title": "Title", "type": "string" }, - "test_run_config_id": { - "title": "Test Run Config Id", - "type": "integer" - }, "project_id": { "title": "Project Id", "type": "integer"