diff --git a/.changes/unreleased/added-20251209-141353.yaml b/.changes/unreleased/added-20251209-141353.yaml new file mode 100644 index 00000000..08f63c2c --- /dev/null +++ b/.changes/unreleased/added-20251209-141353.yaml @@ -0,0 +1,3 @@ +kind: added +body: Include API response data in the output. +time: 2025-12-09T14:13:53.030608891Z diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_capacity.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_capacity.py index 4d6ea0c5..f2f8a4a6 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_capacity.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_capacity.py @@ -71,7 +71,7 @@ def exec(capacity: VirtualWorkspaceItem, args: Namespace) -> None: response = capacity_api.create_capacity(args, payload=json_payload) if response.status_code in (200, 201): - utils_ui.print_output_format(args, message=f"'{capacity.name}' created") + utils_ui.print_output_format(args, message=f"'{capacity.name}' created", data=json.loads(response.text), show_headers=True) # In here we use a different approach since the id responded by the API is not the same as the id we use in the code # The id in the response is the fully qualified azure resource ID for the resource diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_connection.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_connection.py index fd28d23a..78bf2614 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_connection.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_connection.py @@ -125,9 +125,9 @@ def exec(connection: VirtualWorkspaceItem, args: Namespace) -> None: response = connection_api.create_connection(args, payload=json_payload) if response.status_code in (200, 201): - utils_ui.print_output_format(args, message=f"'{connection.name}' created") - data = json.loads(response.text) + utils_ui.print_output_format(args, message=f"'{connection.name}' created", data=data, show_headers=True) + connection._id = data["id"] # Add to mem_store diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_domain.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_domain.py index 8fbc832f..78babe62 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_domain.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_domain.py @@ -49,9 +49,8 @@ def exec(domain: VirtualWorkspaceItem, args: Namespace) -> None: response = domain_api.create_domain(args, payload=json_payload) if response.status_code in (200, 201): - utils_ui.print_output_format(args, message=f"'{domain.name}' created") - data = json.loads(response.text) + utils_ui.print_output_format(args, message=f"'{domain.name}' created", data=data, show_headers=True) domain._id = data["id"] diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_externaldatashare.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_externaldatashare.py index 2dbc8f15..63788dc3 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_externaldatashare.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_externaldatashare.py @@ -71,9 +71,7 @@ def exec(external_data_share: VirtualItem, args: Namespace) -> None: item.name, external_data_share.id ) - utils_ui.print_output_format( - args, message=f"'{external_data_share.name}' created" - ) + utils_ui.print_output_format(args, message=f"'{external_data_share.name}' created", data=data, show_headers=True) # Add to mem_store utils_mem_store.upsert_external_data_share_to_cache(external_data_share, item) diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_folder.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_folder.py index c2eca535..336d5978 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_folder.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_folder.py @@ -36,8 +36,8 @@ def exec(folder: Folder, args: Namespace) -> str | None: response = folder_api.create_folder(args, json_payload) if response.status_code in (200, 201): - utils_ui.print_output_format(args, message=f"'{folder.name}' created") data = json.loads(response.text) + utils_ui.print_output_format(args, message=f"'{folder.name}' created", data=data, show_headers=True) if data is not None and data.get("id"): _folder_id = data["id"] folder._id = _folder_id diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_gateway.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_gateway.py index 502e2283..1354a447 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_gateway.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_gateway.py @@ -95,9 +95,8 @@ def exec(gateway: VirtualWorkspaceItem, args: Namespace) -> None: response = gateway_api.create_gateway(args, payload=json.dumps(payload)) if response.status_code in (200, 201): - utils_ui.print_output_format(args, message=f"'{gateway.name}' created") - data = json.loads(response.text) + utils_ui.print_output_format(args, message=f"'{gateway.name}' created", data=data, show_headers=True) gateway._id = data["id"] # Add to mem_store diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_item.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_item.py index 0d3490cf..a64778a4 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_item.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_item.py @@ -16,6 +16,37 @@ def exec(item: Item, args: Namespace) -> str | None: + """ + Execute the creation of a Microsoft Fabric item. + + This method supports items that may require creating additional dependent + artifacts (e.g., a Report that implicitly creates a SemanticModel). + Output behavior differs depending on whether the current call represents the + user‑requested creation or an internally triggered dependency. + + Two execution modes: + - **Root operation** (`is_root_operation=True`): + Represents the item explicitly requested by the user. + Handles creation of the item and any required dependencies. + Collects and returns a consolidated output for all created artifacts. + + - **Dependency operation** (`is_root_operation=False`): + Represents an item created implicitly as part of another item's dependency chain. + Runs silently and contributes its result to the root operation’s batch output, + without producing standalone output. + + Args: + item (Item): The Fabric item to be created. + args (Namespace): Command arguments. May include `output_batch` used to + accumulate results during dependency operations. + + Returns: + str | None: The created item ID for root operations, or None for dependency + operations or failed creations. + """ + # Determine if this is part of a batch operation + is_root_operation = not hasattr(args, 'output_batch') + # Params params = args.params required_params, optional_params = mkdir_utils.get_params_per_item_type(item) @@ -58,8 +89,30 @@ def exec(item: Item, args: Namespace) -> str | None: response = item_api.create_item(args, json_payload, item_uri=True) if response.status_code in (200, 201): - utils_ui.print_output_format(args, message=f"'{item.name}' created") data = json.loads(response.text) + + if hasattr(args, 'output_batch'): + # Collect operation data for batch output + args.output_batch['items'].append(data) + args.output_batch['names'].append(item.name) + + # Only print consolidated output at the end of root operation + if is_root_operation: + names = args.output_batch['names'] + names_list = f"'{names[0]}' and '{names[1]}'" if len(names) == 2 else "'" + "', '".join(names[:-1]) + f"' and '{names[-1]}'" + + utils_ui.print_output_format( + args, + message=f"{names_list} created", + data=args.output_batch['items'], + show_headers=True + ) + # Clean up + delattr(args, 'output_batch') + else: + # Standard single item output for non-batched scenarios + utils_ui.print_output_format(args, message=f"'{item.name}' created", data=data, show_headers=True) + if data is not None and data.get("id"): _item_id = data["id"] item._id = _item_id diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_managedidentity.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_managedidentity.py index bbf7f294..e4286fea 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_managedidentity.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_managedidentity.py @@ -25,10 +25,10 @@ def exec(managed_identity: VirtualItem, args: Namespace) -> None: utils_ui.print_grey(f"Creating a new Managed Identity...") response = managed_identity_api.provision_managed_identity(args) if response.status_code in (200, 201): - utils_ui.print_output_format(args, message=f"'{managed_identity.name}' created") data = json.loads(response.text) managed_identity._id = data["servicePrincipalId"] # Add to mem_store utils_mem_store.upsert_managed_identity_to_cache(managed_identity) + utils_ui.print_output_format(args, message=f"'{managed_identity.name}' created", data=data, show_headers=True) diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_managedprivateendpoint.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_managedprivateendpoint.py index 890e253d..9c2534ad 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_managedprivateendpoint.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_managedprivateendpoint.py @@ -128,4 +128,4 @@ def exec(managed_private_endpoint: VirtualItem, args: Namespace) -> None: fab_constant.ERROR_OPERATION_FAILED, ) - utils_ui.print_output_format(args, message=result_message) + utils_ui.print_output_format(args, message=result_message, data=data, show_headers=True) diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_sparkpool.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_sparkpool.py index 15ad0b44..a58bb67d 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_sparkpool.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_sparkpool.py @@ -83,10 +83,9 @@ def exec(spark_pool: VirtualItem, args: Namespace) -> None: utils_ui.print_grey(f"Creating a new Spark Pool...") response = sparkpool_api.create_spark_pool(args, payload=json_payload) if response.status_code in (200, 201): - utils_ui.print_output_format(args, message=f"'{spark_pool.name}' created") - data = json.loads(response.text) spark_pool._id = data["id"] # Add to mem_store utils_mem_store.upsert_spark_pool_to_cache(spark_pool) + utils_ui.print_output_format(args, message=f"'{spark_pool.name}' created", data=data, show_headers=True) diff --git a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_workspace.py b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_workspace.py index a389505c..7caa56db 100644 --- a/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_workspace.py +++ b/src/fabric_cli/commands/fs/mkdir/fab_fs_mkdir_workspace.py @@ -76,9 +76,10 @@ def exec(workspace: Workspace, args: Namespace) -> None: response = workspace_api.create_workspace(args, json_payload) if response.status_code in (200, 201): - utils_ui.print_output_format(args, message=f"'{workspace.name}' created") data = json.loads(response.text) workspace._id = data["id"] - # Add to mem_store utils_mem_store.upsert_workspace_to_cache(workspace) + + utils_ui.print_output_format(args, message=f"'{workspace.name}' created", data=data, show_headers=True) + diff --git a/src/fabric_cli/utils/fab_cmd_mkdir_utils.py b/src/fabric_cli/utils/fab_cmd_mkdir_utils.py index 04d21ad3..97c06de9 100644 --- a/src/fabric_cli/utils/fab_cmd_mkdir_utils.py +++ b/src/fabric_cli/utils/fab_cmd_mkdir_utils.py @@ -73,6 +73,8 @@ def add_type_specific_payload(item: Item, args, payload): "EventHouse not provided in params. Creating one first" ) + _initialize_batch_collection_for_dependency_creation(args) + # Create a new Event House first _eventhouse = Item( f"{item.short_name}_auto", @@ -128,6 +130,8 @@ def add_type_specific_payload(item: Item, args, payload): "Semantic Model not provided in params. Creating one first" ) + _initialize_batch_collection_for_dependency_creation(args) + # Create a new Semantic Model first _semantic_model = Item( f"{item.short_name}_auto", @@ -754,3 +758,28 @@ def find_mpe_connection(managed_private_endpoint, targetprivatelinkresourceid): return conn return None + +def _initialize_batch_collection_for_dependency_creation(args): + """Initialize batch collection for scenarios where dependent items need to be created automatically. + + This method is used when creating items that have dependencies that don't exist yet, such as: + - Creating a KQL Database without an EventHouse (auto-creates EventHouse first) + - Creating a Report without a Semantic Model (auto-creates Semantic Model first) + + The batch collection allows multiple related items to be created in sequence and then + display a consolidated output message showing all items that were created together. + + Args: + args (Namespace): The command arguments namespace that will be augmented with + 'output_batch' attribute containing 'items' and 'names' lists + to collect creation results. + + Note: + This method only initializes the batch collection if it doesn't already exist, + ensuring it's safe to call multiple times during a dependency creation chain. + """ + if not hasattr(args, 'output_batch'): + args.output_batch = { + 'items': [], + 'names': [] + } \ No newline at end of file diff --git a/src/fabric_cli/utils/fab_ui.py b/src/fabric_cli/utils/fab_ui.py index c46843aa..708f7507 100644 --- a/src/fabric_cli/utils/fab_ui.py +++ b/src/fabric_cli/utils/fab_ui.py @@ -144,7 +144,7 @@ def print_done(text: str, to_stderr: bool = False) -> None: # Escape the text to avoid HTML injection and parsing issues escaped_text = html.escape(text) _safe_print_formatted_text( - f"* {escaped_text}", escaped_text, to_stderr + f"\n* {escaped_text}", escaped_text, to_stderr ) @@ -368,9 +368,9 @@ def _print_output_format_result_text(output: FabricCLIOutput) -> None: print_grey("------------------------------") _print_raw_data(output_result.hidden_data) + if output_result.message: - print_done(output_result.message) - + print_done(f"{output_result.message}\n") def _print_raw_data(data: list[Any], to_stderr: bool = False) -> None: """ diff --git a/tests/test_commands/recordings/test_commands/test_mkdir/class_setup.yaml b/tests/test_commands/recordings/test_commands/test_mkdir/class_setup.yaml index a6218da0..1ff92c9e 100644 --- a/tests/test_commands/recordings/test_commands/test_mkdir/class_setup.yaml +++ b/tests/test_commands/recordings/test_commands/test_mkdir/class_setup.yaml @@ -11,7 +11,7 @@ interactions: Content-Type: - application/json User-Agent: - - ms-fabric-cli/1.1.0 (None; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) + - ms-fabric-cli/1.2.0 (None; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) method: GET uri: https://api.fabric.microsoft.com/v1/workspaces response: @@ -26,15 +26,15 @@ interactions: Content-Encoding: - gzip Content-Length: - - '786' + - '1054' Content-Type: - application/json; charset=utf-8 Date: - - Thu, 25 Sep 2025 09:13:42 GMT + - Wed, 10 Dec 2025 14:36:12 GMT Pragma: - no-cache RequestId: - - 7eaee504-fac2-4c96-8e32-dbef7efe8bde + - 46a76ab9-a143-451d-90d0-f9fd657b1dbe Strict-Transport-Security: - max-age=31536000; includeSubDomains X-Content-Type-Options: @@ -60,7 +60,7 @@ interactions: Content-Type: - application/json User-Agent: - - ms-fabric-cli/1.1.0 (None; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) + - ms-fabric-cli/1.2.0 (None; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) method: GET uri: https://api.fabric.microsoft.com/v1/workspaces response: @@ -75,15 +75,15 @@ interactions: Content-Encoding: - gzip Content-Length: - - '786' + - '1054' Content-Type: - application/json; charset=utf-8 Date: - - Thu, 25 Sep 2025 09:13:43 GMT + - Wed, 10 Dec 2025 14:36:12 GMT Pragma: - no-cache RequestId: - - 695ed7dd-2104-488c-b0c0-845f91a5ca01 + - b659f6f2-2390-446e-ab99-3b090cc635ea Strict-Transport-Security: - max-age=31536000; includeSubDomains X-Content-Type-Options: @@ -109,7 +109,7 @@ interactions: Content-Type: - application/json User-Agent: - - ms-fabric-cli/1.1.0 (None; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) + - ms-fabric-cli/1.2.0 (None; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) method: GET uri: https://api.fabric.microsoft.com/v1/capacities response: @@ -125,15 +125,15 @@ interactions: Content-Encoding: - gzip Content-Length: - - '271' + - '873' Content-Type: - application/json; charset=utf-8 Date: - - Thu, 25 Sep 2025 09:13:47 GMT + - Wed, 10 Dec 2025 14:36:16 GMT Pragma: - no-cache RequestId: - - 6d155373-03fd-4f34-8d94-0820e30d54b1 + - b23c547e-bc6b-4a83-9f1c-248239404869 Strict-Transport-Security: - max-age=31536000; includeSubDomains X-Content-Type-Options: @@ -162,12 +162,12 @@ interactions: Content-Type: - application/json User-Agent: - - ms-fabric-cli/1.1.0 (None; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) + - ms-fabric-cli/1.2.0 (None; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) method: POST uri: https://api.fabric.microsoft.com/v1/workspaces response: body: - string: '{"id": "d37f35bf-4dce-4291-9812-477382595e4b", "displayName": "fabriccli_WorkspacePerTestclass_000001", + string: '{"id": "b14aea11-ff9f-40fd-a872-21636c090395", "displayName": "fabriccli_WorkspacePerTestclass_000001", "description": "Created by fab", "type": "Workspace", "capacityId": "00000000-0000-0000-0000-000000000004"}' headers: Access-Control-Expose-Headers: @@ -177,17 +177,17 @@ interactions: Content-Encoding: - gzip Content-Length: - - '188' + - '187' Content-Type: - application/json; charset=utf-8 Date: - - Thu, 25 Sep 2025 09:13:55 GMT + - Wed, 10 Dec 2025 14:36:23 GMT Location: - - https://api.fabric.microsoft.com/v1/workspaces/d37f35bf-4dce-4291-9812-477382595e4b + - https://api.fabric.microsoft.com/v1/workspaces/b14aea11-ff9f-40fd-a872-21636c090395 Pragma: - no-cache RequestId: - - 9950b60b-464a-477d-a0ee-0c15c5cdf936 + - 97eb7f80-4e28-4201-ae75-5570c99ad930 Strict-Transport-Security: - max-age=31536000; includeSubDomains X-Content-Type-Options: @@ -213,13 +213,13 @@ interactions: Content-Type: - application/json User-Agent: - - ms-fabric-cli/1.1.0 (ls; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) + - ms-fabric-cli/1.2.0 (mkdir; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) method: GET uri: https://api.fabric.microsoft.com/v1/workspaces response: body: string: '{"value": [{"id": "3634a139-2c9e-4205-910b-3b089a31be47", "displayName": - "My workspace", "description": "", "type": "Personal"}, {"id": "d37f35bf-4dce-4291-9812-477382595e4b", + "My workspace", "description": "", "type": "Personal"}, {"id": "b14aea11-ff9f-40fd-a872-21636c090395", "displayName": "fabriccli_WorkspacePerTestclass_000001", "description": "Created by fab", "type": "Workspace", "capacityId": "00000000-0000-0000-0000-000000000004"}]}' headers: @@ -230,15 +230,15 @@ interactions: Content-Encoding: - gzip Content-Length: - - '820' + - '1091' Content-Type: - application/json; charset=utf-8 Date: - - Thu, 25 Sep 2025 09:14:33 GMT + - Wed, 10 Dec 2025 14:36:57 GMT Pragma: - no-cache RequestId: - - c1d25fd0-3982-4b9e-bf98-dc785f01a79d + - 9d54b68b-5a87-439a-90ba-d343916423c8 Strict-Transport-Security: - max-age=31536000; includeSubDomains X-Content-Type-Options: @@ -264,9 +264,9 @@ interactions: Content-Type: - application/json User-Agent: - - ms-fabric-cli/1.1.0 (ls; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) + - ms-fabric-cli/1.2.0 (mkdir; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) method: GET - uri: https://api.fabric.microsoft.com/v1/workspaces/d37f35bf-4dce-4291-9812-477382595e4b/items + uri: https://api.fabric.microsoft.com/v1/workspaces/b14aea11-ff9f-40fd-a872-21636c090395/items response: body: string: '{"value": []}' @@ -282,11 +282,11 @@ interactions: Content-Type: - application/json; charset=utf-8 Date: - - Thu, 25 Sep 2025 09:14:34 GMT + - Wed, 10 Dec 2025 14:36:58 GMT Pragma: - no-cache RequestId: - - 4faadd3b-1048-460e-9bc3-fc8be2360ac1 + - 353f6d45-0cff-4127-a474-a9b1317790c4 Strict-Transport-Security: - max-age=31536000; includeSubDomains X-Content-Type-Options: @@ -314,9 +314,9 @@ interactions: Content-Type: - application/json User-Agent: - - ms-fabric-cli/1.1.0 (ls; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) + - ms-fabric-cli/1.2.0 (mkdir; Linux; x86_64; 6.6.87.2-microsoft-standard-WSL2) method: DELETE - uri: https://api.fabric.microsoft.com/v1/workspaces/d37f35bf-4dce-4291-9812-477382595e4b + uri: https://api.fabric.microsoft.com/v1/workspaces/b14aea11-ff9f-40fd-a872-21636c090395 response: body: string: '' @@ -332,11 +332,11 @@ interactions: Content-Type: - application/octet-stream Date: - - Thu, 25 Sep 2025 09:14:35 GMT + - Wed, 10 Dec 2025 14:36:59 GMT Pragma: - no-cache RequestId: - - 5c18271d-3f2c-4a89-a672-d0707be7a579 + - 522420d0-d4cb-4d93-8a13-a5a9bdf30d41 Strict-Transport-Security: - max-age=31536000; includeSubDomains X-Content-Type-Options: diff --git a/tests/test_commands/recordings/test_commands/test_mkdir/test_mkdir_dependency_creation_batched_output_kql_database_success.yaml b/tests/test_commands/recordings/test_commands/test_mkdir/test_mkdir_dependency_creation_batched_output_kql_database_success.yaml new file mode 100644 index 00000000..700eb674 --- /dev/null +++ b/tests/test_commands/recordings/test_commands/test_mkdir/test_mkdir_dependency_creation_batched_output_kql_database_success.yaml @@ -0,0 +1,515 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces + response: + body: + string: '{"value": [{"id": "3634a139-2c9e-4205-910b-3b089a31be47", "displayName": + "My workspace", "description": "", "type": "Personal"}, {"id": "b14aea11-ff9f-40fd-a872-21636c090395", + "displayName": "fabriccli_WorkspacePerTestclass_000001", "description": "Created + by fab", "type": "Workspace", "capacityId": "00000000-0000-0000-0000-000000000004"}]}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '1091' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 10 Dec 2025 14:36:24 GMT + Pragma: + - no-cache + RequestId: + - f818cb76-08dc-4c58-8642-b69407adb76f + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces/b14aea11-ff9f-40fd-a872-21636c090395/items + response: + body: + string: '{"value": []}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '32' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 10 Dec 2025 14:36:25 GMT + Pragma: + - no-cache + RequestId: + - 97548fdd-8553-47f0-b61a-44b16f94918a + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces/b14aea11-ff9f-40fd-a872-21636c090395/items + response: + body: + string: '{"value": []}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '32' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 10 Dec 2025 14:36:26 GMT + Pragma: + - no-cache + RequestId: + - c7ddd3b5-2a3a-4f14-b053-c1c790d66a7d + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: '{"description": "Created by fab", "displayName": "fabcli000001_auto", "type": + "Eventhouse", "folderId": null}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '113' + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: POST + uri: https://api.fabric.microsoft.com/v1/workspaces/b14aea11-ff9f-40fd-a872-21636c090395/eventhouses + response: + body: + string: '{"id": "8520e310-fb97-41f4-b9f7-2cf2e55b2c12", "type": "Eventhouse", + "displayName": "fabcli000001_auto", "description": "Created by fab", "workspaceId": + "b14aea11-ff9f-40fd-a872-21636c090395"}' + headers: + Access-Control-Expose-Headers: + - RequestId,ETag + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '174' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 10 Dec 2025 14:36:30 GMT + ETag: + - '""' + Pragma: + - no-cache + RequestId: + - c77fb138-52b9-4aa4-b70e-7e093a481aa0 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 201 + message: Created +- request: + body: '{"description": "Created by fab", "displayName": "fabcli000001", "type": + "KQLDatabase", "folderId": null, "creationPayload": {"databaseType": "ReadWrite", + "parentEventhouseItemId": "8520e310-fb97-41f4-b9f7-2cf2e55b2c12"}}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '225' + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: POST + uri: https://api.fabric.microsoft.com/v1/workspaces/b14aea11-ff9f-40fd-a872-21636c090395/kqlDatabases + response: + body: + string: 'null' + headers: + Access-Control-Expose-Headers: + - RequestId,Location,Retry-After,ETag,x-ms-operation-id + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '24' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 10 Dec 2025 14:36:32 GMT + ETag: + - '""' + Location: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/v1/operations/c08a8a14-a569-47a1-ada7-dcaf3a904eb3 + Pragma: + - no-cache + RequestId: + - cf117302-8705-4b5b-96df-1f579c5f3d20 + Retry-After: + - '20' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + x-ms-operation-id: + - c08a8a14-a569-47a1-ada7-dcaf3a904eb3 + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://wabi-us-central-b-primary-redirect.analysis.windows.net/v1/operations/c08a8a14-a569-47a1-ada7-dcaf3a904eb3 + response: + body: + string: '{"status": "Succeeded", "createdTimeUtc": "2025-12-10T14:36:31.7079906", + "lastUpdatedTimeUtc": "2025-12-10T14:36:44.5531228", "percentComplete": 100, + "error": null}' + headers: + Access-Control-Expose-Headers: + - RequestId,Location,x-ms-operation-id + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '132' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 10 Dec 2025 14:36:53 GMT + Location: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/v1/operations/c08a8a14-a569-47a1-ada7-dcaf3a904eb3/result + Pragma: + - no-cache + RequestId: + - 4f001d02-e052-4cad-859f-f336cb482c16 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + x-ms-operation-id: + - c08a8a14-a569-47a1-ada7-dcaf3a904eb3 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://wabi-us-central-b-primary-redirect.analysis.windows.net/v1/operations/c08a8a14-a569-47a1-ada7-dcaf3a904eb3/result + response: + body: + string: '{"id": "b18bd8f6-f619-441c-99d3-3e76e097938c", "type": "KQLDatabase", + "displayName": "fabcli000001", "description": "Created by fab", "workspaceId": + "b14aea11-ff9f-40fd-a872-21636c090395"}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Wed, 10 Dec 2025 14:36:54 GMT + Pragma: + - no-cache + RequestId: + - 75721e04-1663-4e5a-81e4-ee4ecae62ed7 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces + response: + body: + string: '{"value": [{"id": "3634a139-2c9e-4205-910b-3b089a31be47", "displayName": + "My workspace", "description": "", "type": "Personal"}, {"id": "b14aea11-ff9f-40fd-a872-21636c090395", + "displayName": "fabriccli_WorkspacePerTestclass_000001", "description": "Created + by fab", "type": "Workspace", "capacityId": "00000000-0000-0000-0000-000000000004"}]}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '1091' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 10 Dec 2025 14:36:55 GMT + Pragma: + - no-cache + RequestId: + - 8457d766-4cd3-473a-bde0-8e347258ff4a + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces/b14aea11-ff9f-40fd-a872-21636c090395/items + response: + body: + string: '{"value": [{"id": "8520e310-fb97-41f4-b9f7-2cf2e55b2c12", "type": "Eventhouse", + "displayName": "fabcli000001_auto", "description": "Created by fab", "workspaceId": + "b14aea11-ff9f-40fd-a872-21636c090395"}, {"id": "608c7f46-8cc0-4f34-8608-7ce19f6a4839", + "type": "KQLDatabase", "displayName": "fabcli000001_auto", "description": + "fabcli000001_auto", "workspaceId": "b14aea11-ff9f-40fd-a872-21636c090395"}, + {"id": "b18bd8f6-f619-441c-99d3-3e76e097938c", "type": "KQLDatabase", "displayName": + "fabcli000001", "description": "Created by fab", "workspaceId": "b14aea11-ff9f-40fd-a872-21636c090395"}]}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '262' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 10 Dec 2025 14:36:56 GMT + Pragma: + - no-cache + RequestId: + - 1f5702a8-62dc-4c40-bf32-53eff33c5d59 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: DELETE + uri: https://api.fabric.microsoft.com/v1/workspaces/b14aea11-ff9f-40fd-a872-21636c090395/items/8520e310-fb97-41f4-b9f7-2cf2e55b2c12 + response: + body: + string: '' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '0' + Content-Type: + - application/octet-stream + Date: + - Wed, 10 Dec 2025 14:36:57 GMT + Pragma: + - no-cache + RequestId: + - 31d2cc18-dd35-4f8f-bbee-996b59d0b923 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_commands/recordings/test_commands/test_mkdir/test_mkdir_single_item_creation_batch_output_structure_success.yaml b/tests/test_commands/recordings/test_commands/test_mkdir/test_mkdir_single_item_creation_batch_output_structure_success.yaml new file mode 100644 index 00000000..c1a180b1 --- /dev/null +++ b/tests/test_commands/recordings/test_commands/test_mkdir/test_mkdir_single_item_creation_batch_output_structure_success.yaml @@ -0,0 +1,355 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces + response: + body: + string: '{"value": [{"id": "3634a139-2c9e-4205-910b-3b089a31be47", "displayName": + "My workspace", "description": "", "type": "Personal"}, {"id": "6af05968-8605-4178-a09a-23829d1f9ee0", + "displayName": "fabriccli_WorkspacePerTestclass_000001", "description": "Created + by fab", "type": "Workspace", "capacityId": "00000000-0000-0000-0000-000000000004"}]}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '1053' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 04 Dec 2025 14:43:40 GMT + Pragma: + - no-cache + RequestId: + - 4624fc45-d3f5-469f-8d50-2a0e6d38eb5a + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces/6af05968-8605-4178-a09a-23829d1f9ee0/items + response: + body: + string: '{"value": []}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '32' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 04 Dec 2025 14:43:40 GMT + Pragma: + - no-cache + RequestId: + - 40449fcc-44bb-4119-adfd-79bfdf8a87f9 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces/6af05968-8605-4178-a09a-23829d1f9ee0/items + response: + body: + string: '{"value": []}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '32' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 04 Dec 2025 14:43:41 GMT + Pragma: + - no-cache + RequestId: + - 85d2e888-3929-452b-b9a7-a7d892740b54 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: '{"description": "Created by fab", "displayName": "fabcli000001", "type": + "Lakehouse", "folderId": null}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '107' + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: POST + uri: https://api.fabric.microsoft.com/v1/workspaces/6af05968-8605-4178-a09a-23829d1f9ee0/lakehouses + response: + body: + string: '{"id": "375a206a-39fd-4396-a95a-447d4746d746", "type": "Lakehouse", + "displayName": "fabcli000001", "description": "Created by fab", "workspaceId": + "6af05968-8605-4178-a09a-23829d1f9ee0"}' + headers: + Access-Control-Expose-Headers: + - RequestId,ETag + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '167' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 04 Dec 2025 14:43:47 GMT + ETag: + - '""' + Pragma: + - no-cache + RequestId: + - 7761d018-5b93-4a7f-a12c-c75db6434d03 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces + response: + body: + string: '{"value": [{"id": "3634a139-2c9e-4205-910b-3b089a31be47", "displayName": + "My workspace", "description": "", "type": "Personal"}, {"id": "6af05968-8605-4178-a09a-23829d1f9ee0", + "displayName": "fabriccli_WorkspacePerTestclass_000001", "description": "Created + by fab", "type": "Workspace", "capacityId": "00000000-0000-0000-0000-000000000004"}]}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '1053' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 04 Dec 2025 14:43:47 GMT + Pragma: + - no-cache + RequestId: + - 381d9bef-5d66-4c2e-a2eb-d51ee762a6e4 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: GET + uri: https://api.fabric.microsoft.com/v1/workspaces/6af05968-8605-4178-a09a-23829d1f9ee0/items + response: + body: + string: '{"value": [{"id": "375a206a-39fd-4396-a95a-447d4746d746", "type": "Lakehouse", + "displayName": "fabcli000001", "description": "Created by fab", "workspaceId": + "6af05968-8605-4178-a09a-23829d1f9ee0"}]}' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '179' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 04 Dec 2025 14:43:48 GMT + Pragma: + - no-cache + RequestId: + - 88c858d2-b2d8-4c21-85a7-a33bdbbeea76 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - ms-fabric-cli-test/1.2.0 + method: DELETE + uri: https://api.fabric.microsoft.com/v1/workspaces/6af05968-8605-4178-a09a-23829d1f9ee0/items/375a206a-39fd-4396-a95a-447d4746d746 + response: + body: + string: '' + headers: + Access-Control-Expose-Headers: + - RequestId + Cache-Control: + - no-store, must-revalidate, no-cache + Content-Encoding: + - gzip + Content-Length: + - '0' + Content-Type: + - application/octet-stream + Date: + - Thu, 04 Dec 2025 14:43:49 GMT + Pragma: + - no-cache + RequestId: + - 1b5a10b8-4e6c-4b6c-b4ce-b09aa25a8254 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - deny + home-cluster-uri: + - https://wabi-us-central-b-primary-redirect.analysis.windows.net/ + request-redirected: + - 'true' + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_commands/test_api.py b/tests/test_commands/test_api.py index 708d7517..7b000712 100644 --- a/tests/test_commands/test_api.py +++ b/tests/test_commands/test_api.py @@ -200,6 +200,8 @@ def _build_api_args(endpoint, method, params, input, audience, headers, show_hea def _get_workspace_id(workspace, mock_questionary_print) -> str: + mock_questionary_print.reset_mock() + api("workspaces", "get") # Extract the arguments passed to the mock @@ -219,6 +221,8 @@ def _get_workspace_id(workspace, mock_questionary_print) -> str: def _get_item_id(workspace_id, item, mock_questionary_print) -> str: + mock_questionary_print.reset_mock() + api(f"workspaces/{workspace_id}/items", "get") # Extract the arguments passed to the mock diff --git a/tests/test_commands/test_cd.py b/tests/test_commands/test_cd.py index ac4794b8..de9c0885 100644 --- a/tests/test_commands/test_cd.py +++ b/tests/test_commands/test_cd.py @@ -17,7 +17,7 @@ def test_cd_tenant_success(self, mock_print_done, cli_executor): cli_executor.exec_command("cd /") # Assert - mock_print_done.assert_called_once_with("Switched to root") + mock_print_done.assert_called_once_with("Switched to root\n") assert isinstance(Context().context, Tenant) def test_cd_workspace_success(self, workspace, mock_print_done, cli_executor): diff --git a/tests/test_commands/test_cp.py b/tests/test_commands/test_cp.py index 5e4ce6d4..b29866dc 100644 --- a/tests/test_commands/test_cp.py +++ b/tests/test_commands/test_cp.py @@ -64,7 +64,7 @@ def test_cp_workspace_to_workspace_non_recursive_success( mock_print_warning.assert_called_once() assert any( call.args[0] - == f"2 items copied successfully from {ws1.full_path} to {ws2.full_path}" + == f"2 items copied successfully from {ws1.full_path} to {ws2.full_path}\n" for call in mock_print_done.mock_calls ) @@ -146,7 +146,7 @@ def test_cp_workspace_to_workspace_recursive_success( mock_print_warning.assert_called_once() assert any( call.args[0] - == f"2 items and 1 folders copied successfully from {ws1.full_path} to {ws2.full_path}" + == f"2 items and 1 folders copied successfully from {ws1.full_path} to {ws2.full_path}\n" for call in mock_print_done.mock_calls ) @@ -254,7 +254,7 @@ def test_cp_workspace_to_folder_success( mock_print_warning.assert_called_once() assert any( call.args[0] - == f"2 items copied successfully from {ws1.full_path} to {f2.full_path}" + == f"2 items copied successfully from {ws1.full_path} to {f2.full_path}\n" for call in mock_print_done.mock_calls ) diff --git a/tests/test_commands/test_export.py b/tests/test_commands/test_export.py index 7077d377..dd52add9 100644 --- a/tests/test_commands/test_export.py +++ b/tests/test_commands/test_export.py @@ -136,7 +136,7 @@ def test_export_workspace_success( mock_print_done.assert_called() mock_print_warning.assert_called_once() assert any( - call.args[0] == "2 items exported successfully" + call.args[0] == "2 items exported successfully\n" for call in mock_print_done.mock_calls ) diff --git a/tests/test_commands/test_labels.py b/tests/test_commands/test_labels.py index 86b8e962..784dd9c1 100644 --- a/tests/test_commands/test_labels.py +++ b/tests/test_commands/test_labels.py @@ -92,7 +92,7 @@ def test_labels_set_lakehouse_with_force_success( cli_executor.exec_command(command_str) # Assert mock_print_done.assert_called() - assert mock_print_done.call_args[0][0] == "Label set" + assert mock_print_done.call_args[0][0] == "Label set\n" def test_labels_set_without_force_success( self, item_factory, cli_executor, mock_print_done, test_data: StaticTestData @@ -111,7 +111,7 @@ def test_labels_set_without_force_success( cli_executor.exec_command(command_str) # Assert mock_print_done.assert_called() - assert mock_print_done.call_args[0][0] == "Label set" + assert mock_print_done.call_args[0][0] == "Label set\n" def test_labels_set_invalid_label_name_failure( self, diff --git a/tests/test_commands/test_ls.py b/tests/test_commands/test_ls.py index dd9819c4..dfef0bc5 100644 --- a/tests/test_commands/test_ls.py +++ b/tests/test_commands/test_ls.py @@ -596,6 +596,8 @@ def test_ls_spark_pool_success( workspace.full_path, str(VirtualItemContainerType.SPARK_POOL) ) + mock_questionary_print.reset_mock() + # Test 1: without args cli_executor.exec_command(f"ls {sparkpools_path}") @@ -690,6 +692,9 @@ def test_ls_managed_private_endpoints_success( managed_private_endpoints_path = cli_path_join( workspace.full_path, str(VirtualItemContainerType.MANAGED_PRIVATE_ENDPOINT) ) + + mock_questionary_print.reset_mock() + # Test 1: without args cli_executor.exec_command(f"ls {managed_private_endpoints_path}") diff --git a/tests/test_commands/test_mkdir.py b/tests/test_commands/test_mkdir.py index b6bcf690..3a4a1fe2 100644 --- a/tests/test_commands/test_mkdir.py +++ b/tests/test_commands/test_mkdir.py @@ -177,7 +177,8 @@ def test_mkdir_kqldatabase_without_creation_payload_success( # Assert # call_count is 2 because the first call is for the parent eventhouse and the second call is for the kqldatabase assert upsert_item_to_cache.call_count == 2 - assert mock_print_done.call_count == 2 + # print call_count is 1 because we batch the result of the first call for the parent eventhouse and the second call for the kqldatabase + assert mock_print_done.call_count == 1 assert any( kqldatabase_display_name in call.args[0] for call in mock_print_done.mock_calls @@ -1315,10 +1316,10 @@ def test_mkdir_connection_with_onpremises_gateway_params_success( # Assert mock_print_done.assert_called() assert mock_print_done.call_count == 1 - assert f"'{connection_display_name}.Connection' created" == mock_print_done.call_args[0][0] + assert f"'{connection_display_name}.Connection' created\n" == mock_print_done.call_args[0][0] mock_print_done.reset_mock() - + # Cleanup rm(connection_full_path) @@ -1338,13 +1339,13 @@ def test_mkdir_connection_with_onpremises_gateway_params_ignore_params_success( ) cli_executor.exec_command( - f'mkdir {connection_full_path} -P gatewayId={test_data.onpremises_gateway_details.id},connectionDetails.type=SQL,connectivityType=OnPremisesGateway,connectionDetails.parameters.server={test_data.sql_server.server}.database.windows.net,connectionDetails.parameters.database={test_data.sql_server.database},credentialDetails.type=Basic,credentialDetails.values=\'[{{"gatewayId":"{test_data.onpremises_gateway_details.id}","encryptedCredentials":"{test_data.onpremises_gateway_details.encrypted_credentials}","ignoreParameters":"ignoreParameters"}}]\'' + f"mkdir {connection_full_path} -P gatewayId={test_data.onpremises_gateway_details.id},connectionDetails.type=SQL,connectivityType=OnPremisesGateway,connectionDetails.parameters.server={test_data.sql_server.server}.database.windows.net,connectionDetails.parameters.database={test_data.sql_server.database},credentialDetails.type=Basic,credentialDetails.values='[{{\"gatewayId\":\"{test_data.onpremises_gateway_details.id}\",\"encryptedCredentials\":\"{test_data.onpremises_gateway_details.encrypted_credentials}\",\"ignoreParameters\":\"ignoreParameters\"}}]'" ) # Assert mock_print_done.assert_called() assert mock_print_done.call_count == 1 - assert f"'{connection_display_name}.Connection' created" == mock_print_done.call_args[0][0] + assert f"'{connection_display_name}.Connection' created\n" == mock_print_done.call_args[0][0] mock_print_warning.assert_called() assert mock_print_warning.call_count == 1 @@ -1353,6 +1354,7 @@ def test_mkdir_connection_with_onpremises_gateway_params_ignore_params_success( # Cleanup rm(connection_full_path) + def test_mkdir_connection_with_onpremises_gateway_params_failure( self, cli_executor, @@ -1395,7 +1397,7 @@ def test_mkdir_connection_with_onpremises_gateway_params_failure( # Test 3: Execute command with missing encryptedCredentials params in one of the values cli_executor.exec_command( - f'mkdir {connection_full_path} -P gatewayId={test_data.onpremises_gateway_details.id},connectionDetails.type=SQL,connectivityType=OnPremisesGateway,connectionDetails.parameters.server={test_data.sql_server.server}.database.windows.net,connectionDetails.parameters.database={test_data.sql_server.database},credentialDetails.type=Basic,credentialDetails.values=\'[{{"gatewayId":"{test_data.onpremises_gateway_details.id}","encryptedCredentials":"{test_data.onpremises_gateway_details.encrypted_credentials}"}},{{"encryptedCredentials":"{test_data.onpremises_gateway_details.encrypted_credentials}"}}]\'' + f"mkdir {connection_full_path} -P gatewayId={test_data.onpremises_gateway_details.id},connectionDetails.type=SQL,connectivityType=OnPremisesGateway,connectionDetails.parameters.server={test_data.sql_server.server}.database.windows.net,connectionDetails.parameters.database={test_data.sql_server.database},credentialDetails.type=Basic,credentialDetails.values='[{{\"gatewayId\":\"{test_data.onpremises_gateway_details.id}\",\"encryptedCredentials\":\"{test_data.onpremises_gateway_details.encrypted_credentials}\"}},{{\"encryptedCredentials\":\"{test_data.onpremises_gateway_details.encrypted_credentials}\"}}]'" ) # Assert @@ -1408,7 +1410,7 @@ def test_mkdir_connection_with_onpremises_gateway_params_failure( # Test 4: Execute command with invalid json format for values cli_executor.exec_command( - f"mkdir {connection_full_path} -P gatewayId={test_data.onpremises_gateway_details.id},connectionDetails.type=SQL,connectivityType=OnPremisesGateway,connectionDetails.parameters.server={test_data.sql_server.server}.database.windows.net,connectionDetails.parameters.database={test_data.sql_server.database},credentialDetails.type=Basic,credentialDetails.values='[{{gatewayId:{test_data.onpremises_gateway_details.id}, encryptedCredentials:{test_data.onpremises_gateway_details.encrypted_credentials}}}]'" + f"mkdir {connection_full_path} -P gatewayId={test_data.onpremises_gateway_details.id},connectionDetails.type=SQL,connectivityType=OnPremisesGateway,connectionDetails.parameters.server={test_data.sql_server.server}.database.windows.net,connectionDetails.parameters.database={test_data.sql_server.database},credentialDetails.type=Basic,credentialDetails.values=[{{gatewayId:{test_data.onpremises_gateway_details.id}, encryptedCredentials:{test_data.onpremises_gateway_details.encrypted_credentials}}}]" ) # Assert @@ -1715,7 +1717,7 @@ def test_mkdir_workspace_verify_stderr_stdout_messages_text_format_success( ) # Verify exact stdout message in text format - assert captured.out.strip() == f"* '{workspace_display_name}.Workspace' created" + assert f"* '{workspace_display_name}.Workspace' created" in captured.out.strip() # Cleanup rm(workspace_full_path) @@ -1755,36 +1757,38 @@ def test_mkdir_workspace_verify_stderr_stdout_messages_json_format_success( # endregion # region Folders - + def test_mkdir_item_in_folder_listing_success( self, workspace, cli_executor, mock_print_done, mock_questionary_print, mock_fab_set_state_config, vcr_instance, cassette_name ): # Enable folder listing mock_fab_set_state_config(constant.FAB_FOLDER_LISTING_ENABLED, "true") + # Setup folder_name = f"{generate_random_string(vcr_instance, cassette_name)}.Folder" folder_full_path = cli_path_join(workspace.full_path, folder_name) - + # Create folder cli_executor.exec_command(f"mkdir {folder_full_path}") mock_print_done.assert_called_once() mock_print_done.reset_mock() - + # Create notebook in folder notebook_name = f"{generate_random_string(vcr_instance, cassette_name)}.Notebook" notebook_full_path = cli_path_join(folder_full_path, notebook_name) cli_executor.exec_command(f"mkdir {notebook_full_path}") - + # Verify notebook appears in folder listing cli_executor.exec_command(f"ls {folder_full_path}") printed_output = mock_questionary_print.call_args[0][0] assert notebook_name in printed_output - + # Cleanup rm(notebook_full_path) rm(folder_full_path) + def test_mkdir_folder_success(self, workspace, cli_executor, mock_print_done): # Setup folder_display_name = "folder" @@ -1836,6 +1840,89 @@ def test_mkdir_folder_name_already_exists_failure( # endregion + # region Batch Output Tests + def test_mkdir_single_item_creation_batch_output_structure_success( + self, workspace, cli_executor, mock_print_done, mock_questionary_print, vcr_instance, cassette_name + ): + """Test that single item creation uses batched output structure.""" + # Setup + lakehouse_display_name = generate_random_string(vcr_instance, cassette_name) + lakehouse_full_path = cli_path_join( + workspace.full_path, f"{lakehouse_display_name}.{ItemType.LAKEHOUSE}" + ) + + # Execute command + cli_executor.exec_command(f"mkdir {lakehouse_full_path}") + + # Assert - verify output structure + mock_print_done.assert_called_once() + call_args = mock_print_done.call_args + assert lakehouse_display_name in call_args[0][0] + assert "created" in call_args[0][0] + + # Verify headers and values in mock_questionary_print.mock_calls + # Look for the table output with headers + output_calls = [str(call) for call in mock_questionary_print.mock_calls] + table_output = "\n".join(output_calls) + + # Check for standard table headers + assert "id" in table_output or "ID" in table_output + assert "type" in table_output or "Type" in table_output + assert "displayName" in table_output or "DisplayName" in table_output + assert "workspaceId" in table_output or "WorkspaceId" in table_output + + # Check for actual values + assert lakehouse_display_name in table_output + assert "Lakehouse" in table_output + + # Cleanup + rm(lakehouse_full_path) + + def test_mkdir_dependency_creation_batched_output_kql_database_success( + self, workspace, cli_executor, mock_print_done, mock_questionary_print, vcr_instance, cassette_name + ): + """Test that KQL Database creation with EventHouse dependency produces batched output.""" + # Setup + kqldatabase_display_name = generate_random_string(vcr_instance, cassette_name) + kqldatabase_full_path = cli_path_join( + workspace.full_path, f"{kqldatabase_display_name}.{ItemType.KQL_DATABASE}" + ) + + # Execute command (this will create EventHouse dependency automatically) + cli_executor.exec_command(f"mkdir {kqldatabase_full_path}") + + # Assert - should have two print_done calls (one consolidated output with both items) + # The current implementation may still have separate calls, but data should be collected + assert mock_print_done.call_count >= 1 + + + # Verify both items are mentioned in output + all_calls = [call.args[0] for call in mock_print_done.call_args_list] + all_output = " ".join(all_calls) + assert f"'{kqldatabase_display_name}_auto.{ItemType.EVENTHOUSE.value}' and '{kqldatabase_display_name}.{ItemType.KQL_DATABASE.value}' created" in all_output + + # Verify headers and values in mock_questionary_print.mock_calls for batched output + output_calls = [str(call) for call in mock_questionary_print.mock_calls] + table_output = "\n".join(output_calls) + + # Check for standard table headers (should appear once for consolidated table) + assert "id" in table_output or "ID" in table_output + assert "type" in table_output or "Type" in table_output + assert "displayName" in table_output or "DisplayName" in table_output + assert "workspaceId" in table_output or "WorkspaceId" in table_output + + # Check for both item values in the output + assert kqldatabase_display_name in table_output + assert f"{kqldatabase_display_name}_auto" in table_output # EventHouse dependency name + assert "KQLDatabase" in table_output or "KQL_DATABASE" in table_output + assert "Eventhouse" in table_output or "EVENTHOUSE" in table_output + + # Cleanup - removing parent eventhouse removes the kqldatabase as well + eventhouse_full_path = kqldatabase_full_path.removesuffix(".KQLDatabase") + "_auto.Eventhouse" + rm(eventhouse_full_path) + + # endregion + # region Helper Methods def mkdir(path, params=["run=true"]): diff --git a/tests/test_commands/test_mv.py b/tests/test_commands/test_mv.py index 844af71a..5563ff6b 100644 --- a/tests/test_commands/test_mv.py +++ b/tests/test_commands/test_mv.py @@ -395,7 +395,7 @@ def test_mv_item_from_workspace_to_workspace_when_item_already_exists( # Assert mock_print_done.assert_called() assert any( - call.args[0] == "Move completed" for call in mock_print_done.mock_calls + call.args[0] == "Move completed\n" for call in mock_print_done.mock_calls ) mock_questionary_print.reset_mock() @@ -545,7 +545,7 @@ def test_mv_item_within_workspace_rename_success( # Assert mock_print_done.assert_called() assert any( - call.args[0] == "Move completed" for call in mock_print_done.mock_calls + call.args[0] == "Move completed\n" for call in mock_print_done.mock_calls ) mock_questionary_print.reset_mock() diff --git a/tests/test_commands/test_rm.py b/tests/test_commands/test_rm.py index 91567add..d68f82e7 100644 --- a/tests/test_commands/test_rm.py +++ b/tests/test_commands/test_rm.py @@ -252,7 +252,8 @@ def test_rm_workspace_without_force_success( ) mkdir(notebook.full_path) mock_print_done.reset_mock() - + mock_questionary_print.reset_mock() + with ( patch("questionary.checkbox") as mock_checkbox, patch("questionary.confirm") as mock_confirm, @@ -270,7 +271,7 @@ def test_rm_workspace_without_force_success( mock_questionary_print.assert_called() mock_print_done.assert_called() _assert_strings_in_mock_calls( - ["1 items deleted successfully"], True, mock_print_done.mock_calls + ["1 items deleted successfully\n"], True, mock_print_done.mock_calls ) _assert_strings_in_mock_calls( [notebook.display_name], True, mock_print_done.mock_calls