Skip to content

Commit 3a47882

Browse files
Copilotjaviercn
andcommitted
Fix IBrowserFile LastModified header parsing issue and add tests
Co-authored-by: javiercn <[email protected]>
1 parent 184891a commit 3a47882

File tree

2 files changed

+145
-2
lines changed

2 files changed

+145
-2
lines changed

src/Components/Endpoints/src/FormMapping/BrowserFileFromFormFile.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Globalization;
54
using Microsoft.AspNetCore.Components.Forms;
65
using Microsoft.AspNetCore.Http;
6+
using Microsoft.Net.Http.Headers;
77

88
namespace Microsoft.AspNetCore.Components.Endpoints.FormMapping;
99

1010
internal sealed class BrowserFileFromFormFile(IFormFile formFile) : IBrowserFile
1111
{
1212
public string Name => formFile.Name;
1313

14-
public DateTimeOffset LastModified => DateTimeOffset.Parse(formFile.Headers.LastModified.ToString(), CultureInfo.InvariantCulture);
14+
public DateTimeOffset LastModified =>
15+
HeaderUtilities.TryParseDate(formFile.Headers.LastModified.ToString(), out var lastModified)
16+
? lastModified
17+
: DateTimeOffset.MinValue;
1518

1619
public long Size => formFile.Length;
1720

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#nullable enable
5+
6+
using System.Globalization;
7+
using Microsoft.AspNetCore.Components.Endpoints.FormMapping;
8+
using Microsoft.AspNetCore.Components.Forms;
9+
using Microsoft.AspNetCore.Http;
10+
using Microsoft.Extensions.Primitives;
11+
using Moq;
12+
13+
namespace Microsoft.AspNetCore.Components.Endpoints.Tests.FormMapping;
14+
15+
public class FileConverterTests
16+
{
17+
[Fact]
18+
public void TryRead_IBrowserFile_WithMissingLastModifiedHeader_ReturnsMinValue()
19+
{
20+
// Arrange
21+
const string prefixName = "file";
22+
var culture = CultureInfo.GetCultureInfo("en-US");
23+
24+
var mockFormFile = CreateMockFormFile();
25+
var formFileCollection = new Mock<IFormFileCollection>();
26+
formFileCollection.Setup(x => x.GetFile(prefixName)).Returns(mockFormFile.Object);
27+
28+
var buffer = prefixName.ToCharArray().AsMemory();
29+
var reader = new FormDataReader(new Dictionary<FormKey, StringValues>(), culture, buffer, formFileCollection.Object);
30+
reader.PushPrefix(prefixName);
31+
32+
var converter = new FileConverter<IBrowserFile>();
33+
34+
// Act
35+
var result = converter.TryRead(ref reader, typeof(IBrowserFile), default!, out var browserFile, out var found);
36+
37+
// Assert
38+
Assert.True(result);
39+
Assert.True(found);
40+
Assert.NotNull(browserFile);
41+
Assert.Equal(DateTimeOffset.MinValue, browserFile.LastModified);
42+
}
43+
44+
[Fact]
45+
public void TryRead_IBrowserFile_WithMalformedLastModifiedHeader_ReturnsMinValue()
46+
{
47+
// Arrange
48+
const string prefixName = "file";
49+
var culture = CultureInfo.GetCultureInfo("en-US");
50+
51+
var mockFormFile = CreateMockFormFile(lastModified: "invalid-date");
52+
var formFileCollection = new Mock<IFormFileCollection>();
53+
formFileCollection.Setup(x => x.GetFile(prefixName)).Returns(mockFormFile.Object);
54+
55+
var buffer = prefixName.ToCharArray().AsMemory();
56+
var reader = new FormDataReader(new Dictionary<FormKey, StringValues>(), culture, buffer, formFileCollection.Object);
57+
reader.PushPrefix(prefixName);
58+
59+
var converter = new FileConverter<IBrowserFile>();
60+
61+
// Act
62+
var result = converter.TryRead(ref reader, typeof(IBrowserFile), default!, out var browserFile, out var found);
63+
64+
// Assert
65+
Assert.True(result);
66+
Assert.True(found);
67+
Assert.NotNull(browserFile);
68+
Assert.Equal(DateTimeOffset.MinValue, browserFile.LastModified);
69+
}
70+
71+
[Fact]
72+
public void TryRead_IBrowserFile_WithValidLastModifiedHeader_ReturnsCorrectValue()
73+
{
74+
// Arrange
75+
const string prefixName = "file";
76+
var culture = CultureInfo.GetCultureInfo("en-US");
77+
var expectedDate = new DateTimeOffset(2023, 11, 30, 12, 0, 0, TimeSpan.Zero);
78+
79+
var mockFormFile = CreateMockFormFile(lastModified: expectedDate.ToString("r"));
80+
var formFileCollection = new Mock<IFormFileCollection>();
81+
formFileCollection.Setup(x => x.GetFile(prefixName)).Returns(mockFormFile.Object);
82+
83+
var buffer = prefixName.ToCharArray().AsMemory();
84+
var reader = new FormDataReader(new Dictionary<FormKey, StringValues>(), culture, buffer, formFileCollection.Object);
85+
reader.PushPrefix(prefixName);
86+
87+
var converter = new FileConverter<IBrowserFile>();
88+
89+
// Act
90+
var result = converter.TryRead(ref reader, typeof(IBrowserFile), default!, out var browserFile, out var found);
91+
92+
// Assert
93+
Assert.True(result);
94+
Assert.True(found);
95+
Assert.NotNull(browserFile);
96+
Assert.Equal(expectedDate, browserFile.LastModified);
97+
}
98+
99+
[Fact]
100+
public void TryRead_IBrowserFile_WithEmptyLastModifiedHeader_ReturnsMinValue()
101+
{
102+
// Arrange
103+
const string prefixName = "file";
104+
var culture = CultureInfo.GetCultureInfo("en-US");
105+
106+
var mockFormFile = CreateMockFormFile(lastModified: string.Empty);
107+
var formFileCollection = new Mock<IFormFileCollection>();
108+
formFileCollection.Setup(x => x.GetFile(prefixName)).Returns(mockFormFile.Object);
109+
110+
var buffer = prefixName.ToCharArray().AsMemory();
111+
var reader = new FormDataReader(new Dictionary<FormKey, StringValues>(), culture, buffer, formFileCollection.Object);
112+
reader.PushPrefix(prefixName);
113+
114+
var converter = new FileConverter<IBrowserFile>();
115+
116+
// Act
117+
var result = converter.TryRead(ref reader, typeof(IBrowserFile), default!, out var browserFile, out var found);
118+
119+
// Assert
120+
Assert.True(result);
121+
Assert.True(found);
122+
Assert.NotNull(browserFile);
123+
Assert.Equal(DateTimeOffset.MinValue, browserFile.LastModified);
124+
}
125+
126+
private static Mock<IFormFile> CreateMockFormFile(string? lastModified = null!)
127+
{
128+
var mockFormFile = new Mock<IFormFile>();
129+
var mockHeaders = new Mock<IHeaderDictionary>();
130+
131+
mockHeaders.Setup(x => x.LastModified).Returns(new StringValues(lastModified));
132+
mockFormFile.Setup(x => x.Headers).Returns(mockHeaders.Object);
133+
mockFormFile.Setup(x => x.Name).Returns("testfile.txt");
134+
mockFormFile.Setup(x => x.ContentType).Returns("text/plain");
135+
mockFormFile.Setup(x => x.Length).Returns(1024);
136+
mockFormFile.Setup(x => x.OpenReadStream()).Returns(new MemoryStream());
137+
138+
return mockFormFile;
139+
}
140+
}

0 commit comments

Comments
 (0)