Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions http.Tests/HttpPostBodyTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
using Company.Function;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq;

namespace http.Tests
{
public class HttpPostBodyTests
{
private readonly Mock<ILogger<HttpPostBody>> _mockLogger;
private readonly Mock<ILoggerFactory> _mockLoggerFactory;
private readonly HttpPostBody _function;
private readonly Mock<HttpRequest> _mockRequest;

public HttpPostBodyTests()
{
_mockLogger = new Mock<ILogger<HttpPostBody>>();
_mockLoggerFactory = new Mock<ILoggerFactory>();
_mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>()))
.Returns(_mockLogger.Object);
_function = new HttpPostBody(_mockLoggerFactory.Object);
_mockRequest = new Mock<HttpRequest>();
}

[Fact]
public void Run_WithValidPerson_ReturnsOkResultWithPersonalizedMessage()
{
// Arrange
var person = new Person("Jane", 30);
string expectedMessage = "Hello, Jane! You are 30 years old.";

// Act
var result = _function.Run(_mockRequest.Object, person);

// Assert
Assert.IsType<OkObjectResult>(result);
var okResult = result as OkObjectResult;
Assert.Equal(expectedMessage, okResult?.Value);

// Verify logging - should be called multiple times
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.AtLeast(2));
}

[Fact]
public void Run_WithEmptyName_ReturnsBadRequest()
{
// Arrange
var person = new Person("", 25);
string expectedErrorMessage = "Please provide both name and age in the request body.";

// Act
var result = _function.Run(_mockRequest.Object, person);

// Assert
Assert.IsType<BadRequestObjectResult>(result);
var badRequestResult = result as BadRequestObjectResult;
Assert.Equal(expectedErrorMessage, badRequestResult?.Value);

// Verify logging
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("no name/age provided")),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}

[Fact]
public void Run_WithNullName_ReturnsBadRequest()
{
Comment on lines +78 to +80
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

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

The null-forgiving operator null! is used to construct a Person with a null name. The Person record expects a non-nullable string but the actual function handles null values. This inconsistency between the record definition and runtime behavior should be documented in the test specification or the Person record should be updated to accept string? Name to match the actual validation logic.

Suggested change
[Fact]
public void Run_WithNullName_ReturnsBadRequest()
{
// NOTE: This test intentionally passes a null value for the Person's Name property,
// even though the Person record expects a non-nullable string. This highlights an
// inconsistency between the record definition and the actual validation logic.
// Ideally, the Person record should be updated to accept `string? Name` to match
// the function's handling of null values.
[Fact]
public void Run_WithNullName_ReturnsBadRequest()

Copilot uses AI. Check for mistakes.
// Arrange
var person = new Person(null!, 25);
string expectedErrorMessage = "Please provide both name and age in the request body.";

// Act
var result = _function.Run(_mockRequest.Object, person);

// Assert
Assert.IsType<BadRequestObjectResult>(result);
var badRequestResult = result as BadRequestObjectResult;
Assert.Equal(expectedErrorMessage, badRequestResult?.Value);
}

[Fact]
public void Run_WithZeroAge_ReturnsBadRequest()
{
// Arrange
var person = new Person("John", 0);
string expectedErrorMessage = "Please provide both name and age in the request body.";

// Act
var result = _function.Run(_mockRequest.Object, person);

// Assert
Assert.IsType<BadRequestObjectResult>(result);
var badRequestResult = result as BadRequestObjectResult;
Assert.Equal(expectedErrorMessage, badRequestResult?.Value);
}

[Fact]
public void Run_WithValidPerson_LogsMultipleInformationMessages()
{
// Arrange
var person = new Person("Alice", 28);

// Act
_function.Run(_mockRequest.Object, person);

// Assert - Verify logger was called multiple times
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Exactly(2));

// Verify first log contains URL info
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("processed a request for url")),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);

// Verify second log contains person info
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("Alice") && v.ToString()!.Contains("28")),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}
Comment on lines +110 to +148
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

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

The tests Run_WithValidPerson_LogsMultipleInformationMessages and Run_WithValidPerson_ReturnsOkResultWithPersonalizedMessage are testing the same scenario (valid person). The logging verification in lines 111-148 duplicates the logging verification already done in lines 42-49. Consider consolidating these tests or removing the separate logging-focused test to reduce redundancy.

Copilot uses AI. Check for mistakes.

[Fact]
public void Run_WithInvalidPerson_LogsInformationMessage()
{
// Arrange
var person = new Person("", 0);

// Act
_function.Run(_mockRequest.Object, person);

// Assert
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("no name/age provided")),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}
}
}
28 changes: 28 additions & 0 deletions http.Tests/http.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

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

The version 2.3.0 of Microsoft.AspNetCore.Mvc.Core is quite old (released in 2018) and doesn't align with the .NET 8.0 target framework. For .NET 8.0 projects, consider using Microsoft.AspNetCore.Mvc.Core version 2.2.5 or relying on the framework reference already included in the main project. The main http.csproj includes <FrameworkReference Include="Microsoft.AspNetCore.App" /> which should provide the necessary types without requiring this separate package reference.

Suggested change
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />

