Skip to content

Commit 001581a

Browse files
committed
Refactor code generation templates and enhance test coverage.
- Added `CSharpCompiler.AssertCompile` assertions across multiple test files to ensure generated code compiles successfully. - Introduced new test cases for nullable path parameters, query string handling (arrays, dictionaries, deep objects), and OpenAPI specifications. - Refactored path and query parameter handling in Liquid templates for improved readability and correctness. - Generated auto-verified test outputs for new test cases. - Improved handling of constructor parameters in generated clients.
1 parent 3bf55b1 commit 001581a

16 files changed

Lines changed: 2776 additions & 45 deletions

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "10.0.100-rc.1.25451.107",
3+
"version": "10.0.100-rc.2.25502.107",
44
"rollForward": "latestMinor",
55
"allowPrerelease": true
66
}

src/NSwag.CodeGeneration.CSharp.Tests/AllowNullableBodyParametersTests.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using Microsoft.AspNetCore.Mvc;
22
using NJsonSchema.NewtonsoftJson.Generation;
33
using NSwag.CodeGeneration.OperationNameGenerators;
4+
using NSwag.CodeGeneration.Tests;
45
using NSwag.Generation.WebApi;
56
using System.ComponentModel.DataAnnotations;
6-
using NSwag.CodeGeneration.Tests;
7+
using System.Reflection;
78

