Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,69 @@ public async Task GetWeatherForecastNotFound()
//Assert
Assert.IsType<NotFoundResult>(result.Result);
}

/// <summary>
/// Test when get weather by id is found
/// </summary>
[Fact()]
public async Task GetWeatherForecastFound()
{
//Setup
var service = new Mock<IWeatherService>();

var controller = new WeatherForecastsController(new NullLogger<WeatherForecastsController>(), service.Object);

WeatherForecast weather = new WeatherForecast { Id = 1, Date = DateTime.Now, TemperatureC = 20, Summary = "Test" };

service.Setup(x => x.GetWeatherForecastAsync(It.IsAny<int>())).ReturnsAsync(weather);

//Act
var result = await controller.GetWeatherForecast(1);

//Assert
Assert.IsType<WeatherForecast>(result.Value);
}

/// <summary>
/// Test when adding a weather forecast is successful
/// </summary>
[Fact()]
public async Task AddWeatherForecastSuccess()
{
// Setup
var service = new Mock<IWeatherService>();
var controller = new WeatherForecastsController(new NullLogger<WeatherForecastsController>(), service.Object);
WeatherForecast newWeather = new WeatherForecast { Id = 2, Date = DateTime.Now, TemperatureC = 25, Summary = "Sunny" };

service.Setup(x => x.AddWeatherForecastAsync(It.IsAny<WeatherForecast>())).ReturnsAsync(newWeather);

// Act
var result = await controller.AddWeatherForecast(newWeather);

// Assert
var actionResult = Assert.IsType<CreatedAtActionResult>(result);
var returnValue = Assert.IsType<WeatherForecast>(actionResult.Value);
Assert.Equal(newWeather.Id, returnValue.Id);
}

/// <summary>
/// Test when adding a weather forecast fails due to invalid input
/// </summary>
[Fact()]
public async Task AddWeatherForecastFailure()
{
// Setup
var service = new Mock<IWeatherService>();
var controller = new WeatherForecastsController(new NullLogger<WeatherForecastsController>(), service.Object);
WeatherForecast invalidWeather = null; // Simulating invalid input

service.Setup(x => x.AddWeatherForecastAsync(It.IsAny<WeatherForecast>())).ReturnsAsync(invalidWeather);

// Act
var result = await controller.AddWeatherForecast(invalidWeather);

// Assert
Assert.IsType<BadRequestResult>(result);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,33 @@ public async Task<ActionResult<WeatherForecast>> GetWeatherForecast(int id)

return weatherForecast;
}

/// <summary>
/// adds a new weather forecast.
Copy link

Copilot AI Apr 9, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider capitalizing the first word in the summary tag (e.g., 'Adds a new weather forecast.') for consistency in documentation style.

Suggested change
/// adds a new weather forecast.
/// Adds a new weather forecast.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Apr 9, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider capitalizing the first letter in the summary comment to improve documentation consistency.

Suggested change
/// adds a new weather forecast.
/// Adds a new weather forecast.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Apr 17, 2025

Choose a reason for hiding this comment

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

[nitpick] The XML documentation summary should start with a capital letter. Consider updating it to 'Adds a new weather forecast.' for proper grammar.

Suggested change
/// adds a new weather forecast.
/// Adds a new weather forecast.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI May 14, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider capitalizing the first letter of the summary for consistency and clarity in XML documentation.

Copilot uses AI. Check for mistakes.
/// </summary>
/// <param name="weatherForecast">The weather forecast to add.</param>
/// <returns>The added weather forecast.</returns>
/// <response code="201">Returns the newly created weather forecast.</response>
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
public async Task<ActionResult<WeatherForecast>> AddWeatherForecast(WeatherForecast weatherForecast)
{
try
{
if (weatherForecast == null)
{
return BadRequest();
}

var addedWeatherForecast = await _weatherService.AddWeatherForecastAsync(weatherForecast);

return CreatedAtAction(nameof(GetWeatherForecast), new { id = addedWeatherForecast.Id }, addedWeatherForecast);
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while adding a weather forecast.");
return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while adding a weather forecast.");
}
}
}
}
20 changes: 20 additions & 0 deletions src/dotnet/csharp/WeatherApi/Services/IWeatherService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,29 @@

namespace WeatherApi.Services
{
/// <summary>
/// Represents a service for retrieving weather forecasts.
/// </summary>
public interface IWeatherService
{
/// <summary>
/// Retrieves a collection of weather forecasts asynchronously.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains the collection of weather forecasts.</returns>
Task<IEnumerable<WeatherForecast>> GetWeatherForecastsAsync();

/// <summary>
/// Retrieves a weather forecast asynchronously based on the specified ID.
/// </summary>
/// <param name="id">The ID of the weather forecast to retrieve.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the weather forecast.</returns>
Task<WeatherForecast> GetWeatherForecastAsync(int id);

/// <summary>
/// Adds a new weather forecast asynchronously.
/// </summary>
/// <param name="weatherForecast">The weather forecast to add.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the added weather forecast.</returns>
Task<WeatherForecast> AddWeatherForecastAsync(WeatherForecast weatherForecast);
}
}
40 changes: 35 additions & 5 deletions src/dotnet/csharp/WeatherApi/Services/UnsafeService.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
using Microsoft.Data.SqlClient;
using System.Data;
using System.IO;
using System.Text;


