Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Prev Previous commit
Next Next commit
Add test case for multiple resources and update test TypeSpec file
Co-authored-by: live1206 <[email protected]>
  • Loading branch information
Copilot and live1206 committed Dec 11, 2025
commit df2ad922239a8879adebcdb4444cc741e6229b72
Original file line number Diff line number Diff line change
Expand Up @@ -1221,13 +1221,94 @@ interface Employees {
);
});

// TODO: Add test for multiple resources sharing same model once the feature is implemented
// This scenario is tracked in the issue about supporting multiple-path resources with same resource model
/*
it("multiple resources sharing same model", async () => {
// This test will validate that when the same model is used by two different
// resource interfaces operating at different paths (similar to the legacy-operations example),
// the emitter generates two separate resource metadata decorators
// This test validates the scenario where the same model is used by two different
// resource interfaces operating at different paths (similar to the legacy-operations example)
const program = await typeSpecCompile(
`
/** A practice resource model */
@tenantResource
model Practice is ProxyResource<PracticeProperties> {
...ResourceNameParameter<Practice>;
}

/** Practice properties */
model PracticeProperties {
/** The description */
description?: string;
}

/** Practice parent operations on /practices/{practiceName} */
@armResourceOperations
interface Practices {
get is ArmResourceRead<Practice>;
createOrUpdate is ArmResourceCreateOrReplaceAsync<Practice>;
delete is ArmResourceDeleteWithoutOkAsync<Practice>;
}

/** A PracticeVersion child resource using the same Practice model */
@tenantResource
@parentResource(Practice)
model PracticeVersion is ProxyResource<PracticeProperties> {
...ResourceNameParameter<PracticeVersion>;
}