89
namespace NSwag.CodeGeneration.CSharp.Tests
910
{
@@ -67,6 +68,15 @@ public async Task TestNullableBodyWithAllowNullableBodyParameters()
6768

6869
// Assert
6970
await VerifyHelper.Verify(code);
71+
CSharpCompiler.AssertCompile(code + @"
72+
namespace MyNamespace
73+
{
74+
public class MyBaseClass
75+
{
76+
public MyBaseClass(MyConfig configuration) {}
77+
}
78+
public class MyConfig {}
79+
}");
7080
}
7181

7282
[Fact]
@@ -80,6 +90,15 @@ public async Task TestNullableBodyWithoutAllowNullableBodyParameters()
8090

8191
// Assert
8292
await VerifyHelper.Verify(code);
93+
CSharpCompiler.AssertCompile(code + @"
94+
namespace MyNamespace
95+
{
96+
public class MyBaseClass
97+
{
98+
public MyBaseClass(MyConfig configuration) {}
99+
}
100+
public class MyConfig {}
101+
}");
83102
}
84103

85104
private static async Task<CSharpClientGenerator> GenerateCode(bool allowNullableBodyParameters)

src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ public async Task When_ConfigurationClass_is_set_then_correct_ctor_is_generated(
4848

4949
// Assert
5050
await VerifyHelper.Verify(code);
51+
CSharpCompiler.AssertCompile(code + @"
52+
namespace MyNamespace
53+
{
54+
public class MyBaseClass
55+
{
56+
public MyBaseClass(MyConfig configuration) {}
57+
}
58+
public class MyConfig {}
59+
}");
5160
}
5261

5362
[Fact]
@@ -72,6 +81,16 @@ public async Task When_UseHttpRequestMessageCreationMethod_is_set_then_CreateReq
7281

7382
// Assert
7483
await VerifyHelper.Verify(code);
84+
CSharpCompiler.AssertCompile(code + @"
85+
namespace MyNamespace
86+
{
87+
public class MyBaseClass
88+
{
89+
public MyBaseClass(MyConfig configuration) {}
90+
protected global::System.Threading.Tasks.Task<global::System.Net.Http.HttpRequestMessage> CreateHttpRequestMessageAsync(global::System.Threading.CancellationToken ct) { return default; }
91+
}
92+
public class MyConfig {}
93+
}");
7594
}
7695

7796
[Fact]
@@ -162,6 +181,11 @@ public async Task When_custom_http_client_type_is_specified_then_an_instance_of_
162181

163182
// Assert
164183
await VerifyHelper.Verify(code);
184+
CSharpCompiler.AssertCompile(code + @"
185+
namespace CustomNamespace
186+
{
187+
public class CustomHttpClient : global::System.Net.Http.HttpClient { }
188+
}");
165189
}
166190

167191
[Fact]
@@ -232,6 +256,11 @@ public async Task When_client_base_interface_is_specified_then_client_interface_
232256

233257
// Assert
234258
await VerifyHelper.Verify(code);
259+
CSharpCompiler.AssertCompile(code + @"
260+
namespace MyNamespace
261+
{
262+
public interface IClientBase { }
263+
}");
235264
}
236265

237266
[Fact]
@@ -256,6 +285,11 @@ public async Task When_client_base_interface_is_specified_with_access_modifier_t
256285

257286
// Assert
258287
await VerifyHelper.Verify(code);
288+
CSharpCompiler.AssertCompile(code + @"
289+
namespace MyNamespace
290+
{
291+
public interface IClientBase { }
292+
}");
259293
}
260294

261295
[Fact]
@@ -307,6 +341,11 @@ public async Task When_client_interface_generation_is_enabled_and_suppressed_the
307341

308342
// Assert
309343
await VerifyHelper.Verify(code);
344+
CSharpCompiler.AssertCompile(code + @"
345+
namespace MyNamespace
346+
{
347+
public interface IFooClient { }
348+
}");
310349
}
311350

312351
public class OperationSelectionTestData : IEnumerable<object[]>

src/NSwag.CodeGeneration.CSharp.Tests/CSharpCompiler.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ static CSharpCompiler()
2121
.Append(MetadataReference.CreateFromFile(typeof(System.Collections.ObjectModel.ObservableCollection<>).Assembly.Location))
2222
.Append(MetadataReference.CreateFromFile(typeof(System.Runtime.Serialization.EnumMemberAttribute).Assembly.Location))
2323
.Append(MetadataReference.CreateFromFile(typeof(System.Text.Json.Serialization.JsonConverter).Assembly.Location))
24+
.Append(MetadataReference.CreateFromFile(typeof(Microsoft.Extensions.Primitives.StringValues).Assembly.Location))
2425
.ToList();
2526
}
2627

src/NSwag.CodeGeneration.CSharp.Tests/ClientGenerationTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,21 @@ public class ClientGenerationTests
88
[Fact]
99
public async Task CanGenerateFromJiraOpenApiSpecification()
1010
{
11+
// Jira's OpenAPI spec generates code like this:
12+
//// public bool ShowDaysInColumn { get; set; } = MyNamespace.bool.False;
1113
await VerifyOutput("JIRA_OpenAPI", "jira-open-api.json", compile: false);
1214
}
1315

1416
[Fact]
1517
public async Task CanGenerateFromShipBobOpenApiSpecification()
1618
{
17-
await VerifyOutput("ShipBob_OpenAPI", "shipbob-2025-07.json");
19+
await VerifyOutput("ShipBob_OpenAPI", "shipbob-2025-07.json", compile: true);
1820
}
1921

2022
[Fact]
2123
public async Task CanGenerateFromNhsSpineServicesOpenApiSpecification()
2224
{
23-
await VerifyOutput("NHS_SpineServices_OpenAPI", "nhs-spineservices.json");
25+
await VerifyOutput("NHS_SpineServices_OpenAPI", "nhs-spineservices.json", compile: true);
2426
}
2527

2628
private static async Task VerifyOutput(string name, string fileName, bool compile = true)

src/NSwag.CodeGeneration.CSharp.Tests/CodeGenerationTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ public async Task When_generating_CSharp_code_with_SystemTextJson_and_JsonSerial
6060

6161
// Assert
6262
await VerifyHelper.Verify(code);
63+
CSharpCompiler.AssertCompile(code + @"
64+
namespace MyNamespace
65+
{
66+
partial class Client
67+
{
68+
static global::System.Text.Json.JsonSerializerOptions TestJsonSerializerSettingsTransformationMethod(global::System.Text.Json.JsonSerializerOptions settings) { return settings; }
69+
}
70+
}");
6371
}
6472

6573
[Fact]
@@ -78,6 +86,14 @@ public async Task When_generating_CSharp_code_with_NewtonsoftJson_and_JsonSerial
7886

7987
// Assert
8088
await VerifyHelper.Verify(code);
89+
CSharpCompiler.AssertCompile(code + @"
90+
namespace MyNamespace
91+
{
92+
partial class Client
93+
{
94+
static global::Newtonsoft.Json.JsonSerializerSettings TestJsonSerializerSettingsTransformationMethod(global::Newtonsoft.Json.JsonSerializerSettings settings) { return settings; }
95+
}
96+
}");
8197
}
8298

8399
[Fact]
@@ -96,6 +112,17 @@ public async Task When_generating_CSharp_code_with_SystemTextJson_and_JsonConver
96112

97113
// Assert
98114
await VerifyHelper.Verify(code);
115+
CSharpCompiler.AssertCompile(code + @"
116+
#nullable disable
117+
namespace MyNamespace {
118+
class CustomConverter1 : global::System.Text.Json.Serialization.JsonConverter<Client>
119+
{
120+
public override Client Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) { return default(Client); }
121+
public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Client value, global::System.Text.Json.JsonSerializerOptions options) { }
122+
}
123+
class CustomConverter2 : CustomConverter1 { }
124+
}
125+
");
99126
}
100127

101128
[Fact]
@@ -115,6 +142,17 @@ public async Task When_generating_CSharp_code_with_SystemTextJson_and_GenerateJs
115142

116143
// Assert
117144
await VerifyHelper.Verify(code);
145+
CSharpCompiler.AssertCompile(code + @"
146+
#nullable disable
147+
namespace MyNamespace {
148+
class CustomConverter1 : global::System.Text.Json.Serialization.JsonConverter<Client>
149+
{
150+
public override Client Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) { return default(Client); }
151+
public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Client value, global::System.Text.Json.JsonSerializerOptions options) { }
152+
}
153+
class CustomConverter2 : CustomConverter1 { }
154+
}
155+
");
118156
}
119157

120158
[Fact]