Copilot uses AI. Check for mistakes.
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\http\http.csproj" />
</ItemGroup>

</Project>
124 changes: 124 additions & 0 deletions http.Tests/httpGetFunctionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using Company.Function;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq;

namespace http.Tests
{
public class httpGetFunctionTests
{
private readonly Mock<ILogger<httpGetFunction>> _mockLogger;
private readonly Mock<ILoggerFactory> _mockLoggerFactory;
private readonly httpGetFunction _function;
private readonly Mock<HttpRequest> _mockRequest;

public httpGetFunctionTests()
{
_mockLogger = new Mock<ILogger<httpGetFunction>>();
_mockLoggerFactory = new Mock<ILoggerFactory>();
_mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>()))
.Returns(_mockLogger.Object);
_function = new httpGetFunction(_mockLoggerFactory.Object);
_mockRequest = new Mock<HttpRequest>();
}

[Fact]
public void Run_WithValidName_ReturnsOkResultWithPersonalizedGreeting()
{
// Arrange
string name = "John";
string expectedMessage = "Hello, John.";

// Act
var result = _function.Run(_mockRequest.Object, name);

// Assert
Assert.IsType<OkObjectResult>(result);
var okResult = result as OkObjectResult;
Assert.Equal(expectedMessage, okResult?.Value);

// Verify logging
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(expectedMessage)),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}

[Fact]
public void Run_WithEmptyName_ReturnsOkResultWithDefaultGreeting()
{
// Arrange
string name = "";
string expectedMessage = "Hello, World.";

// Act
var result = _function.Run(_mockRequest.Object, name);

// Assert
Assert.IsType<OkObjectResult>(result);
var okResult = result as OkObjectResult;
Assert.Equal(expectedMessage, okResult?.Value);

// Verify logging
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(expectedMessage)),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}

[Fact]
public void Run_WithNullName_ReturnsOkResultWithDefaultGreeting()
{
// Arrange
string? name = null;
string expectedMessage = "Hello, World.";

// Act
var result = _function.Run(_mockRequest.Object, name!);
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

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

The double null-forgiving operator usage (name! on line 86) is inconsistent with how null is handled in the actual function. The function signature accepts string name (non-nullable), but the implementation safely handles null values via string.IsNullOrEmpty(). Consider either changing the test to pass an empty string instead of null, or updating the actual function signature to string? name to better reflect the actual behavior being tested.

Suggested change
var result = _function.Run(_mockRequest.Object, name!);
var result = _function.Run(_mockRequest.Object, name);

Copilot uses AI. Check for mistakes.

// Assert
Assert.IsType<OkObjectResult>(result);
var okResult = result as OkObjectResult;
Assert.Equal(expectedMessage, okResult?.Value);

// Verify logging
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(expectedMessage)),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}

[Fact]
public void Run_LogsInformationMessage()
{
// Arrange
string name = "TestUser";

// Act
_function.Run(_mockRequest.Object, name);

// Assert
_mockLogger.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("C# HTTP trigger function processed a request")),
null,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}
Comment on lines +104 to +122
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

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

The test name Run_LogsInformationMessage is redundant with the other tests, which already verify logging behavior. This test doesn't add unique coverage since tests on lines 27-102 already verify that logging occurs with the appropriate messages. Consider removing this test or making it verify a specific logging scenario not covered by other tests (e.g., verify the exact log message format including "for Hello, TestUser.").

Copilot uses AI. Check for mistakes.
}
}
26 changes: 26 additions & 0 deletions http.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,42 @@ VisualStudioVersion = 17.10.35027.167
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "http", "http\http.csproj", "{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "http.Tests", "http.Tests\http.Tests.csproj", "{9A5C9145-5589-4C46-9418-9D0E3006F79F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Debug|x64.ActiveCfg = Debug|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Debug|x64.Build.0 = Debug|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Debug|x86.ActiveCfg = Debug|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Debug|x86.Build.0 = Debug|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Release|Any CPU.Build.0 = Release|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Release|x64.ActiveCfg = Release|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Release|x64.Build.0 = Release|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Release|x86.ActiveCfg = Release|Any CPU
{32C6DAE7-2329-47AB-8551-2A9EF0353C9C}.Release|x86.Build.0 = Release|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Debug|x64.ActiveCfg = Debug|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Debug|x64.Build.0 = Debug|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Debug|x86.ActiveCfg = Debug|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Debug|x86.Build.0 = Debug|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Release|Any CPU.Build.0 = Release|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Release|x64.ActiveCfg = Release|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Release|x64.Build.0 = Release|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Release|x86.ActiveCfg = Release|Any CPU
{9A5C9145-5589-4C46-9418-9D0E3006F79F}.Release|x86.Build.0 = Release|Any CPU
Comment on lines +13 to +43
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

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

[nitpick] The addition of x64 and x86 platform configurations is inconsistent with the original solution which only had "Any CPU". Unless there's a specific requirement for platform-specific builds, consider keeping only "Any CPU" configuration to maintain simplicity. If these platforms are needed, ensure they're documented in the PR description or project documentation.

Copilot uses AI. Check for mistakes.
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Loading
Loading