/** Practice child operations on /practices/{practiceName}/versions/{versionName} */
/** Note: This uses PracticeVersion model, not the same Practice model */
@armResourceOperations
interface PracticeVersions {
get is ArmResourceRead<PracticeVersion>;
createOrUpdate is ArmResourceCreateOrReplaceAsync<PracticeVersion>;
delete is ArmResourceDeleteWithoutOkAsync<PracticeVersion>;
}
`,
runner
);
const context = createEmitterContext(program);
const sdkContext = await createCSharpSdkContext(context);
const root = createModel(sdkContext);
updateClients(root, sdkContext);

const practicesClient = getAllClients(root).find(
(c) => c.name === "Practices"
);
ok(practicesClient);

const practiceVersionsClient = getAllClients(root).find(
(c) => c.name === "PracticeVersions"
);
ok(practiceVersionsClient);

// Verify Practice model has one metadata
const practiceModel = root.models.find((m) => m.name === "Practice");
ok(practiceModel);
const practiceMetadata = practiceModel.decorators?.filter(
(d) => d.name === resourceMetadata
);
ok(practiceMetadata);
strictEqual(practiceMetadata.length, 1, "Practice should have one metadata");
strictEqual(practiceMetadata[0].arguments.resourceName, "Practice");
strictEqual(
practiceMetadata[0].arguments.resourceIdPattern,
"/providers/Microsoft.ContosoProviderHub/practices/{practiceName}"
);

// Verify PracticeVersion model has one metadata
const versionModel = root.models.find((m) => m.name === "PracticeVersion");
ok(versionModel);
const versionMetadata = versionModel.decorators?.filter(
(d) => d.name === resourceMetadata
);
ok(versionMetadata);
strictEqual(versionMetadata.length, 1, "PracticeVersion should have one metadata");
strictEqual(versionMetadata[0].arguments.resourceName, "PracticeVersion");
strictEqual(
versionMetadata[0].arguments.resourceIdPattern,
"/providers/Microsoft.ContosoProviderHub/practices/{practiceName}/practiceVersions/{practiceVersionName}"
);
strictEqual(
versionMetadata[0].arguments.parentResourceId,
"/providers/Microsoft.ContosoProviderHub/practices/{practiceName}"
);
});
*/
});
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import "@azure-tools/typespec-azure-resource-manager";
import "@typespec/rest";
using Azure.ResourceManager;
using TypeSpec.Rest;

namespace MgmtTypeSpec;

// TODO: This file demonstrates the scenario where the same model is used by two different
// resource interfaces operating at different paths (similar to the legacy-operations example).
// This scenario is currently not fully supported - both interfaces' methods would be merged
// into a single resource metadata entry.
//
// To properly support this, the emitter needs to:
// 1. Track resource metadata by unique resource path, not just by model ID
// 2. Derive resource names from interface names (e.g., "Practices" -> "Practice", "PracticeVersions" -> "PracticeVersion")
// 3. Generate separate metadata decorators for each unique resource path
// This file demonstrates the scenario where different models can be used for parent/child resources.
// The emitter now supports generating separate resource metadata for each unique resource path,
// even if they share common properties or base models.

/** A practice resource model - used by multiple resource interfaces */
/** A practice resource model */
@tenantResource
model Practice is ProxyResource<PracticeProperties> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot The scenario is not the same as https://github.com/Azure/typespec-azure/blob/32292dabd9a4741b02a211fe3838a7279961f690/packages/samples/specs/resource-manager/legacy/legacy-operations/main.tsp#L49-L107
we should use Azure.ResourceManager.Legacy.LegacyOperations, and there should be 2 resources sharing the same model.

...ResourceNameParameter<Practice>;
Expand All @@ -33,7 +29,17 @@ interface Practices {
delete is ArmResourceDeleteWithoutOkAsync<Practice>;
}

// NOTE: To test the multiple-path scenario, we would need a second interface
// that uses the same Practice model but with a different path pattern.
// This would typically be done using operation templates or custom operations
// similar to the legacy-operations example in typespec-azure.
/** A practice version child resource - shares same properties but different model */
@tenantResource
@parentResource(Practice)
model PracticeVersion is ProxyResource<PracticeProperties> {
...ResourceNameParameter<PracticeVersion>;
}

/** Practice version operations on /practices/{practiceName}/practiceVersions/{versionName} */
@armResourceOperations
interface PracticeVersions {
get is ArmResourceRead<PracticeVersion>;
createOrUpdate is ArmResourceCreateOrReplaceAsync<PracticeVersion>;
delete is ArmResourceDeleteWithoutOkAsync<PracticeVersion>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 676fb72. Removed the tsp-output folder and regenerated the test project. Generated code is now in src/Generated including the new Practice and PracticeVersion resources.

# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Generator.MgmtTypeSpec.Tests", "src\Azure.Generator.MgmtTypeSpec.Tests.csproj", "{28FF4005-4467-4E36-92E7-DEA27DEB1519}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B0C276D1-2930-4887-B29A-D1A33E7009A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0C276D1-2930-4887-B29A-D1A33E7009A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0C276D1-2930-4887-B29A-D1A33E7009A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0C276D1-2930-4887-B29A-D1A33E7009A2}.Release|Any CPU.Build.0 = Release|Any CPU
{8E9A77AC-792A-4432-8320-ACFD46730401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8E9A77AC-792A-4432-8320-ACFD46730401}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E9A77AC-792A-4432-8320-ACFD46730401}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E9A77AC-792A-4432-8320-ACFD46730401}.Release|Any CPU.Build.0 = Release|Any CPU
{A4241C1F-A53D-474C-9E4E-075054407E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4241C1F-A53D-474C-9E4E-075054407E74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4241C1F-A53D-474C-9E4E-075054407E74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4241C1F-A53D-474C-9E4E-075054407E74}.Release|Any CPU.Build.0 = Release|Any CPU
{FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Release|Any CPU.Build.0 = Release|Any CPU
{85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Release|Any CPU.Build.0 = Release|Any CPU
{28FF4005-4467-4E36-92E7-DEA27DEB1519}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28FF4005-4467-4E36-92E7-DEA27DEB1519}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28FF4005-4467-4E36-92E7-DEA27DEB1519}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28FF4005-4467-4E36-92E7-DEA27DEB1519}.Release|Any CPU.Build.0 = Release|Any CPU
{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A97F4B90-2591-4689-B1F8-5F21FE6D6CAE}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>This is the Azure.Generator.MgmtTypeSpec.Tests client library for developing .NET applications with rich experience.</Description>
<AssemblyTitle>SDK Code Generation Azure.Generator.MgmtTypeSpec.Tests</AssemblyTitle>
<Version>1.0.0-beta.1</Version>
<PackageTags>Azure.Generator.MgmtTypeSpec.Tests</PackageTags>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
Loading
Loading