src/NSwag.CodeGeneration.CSharp.Tests/ControllerGenerationFormatTests.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,12 @@ public async Task When_the_generation_of_dto_classes_are_disabled_then_file_is_g
226226

227227
// Assert
228228
await VerifyHelper.Verify(code);
229+
CSharpCompiler.AssertCompile(code + @"
230+
namespace MyNamespace
231+
{
232+
public class ComplexType {}
233+
public class ComplexTypeResponse {}
234+
}");
229235
}
230236

231237
private static OpenApiDocument GetOpenApiDocument()
@@ -434,6 +440,46 @@ public async Task When_controllertarget_aspnet_and_multiple_controllers_then_onl
434440
Assert.Equal(1, fromHeaderCustomBindingCount);
435441

436442
await VerifyHelper.Verify(code);
443+
CSharpCompiler.AssertCompile(code + @"
444+
namespace System.Web.Http
445+
{
446+
public class ApiController { }
447+
public abstract class ParameterBindingAttribute : global::System.Attribute
448+
{
449+
public abstract System.Web.Http.Controllers.HttpParameterBinding GetBinding(global::System.Web.Http.Controllers.HttpParameterDescriptor parameter);
450+
}
451+
public class HttpGet : global::System.Attribute { }
452+
public class HttpPost : global::System.Attribute { }
453+
public class FromUri : global::System.Attribute { }
454+
public class FromBody : global::System.Attribute { }
455+
public class Route : global::System.Attribute
456+
{
457+
public Route(string template) { }
458+
}
459+
}
460+
namespace System.Web.Http.Controllers
461+
{
462+
public abstract class HttpParameterBinding
463+
{
464+
protected HttpParameterBinding(global::System.Web.Http.Controllers.HttpParameterDescriptor parameter) { }
465+
public dynamic Descriptor { get; }
466+
public abstract System.Threading.Tasks.Task ExecuteBindingAsync(global::System.Web.Http.Metadata.ModelMetadataProvider metadataProvider, global::System.Web.Http.Controllers.HttpActionContext actionContext, global::System.Threading.CancellationToken cancellationToken);
467+
}
468+
public abstract class HttpParameterDescriptor
469+
{
470+
public string ParameterName { get; }
471+
public dynamic ActionArguments { get; }
472+
}
473+
public abstract class HttpActionContext
474+
{
475+
public dynamic Request { get; }
476+
public dynamic ActionArguments { get; }
477+
}
478+
}
479+
namespace System.Web.Http.Metadata
480+
{
481+
public abstract class ModelMetadataProvider { }
482+
}");
437483
}
438484

439485
[Fact]
@@ -453,6 +499,7 @@ public async Task When_controllertarget_aspnetcore_then_use_builtin_fromheader()
453499
// Assert
454500
await VerifyHelper.Verify(code);
455501
CSharpCompiler.AssertCompile(code);
502+
CSharpCompiler.AssertCompile(code);
456503
}
457504

458505
[Fact]
@@ -471,6 +518,47 @@ public async Task When_controller_has_operation_with_header_parameter_then_parti
471518

472519
// Assert
473520
await VerifyHelper.Verify(code);
521+
CSharpCompiler.AssertCompile(code + @"
522+
namespace System.Web.Http
523+
{
524+
public class ApiController { }
525+
public abstract class ParameterBindingAttribute : global::System.Attribute
526+
{
527+
public abstract System.Web.Http.Controllers.HttpParameterBinding GetBinding(global::System.Web.Http.Controllers.HttpParameterDescriptor parameter);
528+
}
529+
public class HttpGet : global::System.Attribute { }
530+
public class HttpPost : global::System.Attribute { }
531+
public class FromUri : global::System.Attribute { }
532+
public class FromBody : global::System.Attribute { }
533+
public class Route : global::System.Attribute
534+
{
535+
public Route(string template) { }
536+
}
537+
}
538+
namespace System.Web.Http.Controllers
539+
{
540+
public abstract class HttpParameterBinding
541+
{
542+
protected HttpParameterBinding(global::System.Web.Http.Controllers.HttpParameterDescriptor parameter) { }
543+
public dynamic ActionArguments { get; }
544+
public dynamic Descriptor { get; }
545+
public abstract System.Threading.Tasks.Task ExecuteBindingAsync(global::System.Web.Http.Metadata.ModelMetadataProvider metadataProvider, global::System.Web.Http.Controllers.HttpActionContext actionContext, global::System.Threading.CancellationToken cancellationToken);
546+
}
547+
public abstract class HttpParameterDescriptor
548+
{
549+
public string ParameterName { get; }
550+
public dynamic ActionArguments { get; }
551+
}
552+
public abstract class HttpActionContext
553+
{
554+
public dynamic ActionArguments { get; }
555+
public dynamic Request { get; }
556+
}
557+
}
558+
namespace System.Web.Http.Metadata
559+
{
560+
public abstract class ModelMetadataProvider { }
561+
}");
474562
}
475563
}
476564
}

0 commit comments

Comments
 (0)