diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/ScmKnownParameters.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/ScmKnownParameters.cs
index e07006acf43..336fc35dff0 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/ScmKnownParameters.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/ScmKnownParameters.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.ClientModel;
using System.ClientModel.Primitives;
using System.Text.Json;
using System.Threading;
@@ -66,5 +67,8 @@ public static ParameterProvider ClientOptions(CSharpType clientOptionsType)
public static readonly ParameterProvider NextPage =
new ParameterProvider("nextPage", $"The url of the next page of responses.", typeof(Uri));
+
+ public static readonly ParameterProvider KeyCredential = new("credential", FormattableStringHelpers.Empty, typeof(ApiKeyCredential));
+ public static readonly ParameterProvider TokenCredential = new("credential", FormattableStringHelpers.Empty, typeof(AuthenticationTokenProvider));
}
}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleParameterValue.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleParameterValue.cs
new file mode 100644
index 00000000000..5ff6aa62201
--- /dev/null
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleParameterValue.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.TypeSpec.Generator.Expressions;
+using Microsoft.TypeSpec.Generator.Input;
+using Microsoft.TypeSpec.Generator.Primitives;
+
+namespace Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples
+{
+ ///
+ /// Represents a parameter value in a sample. Supports two modes:
+ /// - : raw example data that will be converted to a C# expression later
+ /// - : a pre-built C# expression (used for known parameters like credentials, endpoints)
+ ///
+ public class ExampleParameterValue
+ {
+ public ExampleParameterValue(string name, CSharpType type, InputExampleValue value)
+ {
+ Name = name;
+ Type = type;
+ Value = value;
+ }
+
+ public ExampleParameterValue(string name, CSharpType type, ValueExpression expression)
+ {
+ Name = name;
+ Type = type;
+ Expression = expression;
+ }
+
+ ///
+ /// The parameter name.
+ ///
+ public string Name { get; }
+
+ ///
+ /// The C# type of the parameter.
+ ///
+ public CSharpType Type { get; }
+
+ ///
+ /// Raw example data from the spec or mock builder. Will be converted to a
+ /// via .
+ /// Mutually exclusive with .
+ ///
+ public InputExampleValue? Value { get; }
+
+ ///
+ /// A pre-built C# expression. Used for known parameters (credentials, endpoints)
+ /// where the expression is fixed regardless of example data.
+ /// Mutually exclusive with .
+ ///
+ public ValueExpression? Expression { get; }
+ }
+}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs
new file mode 100644
index 00000000000..a2c906c9080
--- /dev/null
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs
@@ -0,0 +1,379 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using Microsoft.TypeSpec.Generator.Expressions;
+using Microsoft.TypeSpec.Generator.Input;
+using Microsoft.TypeSpec.Generator.Primitives;
+using Microsoft.TypeSpec.Generator.Providers;
+using static Microsoft.TypeSpec.Generator.Snippets.Snippet;
+
+namespace Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples
+{
+ ///
+ /// Converts + into (C# AST nodes).
+ /// This is the bridge between raw mock/spec example data and generated C# code.
+ ///
+ public static class ExampleValueExpressionBuilder
+ {
+ ///
+ /// Converts an to a C# expression.
+ /// If the parameter has a pre-built expression, returns it directly.
+ /// Otherwise, converts the raw using type information.
+ ///
+ public static ValueExpression GetExpression(ExampleParameterValue parameterValue, SerializationFormat format = SerializationFormat.Default)
+ {
+ if (parameterValue.Expression != null)
+ return parameterValue.Expression;
+
+ if (parameterValue.Value != null)
+ return GetExpression(parameterValue.Type, parameterValue.Value, format);
+
+ return Default;
+ }
+
+ ///
+ /// Converts an to a C# expression based on the target .
+ ///
+ public static ValueExpression GetExpression(CSharpType type, InputExampleValue exampleValue, SerializationFormat format = SerializationFormat.Default)
+ {
+ if (type.IsList)
+ return GetExpressionForList(type, exampleValue);
+ if (type.IsDictionary)
+ return GetExpressionForDictionary(type, exampleValue);
+ if (type.IsEnum)
+ return GetExpressionForEnum(type, exampleValue);
+ if (type is { IsFrameworkType: true })
+ return GetExpressionForFrameworkType(type.FrameworkType, exampleValue, format);
+
+ // For model types, fall back to default
+ return GetExpressionForModel(type, exampleValue);
+ }
+
+ private static ValueExpression GetExpressionForFrameworkType(Type frameworkType, InputExampleValue exampleValue, SerializationFormat format = SerializationFormat.Default)
+ {
+ var rawValue = GetRawValue(exampleValue);
+
+ // String
+ if (frameworkType == typeof(string))
+ {
+ return rawValue is string s ? Literal(s) : Null;
+ }
+
+ // Boolean
+ if (frameworkType == typeof(bool))
+ {
+ return rawValue is bool b ? Literal(b) : Default;
+ }
+
+ // Integer types
+ if (frameworkType == typeof(int))
+ {
+ return rawValue != null ? Literal(Convert.ToInt32(rawValue)) : Default;
+ }
+ if (frameworkType == typeof(long))
+ {
+ return rawValue != null ? Literal(Convert.ToInt64(rawValue)) : Default;
+ }
+ if (frameworkType == typeof(short))
+ {
+ return rawValue != null ? new CastExpression(Literal(Convert.ToInt16(rawValue)), frameworkType) : Default;
+ }
+ if (frameworkType == typeof(sbyte))
+ {
+ return rawValue != null ? new CastExpression(Literal(Convert.ToSByte(rawValue)), frameworkType) : Default;
+ }
+ if (frameworkType == typeof(byte))
+ {
+ return rawValue != null ? new CastExpression(Literal(Convert.ToByte(rawValue)), frameworkType) : Default;
+ }
+ if (frameworkType == typeof(ushort))
+ {
+ return rawValue != null ? new CastExpression(Literal(Convert.ToUInt16(rawValue)), frameworkType) : Default;
+ }
+ if (frameworkType == typeof(uint))
+ {
+ return rawValue != null ? new CastExpression(Literal(Convert.ToUInt32(rawValue)), frameworkType) : Default;
+ }
+ if (frameworkType == typeof(ulong))
+ {
+ return rawValue != null ? new CastExpression(Literal(Convert.ToUInt64(rawValue)), frameworkType) : Default;
+ }
+
+ // Float types
+ if (frameworkType == typeof(float))
+ {
+ return rawValue != null ? Literal(Convert.ToSingle(rawValue)) : Default;
+ }
+ if (frameworkType == typeof(double))
+ {
+ return rawValue != null ? Literal(Convert.ToDouble(rawValue)) : Default;
+ }
+ if (frameworkType == typeof(decimal))
+ {
+ return rawValue != null ? Literal(Convert.ToDecimal(rawValue)) : Default;
+ }
+
+ // Guid
+ if (frameworkType == typeof(Guid))
+ {
+ if (rawValue is string s)
+ return Static(typeof(Guid)).Invoke(nameof(Guid.Parse), Literal(s));
+ return Default;
+ }
+
+ // Uri
+ if (frameworkType == typeof(Uri))
+ {
+ if (rawValue is string s)
+ return New.Instance(typeof(Uri), Literal(s));
+ return Null;
+ }
+
+ // DateTimeOffset
+ if (frameworkType == typeof(DateTimeOffset))
+ {
+ if (format == SerializationFormat.DateTime_Unix)
+ {
+ var unixValue = rawValue is string us ? Convert.ToInt64(us) : rawValue is int or long ? Convert.ToInt64(rawValue) : 0L;
+ return Static(typeof(DateTimeOffset)).Invoke(nameof(DateTimeOffset.FromUnixTimeSeconds), Literal(unixValue));
+ }
+ if (rawValue is string s)
+ return Static(typeof(DateTimeOffset)).Invoke(nameof(DateTimeOffset.Parse), Literal(s));
+ if (rawValue is int or long)
+ return Static(typeof(DateTimeOffset)).Invoke(nameof(DateTimeOffset.FromUnixTimeSeconds), Literal(Convert.ToInt64(rawValue)));
+ return Default;
+ }
+
+ // TimeSpan
+ if (frameworkType == typeof(TimeSpan))
+ {
+ if (format is SerializationFormat.Duration_Seconds or SerializationFormat.Duration_Seconds_Float or SerializationFormat.Duration_Milliseconds)
+ {
+ if (rawValue is string ds)
+ return Static(typeof(TimeSpan)).Invoke(nameof(TimeSpan.FromSeconds), Literal(Convert.ToDouble(ds)));
+ if (rawValue is int or float or double)
+ return Static(typeof(TimeSpan)).Invoke(nameof(TimeSpan.FromSeconds), Literal(Convert.ToDouble(rawValue)));
+ }
+ if (rawValue is string s)
+ return Static(typeof(XmlConvert)).Invoke(nameof(XmlConvert.ToTimeSpan), Literal(s));
+ if (rawValue is int or float or double)
+ return Static(typeof(TimeSpan)).Invoke(nameof(TimeSpan.FromSeconds), Literal(Convert.ToDouble(rawValue)));
+ return Default;
+ }
+
+ // BinaryData
+ if (frameworkType == typeof(BinaryData))
+ {
+ if (rawValue == null && exampleValue is not InputExampleValue)
+ return Null;
+ return GetExpressionForBinaryData(exampleValue);
+ }
+
+ // byte[]
+ if (frameworkType == typeof(byte[]))
+ {
+ if (rawValue is string s)
+ return Static(typeof(Encoding)).Property(nameof(Encoding.UTF8))
+ .Invoke(nameof(Encoding.GetBytes), Literal(s));
+ return Null;
+ }
+
+ // Stream
+ if (frameworkType == typeof(Stream))
+ {
+ if (exampleValue is InputExampleStreamValue streamValue)
+ return Static(typeof(File)).Invoke(nameof(File.OpenRead), Literal(streamValue.Filename));
+ return Null;
+ }
+
+ // Fallback
+ return frameworkType.IsValueType ? Default : Null;
+ }
+
+ private static ValueExpression GetExpressionForList(CSharpType listType, InputExampleValue exampleValue)
+ {
+ var elementType = listType.ElementType;
+ var items = new List();
+
+ if (exampleValue is InputExampleListValue listValue)
+ {
+ foreach (var itemValue in listValue.Values)
+ {
+ items.Add(GetExpression(elementType, itemValue));
+ }
+ }
+
+ return New.Array(elementType, items.ToArray());
+ }
+
+ private static ValueExpression GetExpressionForDictionary(CSharpType dictionaryType, InputExampleValue exampleValue)
+ {
+ var keyType = dictionaryType.Arguments[0];
+ var valueType = dictionaryType.Arguments[1];
+ var entries = new Dictionary();
+
+ if (exampleValue is InputExampleObjectValue objectValue)
+ {
+ foreach (var (key, value) in objectValue.Values)
+ {
+ var keyExpr = GetExpression(keyType, InputExampleValue.Value(new InputPrimitiveType(InputPrimitiveTypeKind.String, "string", "TypeSpec.string"), key));
+ var valueExpr = GetExpression(valueType, value);
+ entries[keyExpr] = valueExpr;
+ }
+ }
+
+ return New.Dictionary(keyType, valueType, entries);
+ }
+
+ private static ValueExpression GetExpressionForEnum(CSharpType enumType, InputExampleValue exampleValue)
+ {
+ var rawValue = GetRawValue(exampleValue);
+ if (rawValue == null)
+ return Default;
+
+ // Access the enum member by name using the type reference
+ var rawString = rawValue.ToString()!;
+ // Use the type name as a static access point: EnumType.MemberName
+ return new MemberExpression(Static(enumType), rawString);
+ }
+
+ private static ValueExpression GetExpressionForModel(CSharpType type, InputExampleValue exampleValue)
+ {
+ if (type.IsValueType)
+ return Default;
+
+ // Try to resolve the model's TypeProvider to get constructor parameters
+ if (exampleValue is InputExampleObjectValue objectValue &&
+ CodeModelGenerator.Instance.TypeFactory.CSharpTypeMap.TryGetValue(type, out var typeProvider) &&
+ typeProvider is ModelProvider modelProvider)
+ {
+ // Find the public constructor with the most parameters
+ ConstructorProvider? bestCtor = null;
+ foreach (var ctor in modelProvider.Constructors)
+ {
+ if (ctor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public))
+ {
+ if (bestCtor == null || ctor.Signature.Parameters.Count > bestCtor.Signature.Parameters.Count)
+ bestCtor = ctor;
+ }
+ }
+
+ if (bestCtor != null && bestCtor.Signature.Parameters.Count > 0)
+ {
+ var arguments = new List();
+ foreach (var param in bestCtor.Signature.Parameters)
+ {
+ InputExampleValue? matchedValue = null;
+
+ // Try matching by parameter name
+ objectValue.Values.TryGetValue(param.Name, out matchedValue);
+
+ // Try matching by wire serialized name
+ if (matchedValue == null && param.Property?.WireInfo?.SerializedName != null)
+ objectValue.Values.TryGetValue(param.Property.WireInfo.SerializedName, out matchedValue);
+
+ // Try matching by property name
+ if (matchedValue == null && param.Property != null)
+ objectValue.Values.TryGetValue(param.Property.Name, out matchedValue);
+
+ if (matchedValue != null)
+ {
+ arguments.Add(GetExpression(param.Type, matchedValue));
+ }
+ else if (param.DefaultValue != null)
+ {
+ arguments.Add(param.DefaultValue);
+ }
+ else
+ {
+ arguments.Add(param.Type.IsValueType ? Default : Null);
+ }
+ }
+
+ return New.Instance(type, [.. arguments]);
+ }
+ }
+
+ return New.Instance(type);
+ }
+
+ private static ValueExpression GetExpressionForBinaryData(InputExampleValue exampleValue)
+ {
+ // Build an anonymous object from the example value and wrap in BinaryData.FromObjectAsJson
+ var anonymousObj = GetExpressionForAnonymousObject(exampleValue);
+ return Static(typeof(BinaryData)).Invoke(nameof(BinaryData.FromObjectAsJson), anonymousObj);
+ }
+
+ ///
+ /// Converts an example value to an anonymous object expression for use in
+ /// BinaryData.FromObjectAsJson() or BinaryContent.Create().
+ ///
+ internal static ValueExpression GetExpressionForAnonymousObject(InputExampleValue exampleValue)
+ {
+ if (exampleValue is InputExampleObjectValue objectValue)
+ {
+ var properties = new Dictionary();
+ foreach (var (key, value) in objectValue.Values)
+ {
+ var rawVal = GetRawValue(value);
+ // Skip null properties in anonymous objects (causes compilation errors)
+ if (rawVal == null && value is InputExampleRawValue)
+ continue;
+
+ var valueExpr = GetExpressionForAnonymousObject(value);
+ properties[Identifier(key)] = valueExpr;
+ }
+ return properties.Count > 0 ? New.Anonymous(properties) : New.Instance(typeof(object));
+ }
+
+ if (exampleValue is InputExampleListValue listValue)
+ {
+ var items = new List();
+ foreach (var item in listValue.Values)
+ {
+ items.Add(GetExpressionForAnonymousObject(item));
+ }
+ return New.Array(new CSharpType(typeof(object)), items.ToArray());
+ }
+
+ if (exampleValue is InputExampleStreamValue streamValue)
+ {
+ return Static(typeof(File)).Invoke(nameof(File.OpenRead), Literal(streamValue.Filename));
+ }
+
+ // Raw value — convert to literal
+ var raw = GetRawValue(exampleValue);
+ if (raw == null)
+ return Null;
+
+ return raw switch
+ {
+ string s => Literal(s),
+ bool b => Literal(b),
+ int i => Literal(i),
+ long l => Literal(l),
+ float f => Literal(f),
+ double d => Literal(d),
+ decimal m => Literal(m),
+ _ => Literal(raw.ToString()!)
+ };
+ }
+
+ ///
+ /// Extracts the raw value from an if it's a raw (primitive) value.
+ /// Returns null for non-raw values (lists, objects, streams).
+ ///
+ private static object? GetRawValue(InputExampleValue exampleValue)
+ {
+ if (exampleValue is InputExampleRawValue rawValue)
+ return rawValue.RawValue;
+ return null;
+ }
+ }
+}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs
new file mode 100644
index 00000000000..292a1882acc
--- /dev/null
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs
@@ -0,0 +1,755 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using Microsoft.TypeSpec.Generator.ClientModel.Primitives;
+using Microsoft.TypeSpec.Generator.Expressions;
+using Microsoft.TypeSpec.Generator.Input;
+using Microsoft.TypeSpec.Generator.Primitives;
+using Microsoft.TypeSpec.Generator.Providers;
+using Microsoft.TypeSpec.Generator.Statements;
+using static Microsoft.TypeSpec.Generator.Snippets.Snippet;
+
+namespace Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples
+{
+ ///
+ /// Represents a single sample for an operation — the bridge between an
+ /// and the generated C# sample method.
+ /// Resolves client construction chains, parameter value mappings, and method invocation details.
+ /// Modeled after the autorest DpgOperationSample pattern.
+ ///
+ [DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
+ public class OperationSample
+ {
+ private readonly ClientProvider _client;
+ private readonly ScmMethodProviderCollection _methodCollection;
+ private readonly InputServiceMethod _serviceMethod;
+ private readonly InputOperationExample _example;
+ private readonly MethodSignature _operationMethodSignature;
+
+ private IReadOnlyList? _clientInvocationChain;
+ private Dictionary? _parameterValueMapping;
+ private InputType? _resultType;
+
+ public OperationSample(
+ ClientProvider client,
+ ScmMethodProviderCollection methodCollection,
+ InputServiceMethod serviceMethod,
+ InputOperationExample example,
+ bool isConvenienceSample,
+ string exampleKey)
+ {
+ _client = client;
+ _methodCollection = methodCollection;
+ _serviceMethod = serviceMethod;
+ _example = example;
+ IsConvenienceSample = isConvenienceSample;
+ ExampleKey = exampleKey;
+ IsAllParametersUsed = exampleKey == "AllParameters";
+ _operationMethodSignature = ResolveOperationSignature();
+ }
+
+ // -------------------------------------------------------------------
+ // Core identity
+ // -------------------------------------------------------------------
+
+ ///
+ /// The example key, e.g. "ShortVersion" or "AllParameters".
+ ///
+ public string ExampleKey { get; }
+
+ ///
+ /// Whether this is a convenience method sample (true) or protocol method sample (false).
+ ///
+ public bool IsConvenienceSample { get; }
+
+ ///
+ /// Whether this sample uses all parameters (including optional).
+ ///
+ public bool IsAllParametersUsed { get; }
+
+ // -------------------------------------------------------------------
+ // Method info
+ // -------------------------------------------------------------------
+
+ ///
+ /// The input operation name.
+ ///
+ public string InputOperationName => _serviceMethod.Operation.Name;
+
+ ///
+ /// The resource name from the operation, or the client name as fallback.
+ /// Used for method naming.
+ ///
+ public string? ResourceName => _serviceMethod.Operation.ResourceName ?? _client.InputClient.Name;
+
+ ///
+ /// Whether the operation uses paging.
+ ///
+ public bool IsPageable => _serviceMethod is InputPagingServiceMethod;
+
+ ///
+ /// Whether the operation is long-running.
+ ///
+ public bool IsLongRunning => _serviceMethod is InputLongRunningServiceMethod;
+
+ ///
+ /// The method signature for the operation being sampled (protocol or convenience).
+ ///
+ public MethodSignature OperationMethodSignature => _operationMethodSignature;
+
+ ///
+ /// Whether there is a response body on the operation.
+ ///
+ public bool HasResponseBody => _serviceMethod.Response?.Type != null;
+
+ ///
+ /// Whether the response is a stream.
+ ///
+ public bool IsResponseStream =>
+ _serviceMethod.Response?.Type is InputPrimitiveType { Kind: InputPrimitiveTypeKind.Stream };
+
+ ///
+ /// The effective result type of the operation.
+ /// For paging, this is the item type; for all others this is the response type.
+ ///
+ public InputType? ResultType => _resultType ??= GetEffectiveResponseType();
+
+ private MethodSignature ResolveOperationSignature()
+ {
+ var kind = IsConvenienceSample ? ScmMethodKind.Convenience : ScmMethodKind.Protocol;
+ var method = _methodCollection.MethodProviders
+ .FirstOrDefault(m => m.Kind == kind && !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async));
+ return method?.Signature ?? _methodCollection.MethodProviders.First().Signature;
+ }
+
+ // -------------------------------------------------------------------
+ // Client invocation chain
+ // -------------------------------------------------------------------
+
+ ///
+ /// Ordered list of method signatures to invoke to construct the client:
+ /// [root ctor, .GetSubClient(), .GetLeafClient()].
+ ///
+ public IReadOnlyList ClientInvocationChain =>
+ _clientInvocationChain ??= BuildClientInvocationChain();
+
+ ///
+ /// Walks from the current client up to the root, collecting factory methods,
+ /// then pushes the root constructor. Returns the chain in top-down order.
+ ///
+ private IReadOnlyList BuildClientInvocationChain()
+ {
+ var callChain = new Stack();
+
+ // Walk from the current client up to root, collecting factory methods.
+ // For each client that has a parent, find the factory method on the parent
+ // that returns it, and push that onto the chain.
+ var currentInputClient = _client.InputClient;
+ while (currentInputClient.Parent != null)
+ {
+ var parentProvider = ResolveClientProvider(currentInputClient.Parent);
+ if (parentProvider != null)
+ {
+ var factoryMethod = FindSubClientFactoryMethod(parentProvider, currentInputClient.Name);
+ if (factoryMethod != null)
+ {
+ callChain.Push(factoryMethod);
+ }
+ }
+ currentInputClient = currentInputClient.Parent;
+ }
+
+ // At the root, push the primary public constructor
+ var rootProvider = ResolveClientProvider(currentInputClient);
+ if (rootProvider != null)
+ {
+ var ctor = GetPrimaryPublicConstructor(rootProvider);
+ if (ctor != null)
+ {
+ callChain.Push(ctor);
+ }
+ }
+
+ return callChain.ToArray();
+ }
+
+ private static ClientProvider? ResolveClientProvider(InputClient inputClient)
+ {
+ return ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient);
+ }
+
+ private static MethodSignature? FindSubClientFactoryMethod(ClientProvider parentProvider, string subClientName)
+ {
+ // Match the naming convention used by ClientProvider:
+ // If name ends with "Client" → "Get{name}", otherwise → "Get{name}Client"
+ var expectedMethodName = subClientName.EndsWith("Client", StringComparison.OrdinalIgnoreCase)
+ ? $"Get{subClientName}"
+ : $"Get{subClientName}Client";
+
+ foreach (var method in parentProvider.Methods)
+ {
+ if (method.Signature.Name.Equals(expectedMethodName, StringComparison.OrdinalIgnoreCase) &&
+ !method.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async))
+ {
+ return method.Signature;
+ }
+ }
+ return null;
+ }
+
+ private static ConstructorSignature? GetPrimaryPublicConstructor(ClientProvider clientProvider)
+ {
+ ConstructorSignature? best = null;
+ foreach (var ctor in clientProvider.Constructors)
+ {
+ if (ctor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public))
+ {
+ if (best == null || ctor.Signature.Parameters.Count > best.Parameters.Count)
+ {
+ best = ctor.Signature;
+ }
+ }
+ }
+ return best;
+ }
+
+ // -------------------------------------------------------------------
+ // Parameter value mapping
+ // -------------------------------------------------------------------
+
+ ///
+ /// Maps parameter names to their example values (either pre-built expressions or raw data).
+ ///
+ public IReadOnlyDictionary ParameterValueMapping =>
+ _parameterValueMapping ??= EnsureParameterValueMapping();
+
+ private Dictionary EnsureParameterValueMapping()
+ {
+ var result = new Dictionary();
+ var parameters = GetAllParameters();
+ var parameterExamples = GetAllParameterExamples();
+
+ foreach (var parameter in parameters)
+ {
+ if (result.ContainsKey(parameter.Name))
+ continue;
+
+ if (TryProcessKnownParameter(result, parameter))
+ continue;
+
+ // Find the corresponding example value
+ var exampleValue = FindExampleValueByName(parameterExamples, parameter.Name);
+
+ if (exampleValue == null && parameter.WireInfo?.SerializedName != null)
+ {
+ exampleValue = FindExampleValueByName(parameterExamples, parameter.WireInfo.SerializedName);
+ }
+
+ if (exampleValue != null)
+ {
+ result.Add(parameter.Name, new ExampleParameterValue(parameter.Name, parameter.Type, exampleValue));
+ }
+ else if (parameter.DefaultValue == null)
+ {
+ // Required parameter with no example — use default
+ var expression = DefaultOf(parameter.Type);
+ result.Add(parameter.Name, new ExampleParameterValue(parameter.Name, parameter.Type, expression));
+ }
+ // Optional parameters with no value are intentionally omitted
+ }
+
+ return result;
+ }
+
+ ///
+ /// Known parameters that receive special handling (endpoint, credentials, cancellation, etc).
+ ///
+ private bool TryProcessKnownParameter(Dictionary result, ParameterProvider parameter)
+ {
+ var type = parameter.Type;
+
+ // WaitUntil — prefer Completed to make long-running samples deterministic.
+ if (type.Name == "WaitUntil")
+ {
+ result[parameter.Name] = new ExampleParameterValue(
+ parameter.Name,
+ type,
+ Static(type).Property("Completed"));
+ return true;
+ }
+
+ // CancellationToken — skip entirely (we don't set it in samples)
+ if (parameter.Equals(ScmKnownParameters.CancellationToken))
+ {
+ return true;
+ }
+
+ // RequestOptions (required) — pass null explicitly to avoid ambiguity, similar
+ // to old RequestContextRequired handling in AutoRest.
+ if ((parameter.Equals(ScmKnownParameters.RequestOptions) || parameter.Equals(ScmKnownParameters.OptionalRequestOptions)) &&
+ parameter.DefaultValue == null)
+ {
+ result[parameter.Name] = new ExampleParameterValue(
+ parameter.Name,
+ type,
+ Null.CastTo(type));
+ return true;
+ }
+
+ // Endpoint (Uri) — use provided example value, otherwise a named placeholder like ""
+ if (type.Equals(typeof(Uri)) && parameter.InputParameter is InputEndpointParameter)
+ {
+ var endpointExpr = GetEndpointValue(parameter.Name);
+ result[parameter.Name] = new ExampleParameterValue(parameter.Name, type, endpointExpr);
+ return true;
+ }
+
+ // Request content (BinaryContent) — handled specially via body parameter lookup
+ if (parameter.IsContentParameter)
+ {
+ result[parameter.Name] = new ExampleParameterValue(parameter.Name, type, GetBodyParameterValue());
+ return true;
+ }
+
+ // Request conditions / match conditions — default to null in samples.
+ if (type.Name == "RequestConditions" ||
+ type.Name == "MatchConditions" ||
+ type.Equals(ScmCodeModelGenerator.Instance.TypeFactory.MatchConditionsType))
+ {
+ result[parameter.Name] = new ExampleParameterValue(
+ parameter.Name,
+ type,
+ Null.CastTo(type));
+ return true;
+ }
+
+ // ApiKeyCredential — produce `new ApiKeyCredential("")`
+ if (type.Equals(ScmKnownParameters.KeyCredential.Type) ||
+ type.Name == ScmKnownParameters.KeyCredential.Type.Name ||
+ type.Name == "AzureKeyCredential")
+ {
+ result[parameter.Name] = new ExampleParameterValue(
+ parameter.Name, type, New.Instance(type, Literal("")));
+ return true;
+ }
+
+ // TokenCredential — produce `new DefaultAzureCredential()`
+ // TokenCredential is abstract, so we must use a concrete type.
+ if (type.Equals(ScmKnownParameters.TokenCredential.Type) ||
+ type.Name == ScmKnownParameters.TokenCredential.Type.Name ||
+ type.Name == "TokenCredential")
+ {
+ // Use FormattableStringExpression because DefaultAzureCredential
+ // lives in Azure.Identity which the generator doesn't reference.
+ result[parameter.Name] = new ExampleParameterValue(
+ parameter.Name, type, new FormattableStringExpression("new DefaultAzureCredential()", []));
+ return true;
+ }
+
+ // ClientOptions — skip (optional, not needed in sample)
+ if (parameter.Name.EndsWith("Options", StringComparison.OrdinalIgnoreCase) && parameter.DefaultValue != null)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Returns all the parameters that should be used in this sample.
+ /// Only required parameters are included if is false.
+ ///
+ private IEnumerable GetAllParameters()
+ {
+ // Parameters from the client invocation chain (ctor + factory methods)
+ foreach (var method in ClientInvocationChain)
+ {
+ foreach (var parameter in method.Parameters)
+ yield return parameter;
+ }
+
+ // Parameters from the operation method itself
+ var operationParams = IsAllParametersUsed
+ ? _operationMethodSignature.Parameters
+ : _operationMethodSignature.Parameters.Where(p => p.DefaultValue == null);
+
+ foreach (var parameter in operationParams)
+ yield return parameter;
+ }
+
+ ///
+ /// Returns all parameter examples, handling spread parameters by extracting
+ /// individual properties from the model type.
+ ///
+ private IEnumerable GetAllParameterExamples()
+ {
+ foreach (var parameterExample in _example.Parameters)
+ {
+ var inputParameter = parameterExample.Parameter;
+
+ // For spread parameters, the example value contains properties of the model
+ if (inputParameter is InputMethodParameter { Scope: InputParameterScope.Spread } &&
+ inputParameter.Type is InputModelType modelType &&
+ parameterExample.ExampleValue is InputExampleObjectValue objectValue)
+ {
+ var properties = objectValue.Values;
+ foreach (var modelOrBase in GetSelfAndBaseModels(modelType))
+ {
+ foreach (var property in modelOrBase.Properties)
+ {
+ if (properties.TryGetValue(property.SerializedName, out var propValue))
+ {
+ // Create a synthetic parameter example for each spread property
+ var syntheticParam = new InputMethodParameter(
+ property.Name,
+ null, // summary
+ property.Doc,
+ property.Type,
+ property.IsRequired,
+ property.IsReadOnly,
+ null, // access
+ property.SerializedName,
+ false, // isApiVersion
+ null, // defaultValue
+ InputParameterScope.Method,
+ InputRequestLocation.Body);
+ yield return new InputParameterExample(syntheticParam, propValue);
+ }
+ }
+ }
+ }
+ else
+ {
+ yield return parameterExample;
+ }
+ }
+ }
+
+ ///
+ /// Searches for an example value by parameter name (case-sensitive).
+ ///
+ private static InputExampleValue? FindExampleValueByName(
+ IEnumerable parameterExamples,
+ string name)
+ {
+ foreach (var parameterExample in parameterExamples)
+ {
+ if (parameterExample.Parameter.Name == name)
+ {
+ return parameterExample.ExampleValue;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Gets the endpoint value, preferring the example value if available.
+ ///
+ private InputExampleValue GetEndpointValue(string parameterName)
+ {
+ // Try to find an endpoint value from the examples
+ var endpointExampleValue = _example.Parameters
+ .FirstOrDefault(e => e.Parameter is InputEndpointParameter)?.ExampleValue;
+
+ if (endpointExampleValue != null)
+ return endpointExampleValue;
+
+ // Fallback: placeholder
+ return InputExampleValue.Value(InputPrimitiveType.String, $"<{parameterName}>");
+ }
+
+ ///
+ /// Gets the body parameter value from the examples.
+ /// If there's a single body parameter example, use it. Otherwise search by type.
+ ///
+ private InputExampleValue GetBodyParameterValue()
+ {
+ var bodyParameters = _example.Parameters
+ .Where(e => e.Parameter is InputBodyParameter)
+ .ToArray();
+
+ if (bodyParameters.Length == 1)
+ {
+ return bodyParameters[0].ExampleValue;
+ }
+
+ // Check for any body-located method parameter
+ var bodyMethodParam = _example.Parameters
+ .FirstOrDefault(e => e.Parameter is InputMethodParameter { Location: InputRequestLocation.Body });
+
+ if (bodyMethodParam != null)
+ {
+ return bodyMethodParam.ExampleValue;
+ }
+
+ return InputExampleValue.Null(InputPrimitiveType.Any);
+ }
+
+ ///
+ /// Walks the model and all its base models.
+ ///
+ private static IEnumerable GetSelfAndBaseModels(InputModelType model)
+ {
+ var current = model;
+ while (current != null)
+ {
+ yield return current;
+ current = current.BaseModel;
+ }
+ }
+
+ // -------------------------------------------------------------------
+ // Expression generation
+ // -------------------------------------------------------------------
+
+ ///
+ /// Converts the parameter value mapping entries to instances
+ /// for a specific set of parameters. Complex types are declared as out-of-line variables,
+ /// while simple types are inlined directly.
+ ///
+ public IEnumerable GetValueExpressionsForParameters(
+ IEnumerable parameters,
+ List variableDeclarationStatements)
+ {
+ foreach (var parameter in parameters)
+ {
+ ValueExpression parameterExpression;
+
+ if (ParameterValueMapping.TryGetValue(parameter.Name, out var exampleValue))
+ {
+ var format = parameter.WireInfo?.SerializationFormat ?? SerializationFormat.Default;
+ parameterExpression = ExampleValueExpressionBuilder.GetExpression(exampleValue, format);
+ }
+ else
+ {
+ // No example value — skip optional, use default for required
+ if (parameter.DefaultValue != null)
+ continue;
+
+ parameterExpression = DefaultOf(parameter.Type);
+ }
+
+ if (IsInlineParameter(parameter))
+ {
+ yield return parameterExpression;
+ }
+ else
+ {
+ // Declare variable out-of-line
+ var varRef = new VariableExpression(parameter.Type, parameter.Name);
+ var declaration = NeedsDispose(parameter)
+ ? UsingDeclare(varRef, parameterExpression)
+ : Declare(varRef, parameterExpression);
+ variableDeclarationStatements.Add(declaration);
+ yield return varRef;
+ }
+ }
+ }
+
+ ///
+ /// Determines whether a parameter value should be inlined directly in the method call
+ /// or declared as a separate variable.
+ ///
+ private static bool IsInlineParameter(ParameterProvider parameter)
+ {
+ var type = parameter.Type;
+
+ // Content parameters (BinaryContent/RequestContent) → out-of-line
+ if (parameter.IsContentParameter)
+ return false;
+
+ // Endpoint → out-of-line
+ if (type.Equals(typeof(Uri)))
+ return false;
+
+ // Credentials → out-of-line
+ if (type.Equals(ScmKnownParameters.KeyCredential.Type) ||
+ type.Name == ScmKnownParameters.KeyCredential.Type.Name ||
+ type.Name == "AzureKeyCredential" ||
+ type.Equals(ScmKnownParameters.TokenCredential.Type) ||
+ type.Name == ScmKnownParameters.TokenCredential.Type.Name ||
+ type.Name == "TokenCredential")
+ return false;
+
+ // Model types (non-framework, non-enum, non-collection) → out-of-line
+ if (!type.IsFrameworkType && !type.IsEnum && !type.IsList && !type.IsDictionary)
+ return false;
+
+ // Everything else (primitives, enums, collections) → inline
+ return true;
+ }
+
+ ///
+ /// Determines whether a parameter needs a using declaration for disposal.
+ ///
+ private static bool NeedsDispose(ParameterProvider parameter)
+ {
+ return parameter.IsContentParameter;
+ }
+
+ // -------------------------------------------------------------------
+ // Response type resolution
+ // -------------------------------------------------------------------
+
+ ///
+ /// Returns the effective response type.
+ /// For paging operations, unwraps the item type from the response model.
+ /// For non-paging operations, returns the response type directly.
+ ///
+ private InputType? GetEffectiveResponseType()
+ {
+ var responseType = _serviceMethod.Response?.Type;
+
+ if (_serviceMethod is not InputPagingServiceMethod pagingMethod)
+ return responseType;
+
+ // For paging, try to unwrap the item type from the response model
+ var itemSegments = pagingMethod.PagingMetadata.ItemPropertySegments;
+ if (itemSegments.Count == 0 || responseType is not InputModelType responseModel)
+ return responseType;
+
+ // Walk the item property path to find the items array
+ InputType currentType = responseModel;
+ foreach (var segment in itemSegments)
+ {
+ if (currentType is not InputModelType currentModel)
+ break;
+
+ var property = currentModel.Properties
+ .FirstOrDefault(p => p.SerializedName == segment || p.Name == segment);
+ if (property == null)
+ break;
+
+ currentType = property.Type;
+ }
+
+ // If we found an array type at the end of the path, return the element type
+ if (currentType is InputArrayType arrayType)
+ return arrayType.ValueType;
+
+ return responseType;
+ }
+
+ // -------------------------------------------------------------------
+ // Static helpers for sample generation decisions
+ // -------------------------------------------------------------------
+
+ ///
+ /// Determines whether samples should be generated for the given method.
+ ///
+ public static bool ShouldGenerateSample(ClientProvider client, MethodSignature protocolSignature)
+ {
+ if (!protocolSignature.Modifiers.HasFlag(MethodSignatureModifiers.Public))
+ return false;
+
+ // Check for obsolete
+ if (protocolSignature.Attributes.Any(a => a.Type.Equals(typeof(ObsoleteAttribute))))
+ return false;
+
+ // Subclients are always valid; root clients need a public constructor
+ bool isSubClient = client.InputClient.Parent != null;
+ if (isSubClient)
+ return true;
+
+ return client.Constructors.Any(c =>
+ c.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public));
+ }
+
+ ///
+ /// Determines whether the ShortVersion sample should be generated.
+ /// If protocol and convenience signatures are effectively identical, skip ShortVersion for protocol
+ /// to avoid duplicate samples.
+ ///
+ public static bool ShouldGenerateShortVersion(ScmMethodProviderCollection methodCollection)
+ {
+ var protocolMethod = methodCollection.MethodProviders
+ .FirstOrDefault(m => m.Kind == ScmMethodKind.Protocol &&
+ !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async));
+ var convenienceMethod = methodCollection.MethodProviders
+ .FirstOrDefault(m => m.Kind == ScmMethodKind.Convenience &&
+ !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async));
+
+ if (protocolMethod == null || convenienceMethod == null)
+ return true;
+
+ var protocolParams = protocolMethod.Signature.Parameters;
+ var convenienceParams = convenienceMethod.Signature.Parameters;
+
+ // If the convenience method has one fewer parameter (e.g., no CancellationToken)
+ // and all overlapping parameter types match, skip — they're effectively identical.
+ if (convenienceParams.Count == protocolParams.Count - 1 && convenienceParams.Count > 0 &&
+ !convenienceParams.Last().Type.Equals(typeof(CancellationToken)))
+ {
+ bool allEqual = true;
+ for (int i = 0; i < convenienceParams.Count; i++)
+ {
+ if (!convenienceParams[i].Type.Equals(protocolParams[i].Type))
+ {
+ allEqual = false;
+ break;
+ }
+ }
+
+ if (allEqual)
+ return false;
+ }
+
+ return true;
+ }
+
+ // -------------------------------------------------------------------
+ // Sample information (human-readable descriptions)
+ // -------------------------------------------------------------------
+
+ ///
+ /// Gets a human-readable description of what the sample demonstrates.
+ ///
+ public string GetSampleInformation(bool isAsync)
+ {
+ var methodName = isAsync
+ ? _operationMethodSignature.Name + "Async"
+ : _operationMethodSignature.Name;
+
+ return IsConvenienceSample
+ ? GetSampleInformationForConvenience(methodName)
+ : GetSampleInformationForProtocol(methodName);
+ }
+
+ private string GetSampleInformationForConvenience(string methodName)
+ {
+ if (IsAllParametersUsed)
+ return $"This sample shows how to call {methodName} with all parameters.";
+ return $"This sample shows how to call {methodName}.";
+ }
+
+ private string GetSampleInformationForProtocol(string methodName)
+ {
+ if (IsAllParametersUsed)
+ {
+ var desc = GenerateParameterAndRequestContentDescription(_operationMethodSignature.Parameters);
+ var parseResult = HasResponseBody ? " and parse the result" : "";
+ return $"This sample shows how to call {methodName} with all {desc}{parseResult}.";
+ }
+ return $"This sample shows how to call {methodName}{(HasResponseBody ? " and parse the result" : "")}.";
+ }
+
+ private static string GenerateParameterAndRequestContentDescription(IReadOnlyList parameters)
+ {
+ var hasNonBodyParameter = parameters.Any(p =>
+ p.Location != ParameterLocation.Body && p.Name != "options");
+ var hasBodyParameter = parameters.Any(p => p.Location == ParameterLocation.Body);
+
+ if (hasNonBodyParameter)
+ return hasBodyParameter ? "parameters and request content" : "parameters";
+ return "request content";
+ }
+
+ private string GetDebuggerDisplay()
+ => $"Sample (Client: {_client.Name}, Method: {_operationMethodSignature.Name})";
+ }
+}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs
index c85805768e9..add87324523 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs
@@ -12,6 +12,7 @@
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.TypeSpec.Generator.ClientModel.Primitives;
+using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples;
using Microsoft.TypeSpec.Generator.ClientModel.Snippets;
using Microsoft.TypeSpec.Generator.ClientModel.Utilities;
using Microsoft.TypeSpec.Generator.EmitterRpc;
@@ -40,12 +41,14 @@ public class ScmMethodProviderCollection : IReadOnlyList
private IReadOnlyList? _convenienceMethodParameters;
private readonly InputPagingServiceMethod? _pagingServiceMethod;
private IReadOnlyList? _methods;
+ private IReadOnlyList? _samples;
private readonly bool _generateConvenienceMethod;
private ClientProvider Client { get; }
protected InputServiceMethod ServiceMethod { get; }
protected TypeProvider EnclosingType { get; }
public IReadOnlyList MethodProviders => _methods ??= BuildMethods();
+ public IReadOnlyList Samples => _samples ??= BuildSamples();
public ScmMethodProvider this[int index]
{
@@ -108,6 +111,47 @@ protected virtual IReadOnlyList BuildMethods()
];
}
+ protected virtual IReadOnlyList BuildSamples()
+ {
+ if (ServiceMethod.Operation.Examples.Count == 0)
+ {
+ return [];
+ }
+
+ var protocolMethod = MethodProviders.FirstOrDefault(m =>
+ m.Kind == ScmMethodKind.Protocol &&
+ !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async));
+
+ if (protocolMethod == null || !OperationSample.ShouldGenerateSample(Client, protocolMethod.Signature))
+ {
+ return [];
+ }
+
+ bool shouldGenerateShortVersion = OperationSample.ShouldGenerateShortVersion(this);
+ bool shouldGenerateConvenienceSamples = MethodProviders.Any(m =>
+ m.Kind == ScmMethodKind.Convenience &&
+ !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async) &&
+ m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public));
+
+ List samples = new();
+ foreach (var example in ServiceMethod.Operation.Examples)
+ {
+ if (!shouldGenerateShortVersion && example.Name == "ShortVersion")
+ {
+ continue;
+ }
+
+ samples.Add(new OperationSample(Client, this, ServiceMethod, example, false, example.Name));
+
+ if (shouldGenerateConvenienceSamples)
+ {
+ samples.Add(new OperationSample(Client, this, ServiceMethod, example, true, example.Name));
+ }
+ }
+
+ return samples;
+ }
+
private ScmMethodProvider BuildConvenienceMethod(MethodProvider protocolMethod, bool isAsync)
{
if (EnclosingType is not ClientProvider client)
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/ExampleValueExpressionBuilderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/ExampleValueExpressionBuilderTests.cs
new file mode 100644
index 00000000000..8daea57ac77
--- /dev/null
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/ExampleValueExpressionBuilderTests.cs
@@ -0,0 +1,624 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Xml;
+using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples;
+using Microsoft.TypeSpec.Generator.Expressions;
+using Microsoft.TypeSpec.Generator.Input;
+using Microsoft.TypeSpec.Generator.Primitives;
+using Microsoft.TypeSpec.Generator.Tests.Common;
+using NUnit.Framework;
+
+namespace Microsoft.TypeSpec.Generator.ClientModel.Tests.Providers.Samples
+{
+ public class ExampleValueExpressionBuilderTests
+ {
+ // -----------------------------------------------------------------------
+ // ExampleParameterValue — dual mode
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void ParameterValue_WithExpression_ReturnsExpression()
+ {
+ var expr = new LiteralExpression(42);
+ var paramValue = new ExampleParameterValue("count", new CSharpType(typeof(int)), expr);
+
+ var result = ExampleValueExpressionBuilder.GetExpression(paramValue);
+
+ Assert.AreSame(expr, result);
+ }
+
+ [Test]
+ public void ParameterValue_WithValue_ConvertsToExpression()
+ {
+ var inputValue = InputExampleValue.Value(InputPrimitiveType.Int32, 1234);
+ var paramValue = new ExampleParameterValue("count", new CSharpType(typeof(int)), inputValue);
+
+ var result = ExampleValueExpressionBuilder.GetExpression(paramValue);
+
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void ParameterValue_WithNeither_ReturnsDefault()
+ {
+ // Edge case: both Value and Expression are null (shouldn't happen in practice)
+ var paramValue = new ExampleParameterValue("x", new CSharpType(typeof(int)),
+ (InputExampleValue)null!);
+
+ // This will go through GetExpression with null value — should return Default
+ var result = ExampleValueExpressionBuilder.GetExpression(paramValue);
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Framework type conversions — primitives
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void String_FromRawValue()
+ {
+ var result = BuildExpression(typeof(string), InputExampleValue.Value(InputPrimitiveType.String, "hello"));
+ Assert.IsNotNull(result);
+ // Should not be null/default
+ Assert.IsNotInstanceOf(result);
+ }
+
+ [Test]
+ public void String_FromNull()
+ {
+ var result = BuildExpression(typeof(string), InputExampleValue.Null(InputPrimitiveType.String));
+ // Null string → Null keyword
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Bool_True()
+ {
+ var result = BuildExpression(typeof(bool), InputExampleValue.Value(InputPrimitiveType.Boolean, true));
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Int32_FromRawValue()
+ {
+ var result = BuildExpression(typeof(int), InputExampleValue.Value(InputPrimitiveType.Int32, 1234));
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Int64_FromRawValue()
+ {
+ var result = BuildExpression(typeof(long), InputExampleValue.Value(InputPrimitiveType.Int64, 1234L));
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Float_FromRawValue()
+ {
+ var result = BuildExpression(typeof(float), InputExampleValue.Value(InputPrimitiveType.Float32, 123.45f));
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Double_FromRawValue()
+ {
+ var result = BuildExpression(typeof(double), InputExampleValue.Value(InputPrimitiveType.Float64, 123.45));
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Decimal_FromRawValue()
+ {
+ var result = BuildExpression(typeof(decimal), InputExampleValue.Value(
+ new InputPrimitiveType(InputPrimitiveTypeKind.Decimal, "decimal", "TypeSpec.decimal"), 123.45m));
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Framework type conversions — complex types
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void Guid_FromString()
+ {
+ var result = BuildExpression(typeof(Guid),
+ InputExampleValue.Value(InputPrimitiveType.String, "73f411fe-4f43-4b4b-9cbd-6828d8f4cf9a"));
+
+ // Should produce Guid.Parse("...")
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void Uri_FromString()
+ {
+ var result = BuildExpression(typeof(Uri),
+ InputExampleValue.Value(InputPrimitiveType.Url, "http://localhost:3000"));
+
+ // Should produce new Uri("...")
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void DateTimeOffset_FromString()
+ {
+ var result = BuildExpression(typeof(DateTimeOffset),
+ InputExampleValue.Value(InputPrimitiveType.String, "2022-05-10T18:57:31.2311892Z"));
+
+ // Should produce DateTimeOffset.Parse("...")
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void DateTimeOffset_FromUnixTimestamp()
+ {
+ var result = BuildExpression(typeof(DateTimeOffset),
+ InputExampleValue.Value(InputPrimitiveType.Int64, 1652209051L));
+
+ // Should produce DateTimeOffset.FromUnixTimeSeconds(...)
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void TimeSpan_FromIso8601String()
+ {
+ var result = BuildExpression(typeof(TimeSpan),
+ InputExampleValue.Value(InputPrimitiveType.String, "PT1H23M45S"));
+
+ // Should produce XmlConvert.ToTimeSpan("...")
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void TimeSpan_FromSeconds()
+ {
+ var result = BuildExpression(typeof(TimeSpan),
+ InputExampleValue.Value(InputPrimitiveType.Float64, 10.5));
+
+ // Should produce TimeSpan.FromSeconds(...)
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void BinaryData_FromObject()
+ {
+ var objValue = InputExampleValue.Object(
+ InputFactory.Model("TestModel"),
+ new Dictionary
+ {
+ ["name"] = InputExampleValue.Value(InputPrimitiveType.String, "test")
+ });
+
+ var result = BuildExpression(typeof(BinaryData), objValue);
+
+ // Should produce BinaryData.FromObjectAsJson(new { name = "test" })
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void ByteArray_FromString()
+ {
+ var result = BuildExpression(typeof(byte[]),
+ InputExampleValue.Value(InputPrimitiveType.String, "dGVzdA=="));
+
+ // Should produce Encoding.UTF8.GetBytes("...")
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void Stream_FromStreamValue()
+ {
+ var result = BuildExpression(typeof(Stream),
+ InputExampleValue.Stream(InputPrimitiveType.String, ""));
+
+ // Should produce File.OpenRead("...")
+ Assert.IsInstanceOf(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Collections
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void List_FromListValue()
+ {
+ var listType = new CSharpType(typeof(IList<>), new CSharpType(typeof(int)));
+ var listValue = InputExampleValue.List(
+ new InputArrayType("list", "TypeSpec.Array", InputPrimitiveType.Int32),
+ new[] { InputExampleValue.Value(InputPrimitiveType.Int32, 1234) });
+
+ var result = ExampleValueExpressionBuilder.GetExpression(listType, listValue);
+
+ // New.Array returns IndexableExpression wrapping NewArrayExpression
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Dictionary_FromObjectValue()
+ {
+ var dictType = new CSharpType(typeof(IDictionary<,>), new CSharpType(typeof(string)), new CSharpType(typeof(int)));
+ var dictValue = InputExampleValue.Object(
+ new InputDictionaryType("dict", InputPrimitiveType.String, InputPrimitiveType.Int32),
+ new Dictionary
+ {
+ ["key"] = InputExampleValue.Value(InputPrimitiveType.Int32, 42)
+ });
+
+ var result = ExampleValueExpressionBuilder.GetExpression(dictType, dictValue);
+
+ // New.Dictionary returns DictionaryExpression wrapping NewInstanceExpression
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Enum
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void Enum_ProducesMemberAccess()
+ {
+ // Use a real framework enum type for testing
+ var enumType = new CSharpType(typeof(DayOfWeek));
+ var value = InputExampleValue.Value(
+ InputFactory.StringEnum("DayOfWeek", [("Monday", "Monday")]), "Monday");
+
+ var result = ExampleValueExpressionBuilder.GetExpression(enumType, value);
+
+ // Should produce a member expression like DayOfWeek.Monday
+ Assert.IsInstanceOf(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Model (basic)
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void Model_ProducesNewInstance()
+ {
+ var modelType = new CSharpType(typeof(object));
+ var value = InputExampleValue.Object(
+ InputFactory.Model("Widget"),
+ new Dictionary());
+
+ var result = ExampleValueExpressionBuilder.GetExpression(modelType, value);
+
+ // Non-collection, non-enum framework type falls through to framework handler
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Anonymous object (for BinaryContent)
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void AnonymousObject_FromNestedValues()
+ {
+ var objValue = InputExampleValue.Object(
+ InputFactory.Model("Request"),
+ new Dictionary
+ {
+ ["name"] = InputExampleValue.Value(InputPrimitiveType.String, "test"),
+ ["count"] = InputExampleValue.Value(InputPrimitiveType.Int32, 5)
+ });
+
+ var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(objValue);
+
+ // Should produce new { name = "test", count = 5 }
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void AnonymousObject_SkipsNullValues()
+ {
+ var objValue = InputExampleValue.Object(
+ InputFactory.Model("Request"),
+ new Dictionary
+ {
+ ["name"] = InputExampleValue.Value(InputPrimitiveType.String, "test"),
+ ["nullable"] = InputExampleValue.Null(InputPrimitiveType.String)
+ });
+
+ var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(objValue);
+
+ // Should produce new { name = "test" } — nullable skipped
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void AnonymousObject_EmptyObject()
+ {
+ var objValue = InputExampleValue.Object(
+ InputFactory.Model("Empty"),
+ new Dictionary());
+
+ var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(objValue);
+
+ // Empty object → new object() (wrapped in ScopedApi)
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Null / default fallbacks
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void Null_ValueType_ReturnsDefault()
+ {
+ var result = BuildExpression(typeof(int), InputExampleValue.Null(InputPrimitiveType.Int32));
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void Null_ReferenceType_ReturnsNull()
+ {
+ var result = BuildExpression(typeof(string), InputExampleValue.Null(InputPrimitiveType.String));
+ Assert.IsInstanceOf(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Missing primitive types — cast expressions
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void Short_FromRawValue()
+ {
+ var result = BuildExpression(typeof(short), InputExampleValue.Value(
+ new InputPrimitiveType(InputPrimitiveTypeKind.Int16, "int16", "TypeSpec.int16"), (short)1234));
+ Assert.IsNotNull(result);
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void SByte_FromRawValue()
+ {
+ var result = BuildExpression(typeof(sbyte), InputExampleValue.Value(
+ new InputPrimitiveType(InputPrimitiveTypeKind.Int8, "int8", "TypeSpec.int8"), (sbyte)123));
+ Assert.IsNotNull(result);
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void Byte_FromRawValue()
+ {
+ var result = BuildExpression(typeof(byte), InputExampleValue.Value(
+ new InputPrimitiveType(InputPrimitiveTypeKind.UInt8, "uint8", "TypeSpec.uint8"), (byte)123));
+ Assert.IsNotNull(result);
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void UShort_FromRawValue()
+ {
+ var result = BuildExpression(typeof(ushort), InputExampleValue.Value(
+ new InputPrimitiveType(InputPrimitiveTypeKind.UInt16, "uint16", "TypeSpec.uint16"), (ushort)1234));
+ Assert.IsNotNull(result);
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void UInt_FromRawValue()
+ {
+ var result = BuildExpression(typeof(uint), InputExampleValue.Value(
+ new InputPrimitiveType(InputPrimitiveTypeKind.UInt32, "uint32", "TypeSpec.uint32"), (uint)1234));
+ Assert.IsNotNull(result);
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void ULong_FromRawValue()
+ {
+ var result = BuildExpression(typeof(ulong), InputExampleValue.Value(
+ new InputPrimitiveType(InputPrimitiveTypeKind.UInt64, "uint64", "TypeSpec.uint64"), (ulong)1234));
+ Assert.IsNotNull(result);
+ Assert.IsInstanceOf(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Null paths for complex types
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void Bool_Null_ReturnsDefault()
+ {
+ var result = BuildExpression(typeof(bool), InputExampleValue.Null(InputPrimitiveType.Boolean));
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void Guid_Null_ReturnsDefault()
+ {
+ var result = BuildExpression(typeof(Guid), InputExampleValue.Null(InputPrimitiveType.String));
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void Uri_Null_ReturnsNull()
+ {
+ var result = BuildExpression(typeof(Uri), InputExampleValue.Null(InputPrimitiveType.String));
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void DateTimeOffset_Null_ReturnsDefault()
+ {
+ var result = BuildExpression(typeof(DateTimeOffset), InputExampleValue.Null(InputPrimitiveType.String));
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void TimeSpan_Null_ReturnsDefault()
+ {
+ var result = BuildExpression(typeof(TimeSpan), InputExampleValue.Null(InputPrimitiveType.String));
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void ByteArray_Null_ReturnsNull()
+ {
+ var result = BuildExpression(typeof(byte[]), InputExampleValue.Null(InputPrimitiveType.String));
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Stream_NonStreamValue_ReturnsNull()
+ {
+ var result = BuildExpression(typeof(Stream), InputExampleValue.Value(InputPrimitiveType.String, "notastream"));
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Enum edge case
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void Enum_Null_ReturnsDefault()
+ {
+ var enumType = new CSharpType(typeof(DayOfWeek));
+ var value = InputExampleValue.Null(InputFactory.StringEnum("DayOfWeek", [("Monday", "Monday")]));
+
+ var result = ExampleValueExpressionBuilder.GetExpression(enumType, value);
+
+ Assert.IsInstanceOf(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Collection edge cases
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void List_FromNonListValue_ReturnsEmptyArray()
+ {
+ var listType = new CSharpType(typeof(IList<>), new CSharpType(typeof(int)));
+ var nonListValue = InputExampleValue.Value(InputPrimitiveType.Int32, 1234);
+
+ var result = ExampleValueExpressionBuilder.GetExpression(listType, nonListValue);
+
+ // Should produce an empty array
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Dictionary_FromNonObjectValue_ReturnsEmptyDictionary()
+ {
+ var dictType = new CSharpType(typeof(IDictionary<,>), new CSharpType(typeof(string)), new CSharpType(typeof(int)));
+ var nonObjValue = InputExampleValue.Value(InputPrimitiveType.Int32, 1234);
+
+ var result = ExampleValueExpressionBuilder.GetExpression(dictType, nonObjValue);
+
+ // Should produce an empty dictionary
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Model edge case
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void Model_ValueType_ReturnsDefault()
+ {
+ var valueType = new CSharpType(typeof(int)); // value type but not enum/collection/known
+ var value = InputExampleValue.Object(
+ InputFactory.Model("Widget"),
+ new Dictionary());
+
+ // int is a framework type, so it goes to framework handler, not model handler
+ var result = ExampleValueExpressionBuilder.GetExpression(valueType, value);
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Anonymous object edge cases
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void AnonymousObject_WithNestedList()
+ {
+ var listValue = InputExampleValue.List(
+ new InputArrayType("list", "TypeSpec.Array", InputPrimitiveType.String),
+ new[] { InputExampleValue.Value(InputPrimitiveType.String, "item1") });
+
+ var objValue = InputExampleValue.Object(
+ InputFactory.Model("Request"),
+ new Dictionary
+ {
+ ["items"] = listValue
+ });
+
+ var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(objValue);
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void AnonymousObject_WithNestedObject()
+ {
+ var innerObj = InputExampleValue.Object(
+ InputFactory.Model("Inner"),
+ new Dictionary
+ {
+ ["innerProp"] = InputExampleValue.Value(InputPrimitiveType.Int32, 42)
+ });
+
+ var outerObj = InputExampleValue.Object(
+ InputFactory.Model("Outer"),
+ new Dictionary
+ {
+ ["nested"] = innerObj
+ });
+
+ var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(outerObj);
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void AnonymousObject_FromRawPrimitive()
+ {
+ var rawValue = InputExampleValue.Value(InputPrimitiveType.String, "hello");
+
+ var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(rawValue);
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void AnonymousObject_FromNullRaw()
+ {
+ var nullValue = InputExampleValue.Null(InputPrimitiveType.String);
+
+ var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(nullValue);
+ // Should return Null keyword
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Fallback for unknown framework types
+ // -----------------------------------------------------------------------
+
+ [Test]
+ public void UnknownValueType_ReturnsDefault()
+ {
+ // A value type that doesn't match any specific handler
+ var result = BuildExpression(typeof(DateTime), InputExampleValue.Null(InputPrimitiveType.String));
+ Assert.IsInstanceOf(result);
+ }
+
+ [Test]
+ public void UnknownReferenceType_ReturnsNull()
+ {
+ // A reference type that doesn't match any specific handler
+ var result = BuildExpression(typeof(System.Text.StringBuilder), InputExampleValue.Null(InputPrimitiveType.String));
+ Assert.IsNotNull(result);
+ }
+
+ // -----------------------------------------------------------------------
+ // Helpers
+ // -----------------------------------------------------------------------
+
+ private static ValueExpression BuildExpression(Type frameworkType, InputExampleValue value)
+ {
+ return ExampleValueExpressionBuilder.GetExpression(new CSharpType(frameworkType), value);
+ }
+ }
+}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs
new file mode 100644
index 00000000000..dcec8a22bbf
--- /dev/null
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs
@@ -0,0 +1,387 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using System.Linq;
+using System;
+using System.Reflection;
+using Microsoft.TypeSpec.Generator.ClientModel.Providers;
+using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples;
+using Microsoft.TypeSpec.Generator.Expressions;
+using Microsoft.TypeSpec.Generator.Input;
+using Microsoft.TypeSpec.Generator.Primitives;
+using Microsoft.TypeSpec.Generator.Snippets;
+using Microsoft.TypeSpec.Generator.Tests.Common;
+using NUnit.Framework;
+
+namespace Microsoft.TypeSpec.Generator.ClientModel.Tests.Providers.Samples
+{
+ public class OperationSampleTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ MockHelpers.LoadMockGenerator();
+ }
+
+ // -------------------------------------------------------------------
+ // TokenCredential → new DefaultAzureCredential()
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void TokenCredential_ProducesDefaultAzureCredential()
+ {
+ var sample = CreateSampleWithClientParameterType("credential", new CSharpType(typeof(TokenCredential)));
+ var mapping = sample.ParameterValueMapping;
+
+ Assert.IsTrue(mapping.ContainsKey("credential"));
+
+ // The expression should be a FormattableStringExpression containing "new DefaultAzureCredential()"
+ // (not "new TokenCredential()" which would fail because TokenCredential is abstract)
+ var expr = ExampleValueExpressionBuilder.GetExpression(mapping["credential"]);
+ Assert.IsInstanceOf(expr);
+
+ // Verify the format string is exactly "new DefaultAzureCredential()" via reflection
+ // (Format is private, but this is the key parity assertion)
+ var formatProp = typeof(FormattableStringExpression).GetProperty("Format", BindingFlags.NonPublic | BindingFlags.Instance);
+ Assert.IsNotNull(formatProp, "Format property should exist on FormattableStringExpression");
+ Assert.AreEqual("new DefaultAzureCredential()", formatProp!.GetValue(expr));
+ }
+
+ // -------------------------------------------------------------------
+ // ApiKeyCredential → new AzureKeyCredential("")
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void ApiKeyCredential_ProducesNewWithKeyPlaceholder()
+ {
+ var sample = CreateSampleWithClientParameterType("credential", new CSharpType(typeof(AzureKeyCredential)));
+ var mapping = sample.ParameterValueMapping;
+
+ Assert.IsTrue(mapping.ContainsKey("credential"));
+
+ var expr = Unwrap(UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["credential"])));
+ Assert.IsInstanceOf(expr);
+
+ var newExpr = (NewInstanceExpression)expr;
+ Assert.AreEqual(1, newExpr.Parameters.Count);
+ // The argument should be the literal ""
+ var argExpr = Unwrap(newExpr.Parameters[0]);
+ Assert.IsInstanceOf(argExpr);
+ Assert.AreEqual("", ((LiteralExpression)argExpr).Literal);
+ }
+
+ // -------------------------------------------------------------------
+ // Endpoint value resolution
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void Endpoint_WithoutExample_UsesPlaceholder()
+ {
+ var sample = CreateBasicSample("ShortVersion");
+ var endpointValue = InvokeGetEndpointValue(sample, "endpoint");
+ Assert.AreEqual("", GetRawExampleValue(endpointValue));
+ }
+
+ [Test]
+ public void Endpoint_WithExample_UsesProvidedUrl()
+ {
+ const string url = "https://example.contoso.test";
+ var sample = CreateSampleWithEndpointExample("ShortVersion", url);
+ var endpointValue = InvokeGetEndpointValue(sample, "endpoint");
+ Assert.AreEqual(url, GetRawExampleValue(endpointValue));
+ }
+
+ // -------------------------------------------------------------------
+ // Client invocation chain — root client
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void RootClient_ChainHasConstructor()
+ {
+ var sample = CreateBasicSample("ShortVersion");
+ var chain = sample.ClientInvocationChain;
+
+ Assert.IsTrue(chain.Count >= 1);
+ Assert.IsInstanceOf(chain[0]);
+ }
+
+ // -------------------------------------------------------------------
+ // Client invocation chain — subclient
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void SubClient_ChainHasConstructorThenFactory()
+ {
+ var (sample, _, _) = CreateSubClientSample("ShortVersion");
+ var chain = sample.ClientInvocationChain;
+
+ Assert.IsTrue(chain.Count >= 2);
+ Assert.IsInstanceOf(chain[0], "First element should be root ctor");
+ Assert.IsInstanceOf(chain[1], "Second element should be factory method");
+ }
+
+ // -------------------------------------------------------------------
+ // Parameter mapping
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void ParameterMapping_ExampleValue_IsMapped()
+ {
+ var bodyParam = InputFactory.MethodParameter("message", InputPrimitiveType.String, isRequired: true);
+ var example = new InputOperationExample(
+ "ShortVersion",
+ null,
+ [new InputParameterExample(bodyParam, InputExampleValue.Value(InputPrimitiveType.String, "hello"))],
+ "");
+
+ var operation = InputFactory.Operation("SendMessage",
+ parameters: [InputFactory.BodyParameter("message", InputPrimitiveType.String, isRequired: true)]);
+ var serviceMethod = InputFactory.BasicServiceMethod("SendMessage", operation, parameters: [bodyParam]);
+ var inputClient = InputFactory.Client("MsgClient", methods: [serviceMethod]);
+
+ MockHelpers.LoadMockGenerator(
+ createCSharpTypeCore: (inputType) => new CSharpType(typeof(string)));
+
+ var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
+ var methodCollection = new ScmMethodProviderCollection(serviceMethod, client);
+ var sample = new OperationSample(client, methodCollection, serviceMethod, example, true, "ShortVersion");
+
+ var mapping = sample.ParameterValueMapping;
+ Assert.IsTrue(mapping.ContainsKey("message"));
+ Assert.IsNotNull(mapping["message"].Value);
+ }
+
+ // -------------------------------------------------------------------
+ // Optional params excluded from ShortVersion
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void ParameterMapping_OptionalParam_ExcludedFromShortVersion()
+ {
+ var optionalParam = InputFactory.MethodParameter("tag", InputPrimitiveType.String, isRequired: false,
+ defaultValue: InputFactory.Constant.String("default"));
+ var example = new InputOperationExample("ShortVersion", null, [], "");
+
+ var operation = InputFactory.Operation("GetItem",
+ parameters: [InputFactory.QueryParameter("tag", InputPrimitiveType.String, isRequired: false)]);
+ var serviceMethod = InputFactory.BasicServiceMethod("GetItem", operation, parameters: [optionalParam]);
+ var inputClient = InputFactory.Client("ItemClient", methods: [serviceMethod]);
+
+ MockHelpers.LoadMockGenerator(
+ createCSharpTypeCore: (inputType) => new CSharpType(typeof(string)));
+
+ var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
+ var methodCollection = new ScmMethodProviderCollection(serviceMethod, client);
+ var sample = new OperationSample(client, methodCollection, serviceMethod, example, true, "ShortVersion");
+
+ Assert.IsFalse(sample.ParameterValueMapping.ContainsKey("tag"));
+ }
+
+ // -------------------------------------------------------------------
+ // WaitUntil → .Completed
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void WaitUntil_ProducesCompletedMemberAccess()
+ {
+ var sample = CreateSampleWithClientParameterType("waitUntil", new CSharpType(typeof(WaitUntil)));
+ var mapping = sample.ParameterValueMapping;
+
+ Assert.IsTrue(mapping.ContainsKey("waitUntil"));
+ var expr = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["waitUntil"]));
+ // Should be a property access like WaitUntil.Completed
+ Assert.IsInstanceOf(expr);
+ }
+
+ // -------------------------------------------------------------------
+ // RequestOptions (required) → null
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void RequiredRequestOptions_ProducesNull()
+ {
+ var sample = CreateSampleWithClientParameterType(
+ "options",
+ new CSharpType(typeof(System.ClientModel.Primitives.RequestOptions)),
+ isOptional: false,
+ defaultValue: null);
+ var mapping = sample.ParameterValueMapping;
+
+ Assert.IsTrue(mapping.ContainsKey("options"));
+ var expr = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["options"]));
+ Assert.IsInstanceOf(expr);
+ }
+
+ // -------------------------------------------------------------------
+ // MatchConditions → null
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void MatchConditions_ProducesNull()
+ {
+ var sample = CreateSampleWithClientParameterType("conditions", new CSharpType(typeof(MatchConditions)));
+ var mapping = sample.ParameterValueMapping;
+
+ Assert.IsTrue(mapping.ContainsKey("conditions"));
+ var expr = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["conditions"]));
+ Assert.IsInstanceOf(expr);
+ }
+
+ // -------------------------------------------------------------------
+ // ShouldGenerateShortVersion
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void ShouldGenerateShortVersion_TrueWhenNoConvenience()
+ {
+ var operation = InputFactory.Operation("TestOp", generateConvenienceMethod: false);
+ var serviceMethod = InputFactory.BasicServiceMethod("TestOp", operation);
+ var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]);
+
+ var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
+ var methodCollection = new ScmMethodProviderCollection(serviceMethod, client);
+
+ Assert.IsTrue(OperationSample.ShouldGenerateShortVersion(methodCollection));
+ }
+
+ // -------------------------------------------------------------------
+ // Sample metadata
+ // -------------------------------------------------------------------
+
+ [Test]
+ public void SampleInformation_ContainsMethodName()
+ {
+ var sample = CreateBasicSample("ShortVersion", isConvenience: true);
+ var info = sample.GetSampleInformation(false);
+ Assert.IsTrue(info.Contains("how to call"));
+ }
+
+ [Test]
+ public void SampleInformation_AsyncAppendsAsync()
+ {
+ var sample = CreateBasicSample("ShortVersion", isConvenience: true);
+ var info = sample.GetSampleInformation(true);
+ Assert.IsTrue(info.Contains("Async"));
+ }
+
+ // -------------------------------------------------------------------
+ // Helpers
+ // -------------------------------------------------------------------
+
+ private static OperationSample CreateBasicSample(string exampleKey, bool isConvenience = false)
+ {
+ var operation = InputFactory.Operation("TestOperation");
+ var serviceMethod = InputFactory.BasicServiceMethod("TestOperation", operation);
+ var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]);
+
+ var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
+ var methodCollection = new ScmMethodProviderCollection(serviceMethod, client);
+ var example = new InputOperationExample(exampleKey, null, [], "");
+
+ return new OperationSample(client, methodCollection, serviceMethod, example, isConvenience, exampleKey);
+ }
+
+ private static (OperationSample sample, ClientProvider rootClient, ClientProvider subClient) CreateSubClientSample(string exampleKey)
+ {
+ var endpointParam = InputFactory.EndpointParameter("endpoint", InputPrimitiveType.Url, isRequired: true);
+ var parentClient = InputFactory.Client("RootClient", parameters: [endpointParam]);
+ var subOperation = InputFactory.Operation("DoWork");
+ var subServiceMethod = InputFactory.BasicServiceMethod("DoWork", subOperation);
+ var subClient = InputFactory.Client("SubClient", parent: parentClient,
+ methods: [subServiceMethod],
+ initializedBy: InputClientInitializedBy.Parent);
+
+ MockHelpers.LoadMockGenerator(clients: () => [parentClient, subClient]);
+
+ var rootProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(parentClient)!;
+ var subProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(subClient)!;
+ var methodCollection = new ScmMethodProviderCollection(subServiceMethod, subProvider);
+ var example = new InputOperationExample(exampleKey, null, [], "");
+
+ var sample = new OperationSample(subProvider, methodCollection, subServiceMethod, example, false, exampleKey);
+ return (sample, rootProvider, subProvider);
+ }
+
+ private static OperationSample CreateSampleWithEndpointExample(string exampleKey, string endpointExample)
+ {
+ var endpointParam = InputFactory.EndpointParameter("endpoint", InputPrimitiveType.Url, isRequired: true);
+ var operation = InputFactory.Operation("TestOperation");
+ var serviceMethod = InputFactory.BasicServiceMethod("TestOperation", operation);
+ var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]);
+
+ MockHelpers.LoadMockGenerator();
+
+ var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
+ var methodCollection = new ScmMethodProviderCollection(serviceMethod, client);
+ var example = new InputOperationExample(
+ exampleKey,
+ null,
+ [new InputParameterExample(endpointParam, InputExampleValue.Value(InputPrimitiveType.Url, endpointExample))],
+ "");
+
+ return new OperationSample(client, methodCollection, serviceMethod, example, false, exampleKey);
+ }
+
+ private static OperationSample CreateSampleWithClientParameterType(
+ string parameterName,
+ CSharpType mappedType,
+ bool isOptional = false,
+ InputConstant? defaultValue = null)
+ {
+ var clientParameter = InputFactory.QueryParameter(
+ parameterName,
+ InputPrimitiveType.String,
+ isRequired: !isOptional,
+ defaultValue: defaultValue);
+ var operation = InputFactory.Operation("DoWork");
+ var serviceMethod = InputFactory.BasicServiceMethod("DoWork", operation);
+ var inputClient = InputFactory.Client("TypedClient", parameters: [clientParameter], methods: [serviceMethod]);
+
+ MockHelpers.LoadMockGenerator(
+ clients: () => [inputClient],
+ createCSharpTypeCore: _ => mappedType);
+
+ var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
+ var methodCollection = new ScmMethodProviderCollection(serviceMethod, client);
+ var example = new InputOperationExample("ShortVersion", null, [], "");
+
+ return new OperationSample(client, methodCollection, serviceMethod, example, false, "ShortVersion");
+ }
+
+ // Stub types for credential/LRO tests (matched by type name in TryProcessKnownParameter)
+ private sealed class TokenCredential { }
+ private sealed class AzureKeyCredential { }
+ private sealed class WaitUntil
+ {
+ public static WaitUntil Completed { get; } = new WaitUntil();
+ }
+ private sealed class MatchConditions { }
+
+ private static object? GetRawExampleValue(InputExampleValue value)
+ {
+ var rawValueProperty = value.GetType().GetProperty("RawValue");
+ return rawValueProperty?.GetValue(value);
+ }
+
+ private static ValueExpression UnwrapCast(ValueExpression expression)
+ {
+ while (expression is CastExpression castExpression)
+ expression = castExpression.Inner;
+ return expression;
+ }
+
+ /// Unwrap ScopedApi wrappers to get the underlying expression.
+ private static ValueExpression Unwrap(ValueExpression expr)
+ => expr is ScopedApi scoped ? scoped.Original : expr;
+
+ private static InputExampleValue InvokeGetEndpointValue(OperationSample sample, string parameterName)
+ {
+ var method = typeof(OperationSample).GetMethod("GetEndpointValue", BindingFlags.Instance | BindingFlags.NonPublic);
+ Assert.IsNotNull(method);
+ var value = method!.Invoke(sample, [parameterName]);
+ Assert.IsNotNull(value);
+ return (InputExampleValue)value!;
+ }
+ }
+}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs
index b54ece36bd0..d23513876fe 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs
@@ -6,10 +6,12 @@
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.TypeSpec.Generator.ClientModel.Providers;
+using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples;
using Microsoft.TypeSpec.Generator.Input;
using Microsoft.TypeSpec.Generator.Input.Extensions;
using Microsoft.TypeSpec.Generator.Primitives;
@@ -1996,5 +1998,82 @@ public void ConvenienceMethod_JsonListBody_DoesNotUseXmlFromEnumerable()
Assert.IsFalse(methodBody.Contains("rootNameHint"));
Assert.IsFalse(methodBody.Contains("childNameHint"));
}
+
+ [Test]
+ public void Samples_AttachesProtocolAndConvenienceSamplesForEachExample()
+ {
+ var shortVersion = new InputOperationExample("ShortVersion", null, [], "");
+ var allParameters = new InputOperationExample("AllParameters", null, [], "");
+ var operation = InputFactory.Operation("GetWidget");
+ SetOperationExamples(operation, [shortVersion, allParameters]);
+ var serviceMethod = InputFactory.BasicServiceMethod("GetWidget", operation);
+ var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]);
+
+ MockHelpers.LoadMockGenerator(clients: () => [inputClient]);
+
+ var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
+ var methodCollection = new ScmMethodProviderCollection(serviceMethod, client);
+
+ var expectedCount = OperationSample.ShouldGenerateShortVersion(methodCollection) ? 4 : 3;
+ Assert.AreEqual(expectedCount, methodCollection.Samples.Count);
+ Assert.IsTrue(methodCollection.Samples.Any(s => !s.IsConvenienceSample && s.ExampleKey == "AllParameters"));
+ Assert.IsTrue(methodCollection.Samples.Any(s => s.IsConvenienceSample && s.ExampleKey == "AllParameters"));
+ Assert.IsTrue(methodCollection.Samples.Any(s => s.IsConvenienceSample && s.ExampleKey == "ShortVersion") ||
+ !OperationSample.ShouldGenerateShortVersion(methodCollection));
+
+ if (OperationSample.ShouldGenerateShortVersion(methodCollection))
+ {
+ Assert.IsTrue(methodCollection.Samples.Any(s => !s.IsConvenienceSample && s.ExampleKey == "ShortVersion"));
+ }
+ else
+ {
+ Assert.IsFalse(methodCollection.Samples.Any(s => !s.IsConvenienceSample && s.ExampleKey == "ShortVersion"));
+ }
+ }
+
+ [Test]
+ public void Samples_DoesNotAttachConvenienceSamplesWhenConvenienceMethodIsDisabled()
+ {
+ var operation = InputFactory.Operation("GetWidget", generateConvenienceMethod: false);
+ SetOperationExamples(
+ operation,
+ [
+ new InputOperationExample("ShortVersion", null, [], ""),
+ new InputOperationExample("AllParameters", null, [], "")
+ ]);
+ var serviceMethod = InputFactory.BasicServiceMethod("GetWidget", operation);
+ var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]);
+
+ MockHelpers.LoadMockGenerator(clients: () => [inputClient]);
+
+ var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
+ var methodCollection = new ScmMethodProviderCollection(serviceMethod, client);
+
+ Assert.AreEqual(2, methodCollection.Samples.Count);
+ Assert.IsTrue(methodCollection.Samples.All(s => !s.IsConvenienceSample));
+ }
+
+ [Test]
+ public void Samples_WithoutExamples_ReturnsEmpty()
+ {
+ var operation = InputFactory.Operation("GetWidget");
+ var serviceMethod = InputFactory.BasicServiceMethod("GetWidget", operation);
+ var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]);
+
+ MockHelpers.LoadMockGenerator(clients: () => [inputClient]);
+
+ var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
+ var methodCollection = new ScmMethodProviderCollection(serviceMethod, client);
+
+ Assert.IsEmpty(methodCollection.Samples);
+ }
+
+ private static void SetOperationExamples(InputOperation operation, IReadOnlyList examples)
+ {
+ var setter = typeof(InputOperation)
+ .GetProperty(nameof(InputOperation.Examples), BindingFlags.Instance | BindingFlags.Public)!
+ .GetSetMethod(nonPublic: true)!;
+ setter.Invoke(operation, [examples]);
+ }
}
}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ScmKnownParametersTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ScmKnownParametersTests.cs
index 40e7c9e92bd..06b74024358 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ScmKnownParametersTests.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ScmKnownParametersTests.cs
@@ -4,6 +4,7 @@
using System;
using Microsoft.TypeSpec.Generator.ClientModel.Primitives;
using Microsoft.TypeSpec.Generator.Primitives;
+using Microsoft.TypeSpec.Generator.Providers;
using NUnit.Framework;
using static Microsoft.TypeSpec.Generator.Snippets.Snippet;
@@ -40,5 +41,17 @@ public void RepeatabilityFirstSentParamHasDefaultValue()
var expectedDefaultValue = Static(typeof(DateTimeOffset)).Property(nameof(DateTimeOffset.Now));
Assert.AreEqual(expectedDefaultValue, parameter.DefaultValue);
}
+
+ [Test]
+ public void KeyCredentialHasExpectedType()
+ {
+ Assert.AreEqual("ApiKeyCredential", ScmKnownParameters.KeyCredential.Type.Name);
+ }
+
+ [Test]
+ public void TokenCredentialHasExpectedType()
+ {
+ Assert.AreEqual("AuthenticationTokenProvider", ScmKnownParameters.TokenCredential.Type.Name);
+ }
}
}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Properties/AssemblyInfo.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Properties/AssemblyInfo.cs
index f3131262a05..53b57a107a3 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Properties/AssemblyInfo.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Properties/AssemblyInfo.cs
@@ -6,3 +6,5 @@
[assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator.Input.Tests.Perf, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")]
[assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator.Input.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")]
[assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator.Tests.Common, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")]
+[assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")]
+[assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator.ClientModel, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")]