namespace WeatherApi.Services
{
/// <summary>
/// Represents a service that performs unsafe operations.
/// </summary>
public class UnSafeService
{
// Assuming "safeDirectory" is the directory you allow access to
private readonly string safeDirectory = "path/to/safe/directory";

/// <summary>
/// Reads a file from the safe directory.
/// </summary>
/// <param name="userInput">The user input representing the file name.</param>
/// <returns>The content of the file as a string.</returns>
/// <exception cref="UnauthorizedAccessException">Thrown when access to the path is denied.</exception>
public string ReadFile(string userInput)
{
using (FileStream fs = File.Open(userInput, FileMode.Open))
// Validate the userInput to prevent path traversal
var fullPath = Path.GetFullPath(Path.Combine(safeDirectory, userInput));
if (!fullPath.StartsWith(safeDirectory))
Copy link

Copilot AI Apr 9, 2025

Choose a reason for hiding this comment

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

Consider ensuring that 'safeDirectory' ends with a directory separator or use a more robust method (e.g., comparing DirectoryInfo objects) to reliably verify that 'fullPath' is confined within 'safeDirectory'.

Suggested change
if (!fullPath.StartsWith(safeDirectory))
var safeDirectoryInfo = new DirectoryInfo(safeDirectory);
var fullPathInfo = new DirectoryInfo(fullPath);
if (!fullPathInfo.FullName.StartsWith(safeDirectoryInfo.FullName))

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI May 14, 2025

Choose a reason for hiding this comment

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

Consider using a more robust file path validation approach. For instance, ensure the safeDirectory ends with a directory separator and use a secure path comparison method to avoid bypasses when safeDirectory appears as a substring in unintended paths.

Suggested change
if (!fullPath.StartsWith(safeDirectory))
if (!fullPath.StartsWith(safeDirectory, StringComparison.OrdinalIgnoreCase) ||
!fullPath.StartsWith(safeDirectory + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))

Copilot uses AI. Check for mistakes.
{
throw new UnauthorizedAccessException("Access to the path is denied.");
}

using (FileStream fs = File.Open(fullPath, FileMode.Open))
{
byte[] b = new byte[1024];
UTF8Encoding temp = new UTF8Encoding(true);
Expand All @@ -23,18 +42,29 @@ public string ReadFile(string userInput)
return null;
}

/// <summary>
/// Gets the product ID for the specified product name.
/// </summary>
/// <param name="productName">The name of the product.</param>
/// <returns>The product ID as an integer.</returns>
public int GetProduct(string productName)
{
using (SqlConnection connection = new SqlConnection("fakeconnectionstring"))
{
// Use parameterized query to prevent SQL injection
SqlCommand sqlCommand = new SqlCommand()
{
CommandText = "SELECT ProductId FROM Products WHERE ProductName = '" + productName + "'",
CommandText = "SELECT ProductId FROM Products WHERE ProductName = @ProductName",
CommandType = CommandType.Text,
Connection = connection
};
sqlCommand.Parameters.AddWithValue("@ProductName", productName);

connection.Open();
var result = sqlCommand.ExecuteScalar();
connection.Close();

SqlDataReader reader = sqlCommand.ExecuteReader();
return reader.GetInt32(0);
return result != null ? Convert.ToInt32(result) : 0;
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions src/dotnet/csharp/WeatherApi/Services/WeatherService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public WeatherService(ILogger<WeatherService> logger, WebApiContext context)
_logger = logger;
}

/// <summary>
/// Retrieves the weather forecast for a given ID asynchronously.
/// </summary>
/// <param name="id">The ID of the weather forecast to retrieve.</param>
/// <returns>The weather forecast with the specified ID.</returns>
public async Task<WeatherForecast> GetWeatherForecastAsync(int id)
{
var weatherForecasts = await _context.GetMockDataAsync.ToListAsync();
Expand All @@ -23,11 +28,35 @@ public async Task<WeatherForecast> GetWeatherForecastAsync(int id)
return weatherForecast;
}

/// <summary>
/// Retrieves the weather forecasts asynchronously.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains the collection of weather forecasts.</returns>
public async Task<IEnumerable<WeatherForecast>> GetWeatherForecastsAsync()
{
return await _context.GetMockDataAsync.ToListAsync();
}

/// <summary>
/// Adds a new weather forecast asynchronously.
/// </summary>
/// <param name="weatherForecast">The weather forecast to add.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the added weather forecast.</returns>
public async Task<WeatherForecast> AddWeatherForecastAsync(WeatherForecast weatherForecast)
{
try
{
return await _context.AddWeatherForecastAsync(weatherForecast);

}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while adding the weather forecast.");
throw; // rethrow the exception to be handled by the caller
}
}



}
}