Skip to content

Commit 7d313cb

Browse files
committed
add more tests for string optional array
align array creation more closely with the reflection delegate factory
1 parent c571a3e commit 7d313cb

File tree

3 files changed

+188
-30
lines changed

3 files changed

+188
-30
lines changed

src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ internal static void EmitParsingBlock(this EndpointParameter endpointParameter,
9595
var createArray = $"new {endpointParameter.ElementType.ToDisplayString(EmitterConstants.DisplayFormat)}[{endpointParameter.EmitTempArgument()}.Length]";
9696

9797
// we assign a null to result parameter if it's optional array, otherwise we create new array immediately
98-
codeWriter.WriteLine($"{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)} {endpointParameter.EmitHandlerArgument()} = {(endpointParameter.IsOptional ? "null" : createArray)};");
98+
codeWriter.WriteLine($"{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)} {endpointParameter.EmitHandlerArgument()} = {createArray};");
9999

100100
codeWriter.WriteLine($"for (var i = 0; i < {endpointParameter.EmitTempArgument()}.Length; i++)");
101101
codeWriter.StartBlock();
102102
codeWriter.WriteLine($"var element = {endpointParameter.EmitTempArgument()}[i];");
103103

104-
//endpointParameter.ParsingBlockEmitter(codeWriter, "element", "parsed_element");
104+
// emit parsing block for current array element
105105
codeWriter.WriteLine($$"""if (!{{endpointParameter.PreferredTryParseInvocation("element", "parsed_element")}})""");
106106
codeWriter.StartBlock();
107107
codeWriter.WriteLine("if (!string.IsNullOrEmpty(element))");
@@ -110,12 +110,6 @@ internal static void EmitParsingBlock(this EndpointParameter endpointParameter,
110110
codeWriter.EndBlock();
111111
codeWriter.EndBlock();
112112

113-
// In case we have optional parameter, we emit array assignment
114-
if (endpointParameter.IsOptional)
115-
{
116-
codeWriter.WriteLine($$"""{{endpointParameter.EmitHandlerArgument()}} ??= {{createArray}};""");
117-
}
118-
119113
// In cases where we are dealing with an array of parsable nullables we need to substitute
120114
// empty strings for null values.
121115
if (endpointParameter.ElementType.NullableAnnotation == NullableAnnotation.Annotated)
@@ -139,10 +133,9 @@ internal static void EmitParsingBlock(this EndpointParameter endpointParameter,
139133
var temp_argument = endpointParameter.EmitTempArgument();
140134
var output_argument = endpointParameter.EmitParsedTempArgument();
141135

142-
//endpointParameter.ParsingBlockEmitter(codeWriter, endpointParameter.EmitTempArgument(), endpointParameter.EmitParsedTempArgument());
136+
// emit parsing block for optional OR nullable values
143137
if (endpointParameter.IsOptional || endpointParameter.Type.NullableAnnotation == NullableAnnotation.Annotated)
144138
{
145-
var parameterType = endpointParameter.Type.UnwrapTypeSymbol(unwrapArray: true, unwrapNullable: true);
146139
var temp_argument_parsed_non_nullable = $"{temp_argument}_parsed_non_nullable";
147140

148141
codeWriter.WriteLine($"""{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)} {output_argument} = default;""");
@@ -159,6 +152,7 @@ internal static void EmitParsingBlock(this EndpointParameter endpointParameter,
159152
codeWriter.WriteLine("wasParamCheckFailure = true;");
160153
codeWriter.EndBlock();
161154
}
155+
// parsing block for non-nullable required parameters
162156
else
163157
{
164158
codeWriter.WriteLine($$"""if (!{{endpointParameter.PreferredTryParseInvocation(temp_argument, output_argument)}})""");

src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Arrays.cs

Lines changed: 184 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,96 @@ public async Task MapAction_ImplicitQuery_NullableStringArrayParam()
327327
}
328328

329329
[Fact]
330-
public async Task MapAction_ImplicitQuery_OptionalIntArrayParam()
330+
public async Task MapAction_ImplicitQuery_StringArrayParam_Optional()
331331
{
332332
var (results, compilation) = await RunGeneratorAsync("""
333-
app.MapGet("/hello", (int[]? p = null) => p?.Length ?? 0);
333+
app.MapGet("/hello", (string[]? p = null) => p);
334+
""");
335+
336+
var endpoint = GetEndpointFromCompilation(compilation);
337+
338+
VerifyStaticEndpointModel(results, endpointModel =>
339+
{
340+
Assert.Equal("MapGet", endpointModel.HttpMethod);
341+
});
342+
343+
var httpContext = CreateHttpContext();
344+
httpContext.Request.QueryString = new QueryString("?p=a&p=b&p=c");
345+
346+
await endpoint.RequestDelegate(httpContext);
347+
await VerifyResponseBodyAsync(httpContext, """["a","b","c"]""");
348+
//await VerifyAgainstBaselineUsingFile(compilation);
349+
}
350+
351+
[Fact]
352+
public async Task MapAction_ImplicitQuery_StringArrayParam_Optional_QueryNotPresent()
353+
{
354+
var (results, compilation) = await RunGeneratorAsync("""
355+
app.MapGet("/hello", (string[]? p = null) => p);
356+
""");
357+
358+
var endpoint = GetEndpointFromCompilation(compilation);
359+
360+
VerifyStaticEndpointModel(results, endpointModel =>
361+
{
362+
Assert.Equal("MapGet", endpointModel.HttpMethod);
363+
});
364+
365+
var httpContext = CreateHttpContext();
366+
367+
await endpoint.RequestDelegate(httpContext);
368+
await VerifyResponseBodyAsync(httpContext, "[]");
369+
//await VerifyAgainstBaselineUsingFile(compilation);
370+
}
371+
372+
[Fact]
373+
public async Task MapAction_ExplicitQuery_StringArrayParam_Optional()
374+
{
375+
var (results, compilation) = await RunGeneratorAsync("""
376+
app.MapGet("/hello", ([FromQuery] string[]? p = null) => p);
377+
""");
378+
379+
var endpoint = GetEndpointFromCompilation(compilation);
380+
381+
VerifyStaticEndpointModel(results, endpointModel =>
382+
{
383+
Assert.Equal("MapGet", endpointModel.HttpMethod);
384+
});
385+
386+
var httpContext = CreateHttpContext();
387+
httpContext.Request.QueryString = new QueryString("?p=a&p=b&p=c");
388+
389+
await endpoint.RequestDelegate(httpContext);
390+
await VerifyResponseBodyAsync(httpContext, """["a","b","c"]""");
391+
//await VerifyAgainstBaselineUsingFile(compilation);
392+
}
393+
394+
[Fact]
395+
public async Task MapAction_ExplicitQuery_StringArrayParam_Optional_QueryNotPresent()
396+
{
397+
var (results, compilation) = await RunGeneratorAsync("""
398+
app.MapGet("/hello", ([FromQuery] string[]? p = null) => p);
399+
""");
400+
401+
var endpoint = GetEndpointFromCompilation(compilation);
402+
403+
VerifyStaticEndpointModel(results, endpointModel =>
404+
{
405+
Assert.Equal("MapGet", endpointModel.HttpMethod);
406+
});
407+
408+
var httpContext = CreateHttpContext();
409+
410+
await endpoint.RequestDelegate(httpContext);
411+
await VerifyResponseBodyAsync(httpContext, "[]");
412+
//await VerifyAgainstBaselineUsingFile(compilation);
413+
}
414+
415+
[Fact]
416+
public async Task MapAction_ImplicitQuery_IntArrayParam_Optional()
417+
{
418+
var (results, compilation) = await RunGeneratorAsync("""
419+
app.MapGet("/hello", (int[]? p = null) => p);
334420
""");
335421

336422
var endpoint = GetEndpointFromCompilation(compilation);
@@ -344,15 +430,15 @@ public async Task MapAction_ImplicitQuery_OptionalIntArrayParam()
344430
httpContext.Request.QueryString = new QueryString("?p=0&p=1&p=2");
345431

346432
await endpoint.RequestDelegate(httpContext);
347-
await VerifyResponseBodyAsync(httpContext, "3");
433+
await VerifyResponseBodyAsync(httpContext, "[0,1,2]");
348434
//await VerifyAgainstBaselineUsingFile(compilation);
349435
}
350436

351437
[Fact]
352-
public async Task MapAction_ImplicitQuery_OptionalIntArrayParam_EmptyValues()
438+
public async Task MapAction_ImplicitQuery_IntArrayParam_Optional_QueryNotPresent()
353439
{
354440
var (results, compilation) = await RunGeneratorAsync("""
355-
app.MapGet("/hello", (int[]? p = null) => p?.Length ?? 0);
441+
app.MapGet("/hello", (int[]? p = null) => p);
356442
""");
357443

358444
var endpoint = GetEndpointFromCompilation(compilation);
@@ -365,15 +451,15 @@ public async Task MapAction_ImplicitQuery_OptionalIntArrayParam_EmptyValues()
365451
var httpContext = CreateHttpContext();
366452

367453
await endpoint.RequestDelegate(httpContext);
368-
await VerifyResponseBodyAsync(httpContext, "0");
454+
await VerifyResponseBodyAsync(httpContext, "[]");
369455
//await VerifyAgainstBaselineUsingFile(compilation);
370456
}
371457

372458
[Fact]
373-
public async Task MapAction_ExplicitQuery_OptionalIntArrayParam()
459+
public async Task MapAction_ExplicitQuery_IntArrayParam_Optional()
374460
{
375461
var (results, compilation) = await RunGeneratorAsync("""
376-
app.MapGet("/hello", ([FromQuery] int[]? p = null) => p?.Length ?? 0);
462+
app.MapGet("/hello", ([FromQuery] int[]? p = null) => p);
377463
""");
378464

379465
var endpoint = GetEndpointFromCompilation(compilation);
@@ -387,15 +473,15 @@ public async Task MapAction_ExplicitQuery_OptionalIntArrayParam()
387473
httpContext.Request.QueryString = new QueryString("?p=0&p=1&p=2");
388474

389475
await endpoint.RequestDelegate(httpContext);
390-
await VerifyResponseBodyAsync(httpContext, "3");
476+
await VerifyResponseBodyAsync(httpContext, "[0,1,2]");
391477
//await VerifyAgainstBaselineUsingFile(compilation);
392478
}
393479

394480
[Fact]
395-
public async Task MapAction_ExplicitQuery_OptionalIntArrayParam_EmptyValues()
481+
public async Task MapAction_ExplicitQuery_IntArrayParam_Optional_QueryNotPresent()
396482
{
397483
var (results, compilation) = await RunGeneratorAsync("""
398-
app.MapGet("/hello", ([FromQuery] int[]? p = null) => p?.Length ?? 0);
484+
app.MapGet("/hello", ([FromQuery] int[]? p = null) => p);
399485
""");
400486

401487
var endpoint = GetEndpointFromCompilation(compilation);
@@ -408,7 +494,93 @@ public async Task MapAction_ExplicitQuery_OptionalIntArrayParam_EmptyValues()
408494
var httpContext = CreateHttpContext();
409495

410496
await endpoint.RequestDelegate(httpContext);
411-
await VerifyResponseBodyAsync(httpContext, "0");
497+
await VerifyResponseBodyAsync(httpContext, "[]");
498+
//await VerifyAgainstBaselineUsingFile(compilation);
499+
}
500+
501+
[Fact]
502+
public async Task MapAction_ImplicitQuery_NullableIntArrayParam_Optional()
503+
{
504+
var (results, compilation) = await RunGeneratorAsync("""
505+
app.MapGet("/hello", (int?[]? p = null) => p);
506+
""");
507+
508+
var endpoint = GetEndpointFromCompilation(compilation);
509+
510+
VerifyStaticEndpointModel(results, endpointModel =>
511+
{
512+
Assert.Equal("MapGet", endpointModel.HttpMethod);
513+
});
514+
515+
var httpContext = CreateHttpContext();
516+
httpContext.Request.QueryString = new QueryString("?p=0&p=1&p=2");
517+
518+
await endpoint.RequestDelegate(httpContext);
519+
await VerifyResponseBodyAsync(httpContext, "[0,1,2]");
520+
//await VerifyAgainstBaselineUsingFile(compilation);
521+
}
522+
523+
[Fact]
524+
public async Task MapAction_ImplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent()
525+
{
526+
var (results, compilation) = await RunGeneratorAsync("""
527+
app.MapGet("/hello", (int?[]? p = null) => p);
528+
""");
529+
530+
var endpoint = GetEndpointFromCompilation(compilation);
531+
532+
VerifyStaticEndpointModel(results, endpointModel =>
533+
{
534+
Assert.Equal("MapGet", endpointModel.HttpMethod);
535+
});
536+
537+
var httpContext = CreateHttpContext();
538+
539+
await endpoint.RequestDelegate(httpContext);
540+
await VerifyResponseBodyAsync(httpContext, "[]");
541+
//await VerifyAgainstBaselineUsingFile(compilation);
542+
}
543+
544+
[Fact]
545+
public async Task MapAction_ExplicitQuery_NullableIntArrayParam_Optional()
546+
{
547+
var (results, compilation) = await RunGeneratorAsync("""
548+
app.MapGet("/hello", ([FromQuery] int?[]? p = null) => p);
549+
""");
550+
551+
var endpoint = GetEndpointFromCompilation(compilation);
552+
553+
VerifyStaticEndpointModel(results, endpointModel =>
554+
{
555+
Assert.Equal("MapGet", endpointModel.HttpMethod);
556+
});
557+
558+
var httpContext = CreateHttpContext();
559+
httpContext.Request.QueryString = new QueryString("?p=0&p=1&p=2");
560+
561+
await endpoint.RequestDelegate(httpContext);
562+
await VerifyResponseBodyAsync(httpContext, "[0,1,2]");
563+
//await VerifyAgainstBaselineUsingFile(compilation);
564+
}
565+
566+
[Fact]
567+
public async Task MapAction_ExplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent()
568+
{
569+
var (results, compilation) = await RunGeneratorAsync("""
570+
app.MapGet("/hello", ([FromQuery] int?[]? p = null) => p);
571+
""");
572+
573+
var endpoint = GetEndpointFromCompilation(compilation);
574+
575+
VerifyStaticEndpointModel(results, endpointModel =>
576+
{
577+
Assert.Equal("MapGet", endpointModel.HttpMethod);
578+
});
579+
580+
var httpContext = CreateHttpContext();
581+
582+
await endpoint.RequestDelegate(httpContext);
583+
await VerifyResponseBodyAsync(httpContext, "[]");
412584
//await VerifyAgainstBaselineUsingFile(compilation);
413585
}
414586

src/Http/samples/MinimalSample/Program.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,6 @@
9494
app.MapPost("/todos", (TodoBindable todo) => todo);
9595
app.MapGet("/todos", () => new Todo[] { new Todo(1, "Walk the dog"), new Todo(2, "Come back home") });
9696

97-
app.MapGet("/array-of-nullables", (int?[] array1) => Results.Ok());
98-
99-
app.MapGet("/array-nullable", (int[]? array2) => Results.Ok());
100-
app.MapGet("/array-nullable-of-nullables", (int?[]? array3) => Results.Ok());
101-
102-
app.MapGet("/array-nullable-optional", IResult (int[]? array4 = null) => Results.Ok());
103-
app.MapGet("/array-nullable-optional-of-nullables", (int?[]? array5 = null) => Results.Ok());
104-
10597
app.Run();
10698

10799
internal record Todo(int Id, string Title);

0 commit comments

Comments
 (0)