diff --git a/.chronus/changes/add-python-alternate-type-mock-api-test-2026-5-12-23-16-00.md b/.chronus/changes/add-python-alternate-type-mock-api-test-2026-5-12-23-16-00.md new file mode 100644 index 00000000000..47ce56ebe73 --- /dev/null +++ b/.chronus/changes/add-python-alternate-type-mock-api-test-2026-5-12-23-16-00.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Add sync and async mock API tests for the azure/client-generator-core/alternate-type Spector scenario diff --git a/.chronus/changes/add-visibility-head-model-test-2026-5-4-23-8-9.md b/.chronus/changes/add-visibility-head-model-test-2026-5-4-23-8-9.md new file mode 100644 index 00000000000..26893a4c4dc --- /dev/null +++ b/.chronus/changes/add-visibility-head-model-test-2026-5-4-23-8-9.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Add test coverage for `headModel` scenario in `type/model/visibility` spec. diff --git a/.chronus/changes/payload-head-python-test-2026-4-17-22-55-53.md b/.chronus/changes/payload-head-python-test-2026-4-17-22-55-53.md new file mode 100644 index 00000000000..ee198e709bd --- /dev/null +++ b/.chronus/changes/payload-head-python-test-2026-4-17-22-55-53.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Add mock API test for `payload/head` scenario with `Content-Type` and `x-ms-meta` response headers. diff --git a/.chronus/changes/python-add-response-as-bool-test-2026-6-10-5-33-0.md b/.chronus/changes/python-add-response-as-bool-test-2026-6-10-5-33-0.md new file mode 100644 index 00000000000..9c5937cf445 --- /dev/null +++ b/.chronus/changes/python-add-response-as-bool-test-2026-6-10-5-33-0.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Add mock api test for azure client-generator-core response-as-bool spector case diff --git a/.chronus/changes/python-reserved-operation-body-params-test-2026-4-28-23-10-0.md b/.chronus/changes/python-reserved-operation-body-params-test-2026-4-28-23-10-0.md new file mode 100644 index 00000000000..ad10ee1042b --- /dev/null +++ b/.chronus/changes/python-reserved-operation-body-params-test-2026-4-28-23-10-0.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Add mock API test cases for operation body parameters with reserved names (e.g., `items`) to verify the wire name is not mangled. diff --git a/.chronus/changes/python-usage-namespace-test-2026-5-26-23-18-25.md b/.chronus/changes/python-usage-namespace-test-2026-5-26-23-18-25.md new file mode 100644 index 00000000000..255a67569a5 --- /dev/null +++ b/.chronus/changes/python-usage-namespace-test-2026-5-26-23-18-25.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Add sync and async mock API tests for the Azure client-generator-core usage namespace usage scenario from azure-http-specs. diff --git a/cspell.yaml b/cspell.yaml index 58ec6e0489f..42cc56da89c 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -238,6 +238,7 @@ words: - reinjected - repr - respecify + - responseasbool - rjust - rollup - rpaas diff --git a/packages/http-client-python/eng/scripts/ci/regenerate-common.ts b/packages/http-client-python/eng/scripts/ci/regenerate-common.ts index 1fe6f17d63c..ded02b8ef50 100644 --- a/packages/http-client-python/eng/scripts/ci/regenerate-common.ts +++ b/packages/http-client-python/eng/scripts/ci/regenerate-common.ts @@ -70,11 +70,7 @@ export interface BuildTaskGroupsOptions { // ---- Public constants ---- -export const SKIP_SPECS: string[] = [ - "type/file", - "service/multiple-services", - "azure/client-generator-core/response-as-bool", -]; +export const SKIP_SPECS: string[] = ["type/file", "service/multiple-services"]; export const SpecialFlags: Record> = { azure: { @@ -728,6 +724,7 @@ export async function prepareBaselineOfGeneratedCode(generatedFolder: string): P "azure/generation-subdir2", "unbranded/generation-subdir", "unbranded/generation-subdir2", + "azure/azure-client-generator-core-alternate-type", ]; const sourceRoot = join(tempDir, ...sourceSubdir.split("/")); diff --git a/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_alternate_type_async.py b/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_alternate_type_async.py new file mode 100644 index 00000000000..30f453723e1 --- /dev/null +++ b/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_alternate_type_async.py @@ -0,0 +1,74 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import pytest +import pytest_asyncio +import geojson +from specs.azure.clientgenerator.core.alternatetype.aio import AlternateTypeClient +from specs.azure.clientgenerator.core.alternatetype import models + +# Shared test data +PROPERTIES = {"name": "A single point of interest", "category": "landmark", "elevation": 100} + +GEOMETRY = geojson.Point((-122.25, 37.87)) + +FEATURE_ID = "feature-1" + + +@pytest_asyncio.fixture +async def client(): + async with AlternateTypeClient(endpoint="http://localhost:3000") as client: + yield client + + +@pytest.fixture +def feature_geojson(): + """Shared GeoJSON Feature for tests.""" + return geojson.Feature(type="Feature", geometry=GEOMETRY, properties=PROPERTIES, id=FEATURE_ID) + + +@pytest.mark.asyncio +async def test_external_type_get_model(client: AlternateTypeClient): + """Test getting a Feature object with geometry, properties, and optional id fields.""" + result = await client.external_type.get_model() + + # Validate the response structure based on the TypeSpec example + assert result.type == "Feature" + assert result.geometry.type == "Point" + assert result.geometry.coordinates == [-122.25, 37.87] + assert result.properties == PROPERTIES + assert result.id == FEATURE_ID + + +@pytest.mark.asyncio +async def test_external_type_put_model(client: AlternateTypeClient, feature_geojson): + """Test putting a Feature object in request body.""" + # Should return None (204/empty response) + result = await client.external_type.put_model(body=feature_geojson) + assert result is None + + +@pytest.mark.asyncio +async def test_external_type_get_property(client: AlternateTypeClient): + """Test getting a ModelWithFeatureProperty object with feature and additionalProperty fields.""" + result = await client.external_type.get_property() + + # Validate the response structure based on the TypeSpec example + assert result.feature.type == "Feature" + assert result.feature.geometry.type == "Point" + assert result.feature.geometry.coordinates == [-122.25, 37.87] + assert result.feature.properties == PROPERTIES + assert result.feature.id == FEATURE_ID + assert result.additional_property == "extra" + + +@pytest.mark.asyncio +async def test_external_type_put_property(client: AlternateTypeClient, feature_geojson): + """Test putting a ModelWithFeatureProperty object in request body.""" + model_with_feature = models.ModelWithFeatureProperty(feature=feature_geojson, additional_property="extra") + + # Should return None (204/empty response) + result = await client.external_type.put_property(body=model_with_feature) + assert result is None diff --git a/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_response_as_bool_async.py b/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_response_as_bool_async.py new file mode 100644 index 00000000000..9df21cc96d8 --- /dev/null +++ b/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_response_as_bool_async.py @@ -0,0 +1,24 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import pytest +import pytest_asyncio +from specs.azure.clientgenerator.core.responseasbool.aio import ResponseAsBoolClient + + +@pytest_asyncio.fixture +async def client(): + async with ResponseAsBoolClient() as client: + yield client + + +@pytest.mark.asyncio +async def test_exists(client: ResponseAsBoolClient): + assert await client.head_as_boolean.exists() is True + + +@pytest.mark.asyncio +async def test_not_exists(client: ResponseAsBoolClient): + assert await client.head_as_boolean.not_exists() is False diff --git a/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_usage_async.py b/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_usage_async.py index 816279eeb94..9a82c7e573a 100644 --- a/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_usage_async.py +++ b/packages/http-client-python/tests/mock_api/azure/asynctests/test_azure_client_generator_core_usage_async.py @@ -37,3 +37,9 @@ async def test_orphan_model_serializable(client: UsageClient): await client.model_in_operation.orphan_model_serializable( body=models.OrphanModel(model_name="name", description="desc") ) + + +@pytest.mark.asyncio +async def test_namespace_model_serializable(client: UsageClient): + namespace_model = models.NamespaceModel(name="test") + await client.namespace_usage.namespace_model_serializable(body=namespace_model) diff --git a/packages/http-client-python/tests/mock_api/azure/asynctests/test_special_words_async.py b/packages/http-client-python/tests/mock_api/azure/asynctests/test_special_words_async.py index 8c59ea05070..78650db138d 100644 --- a/packages/http-client-python/tests/mock_api/azure/asynctests/test_special_words_async.py +++ b/packages/http-client-python/tests/mock_api/azure/asynctests/test_special_words_async.py @@ -70,3 +70,8 @@ async def test_model_properties_with_list(client: SpecialWordsClient): async def test_extensible_strings(client: SpecialWordsClient): for enum_value in models.ExtensibleString: assert enum_value == await client.extensible_strings.put_extensible_string_value(body=enum_value) + + +@pytest.mark.asyncio +async def test_reserved_operation_body_params_with_items(client: SpecialWordsClient): + await client.reserved_operation_body_params.with_items(items=["item"]) diff --git a/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_alternate_type.py b/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_alternate_type.py new file mode 100644 index 00000000000..c5ae3266c87 --- /dev/null +++ b/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_alternate_type.py @@ -0,0 +1,69 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import pytest +import geojson +from specs.azure.clientgenerator.core.alternatetype import AlternateTypeClient +from specs.azure.clientgenerator.core.alternatetype import models + +# Shared test data +PROPERTIES = {"name": "A single point of interest", "category": "landmark", "elevation": 100} + +GEOMETRY = geojson.Point((-122.25, 37.87)) + +FEATURE_ID = "feature-1" + + +@pytest.fixture +def client(): + with AlternateTypeClient(endpoint="http://localhost:3000") as client: + yield client + + +@pytest.fixture +def feature_geojson(): + """Shared GeoJSON Feature for tests.""" + return geojson.Feature(type="Feature", geometry=GEOMETRY, properties=PROPERTIES, id=FEATURE_ID) + + +def test_external_type_get_model(client: AlternateTypeClient): + """Test getting a Feature object with geometry, properties, and optional id fields.""" + result = client.external_type.get_model() + + # Validate the response structure based on the TypeSpec example + assert result.type == "Feature" + assert result.geometry.type == "Point" + assert result.geometry.coordinates == [-122.25, 37.87] + assert result.properties == PROPERTIES + assert result.id == FEATURE_ID + + +def test_external_type_put_model(client: AlternateTypeClient, feature_geojson): + """Test putting a Feature object in request body.""" + # Should return None (204/empty response) + result = client.external_type.put_model(body=feature_geojson) + assert result is None + + +def test_external_type_get_property(client: AlternateTypeClient): + """Test getting a ModelWithFeatureProperty object with feature and additionalProperty fields.""" + result = client.external_type.get_property() + + # Validate the response structure based on the TypeSpec example + assert result.feature.type == "Feature" + assert result.feature.geometry.type == "Point" + assert result.feature.geometry.coordinates == [-122.25, 37.87] + assert result.feature.properties == PROPERTIES + assert result.feature.id == FEATURE_ID + assert result.additional_property == "extra" + + +def test_external_type_put_property(client: AlternateTypeClient, feature_geojson): + """Test putting a ModelWithFeatureProperty object in request body.""" + model_with_feature = models.ModelWithFeatureProperty(feature=feature_geojson, additional_property="extra") + + # Should return None (204/empty response) + result = client.external_type.put_property(body=model_with_feature) + assert result is None diff --git a/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_response_as_bool.py b/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_response_as_bool.py new file mode 100644 index 00000000000..e37a641ce11 --- /dev/null +++ b/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_response_as_bool.py @@ -0,0 +1,21 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import pytest +from specs.azure.clientgenerator.core.responseasbool import ResponseAsBoolClient + + +@pytest.fixture +def client(): + with ResponseAsBoolClient() as client: + yield client + + +def test_exists(client: ResponseAsBoolClient): + assert client.head_as_boolean.exists() is True + + +def test_not_exists(client: ResponseAsBoolClient): + assert client.head_as_boolean.not_exists() is False diff --git a/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_usage.py b/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_usage.py index 0094bef89a1..f40f64041eb 100644 --- a/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_usage.py +++ b/packages/http-client-python/tests/mock_api/azure/test_azure_client_generator_core_usage.py @@ -30,3 +30,12 @@ def test_model_usage(client: UsageClient): def test_orphan_model_serializable(client: UsageClient): client.model_in_operation.orphan_model_serializable(body=models.OrphanModel(model_name="name", description="desc")) + + +def test_namespace_model_serializable(client: UsageClient): + namespace_model = models.NamespaceModel(name="test") + client.namespace_usage.namespace_model_serializable(body=namespace_model) + +def test_import(): + # NestedNamespaceModel shall be exposed + from specs.azure.clientgenerator.core.usage.models import NestedNamespaceModel diff --git a/packages/http-client-python/tests/mock_api/azure/test_special_words.py b/packages/http-client-python/tests/mock_api/azure/test_special_words.py index ab23b59409f..234301dede1 100644 --- a/packages/http-client-python/tests/mock_api/azure/test_special_words.py +++ b/packages/http-client-python/tests/mock_api/azure/test_special_words.py @@ -61,3 +61,7 @@ def test_model_properties_with_list(client: SpecialWordsClient): def test_extensible_strings(client: SpecialWordsClient): for enum_value in models.ExtensibleString: assert enum_value == client.extensible_strings.put_extensible_string_value(body=enum_value) + + +def test_reserved_operation_body_params_with_items(client: SpecialWordsClient): + client.reserved_operation_body_params.with_items(items=["item"]) diff --git a/packages/http-client-python/tests/mock_api/shared/asynctests/test_payload_head_async.py b/packages/http-client-python/tests/mock_api/shared/asynctests/test_payload_head_async.py new file mode 100644 index 00000000000..0b24e996530 --- /dev/null +++ b/packages/http-client-python/tests/mock_api/shared/asynctests/test_payload_head_async.py @@ -0,0 +1,26 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import pytest +import pytest_asyncio +from payload.head.aio import HeadClient + + +@pytest_asyncio.fixture +async def client(): + async with HeadClient(endpoint="http://localhost:3000") as client: + yield client + + +@pytest.mark.asyncio +async def test_content_type_header_in_response(client: HeadClient): + assert await client.content_type_header_in_response() is True + + +@pytest.mark.asyncio +async def test_content_type_header_in_response_with_cls(client: HeadClient): + headers = await client.content_type_header_in_response(cls=lambda x, y, z: z) + assert headers["Content-Type"] == "text/plain; charset=utf-8" + assert headers["x-ms-meta"] == "hello" diff --git a/packages/http-client-python/tests/mock_api/shared/asynctests/test_typetest_model_visibility_async.py b/packages/http-client-python/tests/mock_api/shared/asynctests/test_typetest_model_visibility_async.py index 62827695819..d5038fcd7de 100644 --- a/packages/http-client-python/tests/mock_api/shared/asynctests/test_typetest_model_visibility_async.py +++ b/packages/http-client-python/tests/mock_api/shared/asynctests/test_typetest_model_visibility_async.py @@ -21,6 +21,11 @@ async def test_get_model(client): assert result == models.VisibilityModel(read_prop="abc") +@pytest.mark.asyncio +async def test_head_model(client): + assert await client.head_model(models.VisibilityModel(), query_prop=123) + + @pytest.mark.asyncio async def test_put_model(client): await client.put_model(models.VisibilityModel(create_prop=["foo", "bar"], update_prop=[1, 2])) diff --git a/packages/http-client-python/tests/mock_api/shared/test_payload_head.py b/packages/http-client-python/tests/mock_api/shared/test_payload_head.py new file mode 100644 index 00000000000..86464d56b2d --- /dev/null +++ b/packages/http-client-python/tests/mock_api/shared/test_payload_head.py @@ -0,0 +1,23 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import pytest +from payload.head import HeadClient + + +@pytest.fixture +def client(): + with HeadClient(endpoint="http://localhost:3000") as client: + yield client + + +def test_content_type_header_in_response(client: HeadClient): + assert client.content_type_header_in_response() is True + + +def test_content_type_header_in_response_with_cls(client: HeadClient): + headers = client.content_type_header_in_response(cls=lambda x, y, z: z) + assert headers["Content-Type"] == "text/plain; charset=utf-8" + assert headers["x-ms-meta"] == "hello" diff --git a/packages/http-client-python/tests/mock_api/shared/test_typetest_model_visibility.py b/packages/http-client-python/tests/mock_api/shared/test_typetest_model_visibility.py index c6f4695f2fa..ddbe78d2e3e 100644 --- a/packages/http-client-python/tests/mock_api/shared/test_typetest_model_visibility.py +++ b/packages/http-client-python/tests/mock_api/shared/test_typetest_model_visibility.py @@ -18,6 +18,10 @@ def test_get_model(client): assert result == models.VisibilityModel(read_prop="abc") +def test_head_model(client): + assert client.head_model(models.VisibilityModel(), query_prop=123) + + def test_put_model(client): client.put_model(models.VisibilityModel(create_prop=["foo", "bar"], update_prop=[1, 2])) diff --git a/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_special_words_async.py b/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_special_words_async.py index c1e3bc0a8f2..5b8dcebaf1f 100644 --- a/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_special_words_async.py +++ b/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_special_words_async.py @@ -72,3 +72,8 @@ async def test_model_properties_with_list(client: SpecialWordsClient): async def test_extensible_strings(client: SpecialWordsClient): for enum_value in extensible_strings_models.ExtensibleString: assert enum_value == await client.extensible_strings.put_extensible_string_value(body=enum_value) + + +@pytest.mark.asyncio +async def test_reserved_operation_body_params_with_items(client: SpecialWordsClient): + await client.reserved_operation_body_params.with_items(items=["item"]) diff --git a/packages/http-client-python/tests/mock_api/unbranded/test_special_words.py b/packages/http-client-python/tests/mock_api/unbranded/test_special_words.py index 83269cc1e47..9da0330a805 100644 --- a/packages/http-client-python/tests/mock_api/unbranded/test_special_words.py +++ b/packages/http-client-python/tests/mock_api/unbranded/test_special_words.py @@ -64,3 +64,7 @@ def test_model_properties_with_list(client: SpecialWordsClient): def test_extensible_strings(client: SpecialWordsClient): for enum_value in extensible_strings_models.ExtensibleString: assert enum_value == client.extensible_strings.put_extensible_string_value(body=enum_value) + + +def test_reserved_operation_body_params_with_items(client: SpecialWordsClient): + client.reserved_operation_body_params.with_items(items=["item"])