Skip to content

Commit

Permalink
Merge pull request #294 from microsoft/241/intgration_tests
Browse files Browse the repository at this point in the history
Integration tests coverage for WebAPI and CLI
  • Loading branch information
Willmish authored Sep 12, 2023
2 parents c2cb730 + 3bac48b commit 730a3bc
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,26 @@ public async Task Emissions_AverageOption_ReturnsExpectedData()
Assert.IsNotNull(firstResult["Duration"]);
}

[Test]
public async Task Average_Best_ReturnsExpectedError()
{
// Arrange
var start = DateTimeOffset.Parse("2022-09-01T00:00:00Z");
var end = DateTimeOffset.Parse("2022-09-01T03:00:00Z");
var location = "eastus";
_dataSourceMocker.SetupDataMock(start, end, location);

// Act
var exitCode = await InvokeCliAsync($"emissions -l {location} -s 2022-09-01T02:01:00Z -e 2022-09-01T02:04:00Z -a -best");
// Assert
Assert.AreEqual(1, exitCode);
var expectedError = "Options --average and --best cannot be used together Option '-s' expects a single argument but 2 were provided. ";
// Whitespace characters regex
var regex = @"\s+";
var output = Regex.Replace(_console.Error.ToString()!, regex, " ");

Assert.AreEqual(expectedError, output);
}
private void IgnoreTestForDataSource(string reasonMessage, params DataSourceType[] ignoredDataSources)
{
if (ignoredDataSources.Contains(_dataSource))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ public async Task<IActionResult> GetEmissionsDataForLocationsByTime([FromQuery]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ValidationProblemDetails))]
[HttpGet("bylocation")]
public async Task<IActionResult> GetEmissionsDataForLocationByTime(
[FromQuery, SwaggerParameter(Required = true)] string location,
DateTimeOffset? startTime = null,
DateTimeOffset? endTime = null)
[FromQuery, SwaggerParameter(Required = true)] string location,
[FromQuery(Name = "time")] DateTimeOffset? startTime = null,
[FromQuery(Name = "toTime")] DateTimeOffset? endTime = null)
{
var parameters = new EmissionsDataForLocationsParametersDTO
{
Expand Down
6 changes: 4 additions & 2 deletions src/CarbonAware.WebApi/src/Models/CarbonIntensityDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace CarbonAware.WebApi.Models;
[Serializable]
public record CarbonIntensityDTO
{
private DateTimeOffset? _startTime;
private DateTimeOffset? _endTime;
/// <summary>the location name where workflow is run </summary>
/// <example>eastus</example>
[JsonPropertyName("location")]
Expand All @@ -13,12 +15,12 @@ public record CarbonIntensityDTO
/// <summary>the time at which the workflow we are measuring carbon intensity for started </summary>
/// <example>2022-03-01T15:30:00Z</example>
[JsonPropertyName("startTime")]
public DateTimeOffset? StartTime { get; set; }
public DateTimeOffset? StartTime { get => _startTime; set => _startTime = value?.ToUniversalTime(); }

/// <summary> the time at which the workflow we are measuring carbon intensity for ended</summary>
/// <example>2022-03-01T18:30:00Z</example>
[JsonPropertyName("endTime")]
public DateTimeOffset? EndTime { get; set; }
public DateTimeOffset? EndTime { get => _endTime; set => _endTime = value?.ToUniversalTime(); }

/// <summary>Value of the marginal carbon intensity in grams per kilowatt-hour.</summary>
/// <example>345.434</example>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ class CarbonAwareControllerTests : IntegrationTestingBase
private readonly string healthURI = "/health";
private readonly string fakeURI = "/fake-endpoint";
private readonly string bestLocationsURI = "/emissions/bylocations/best";
private readonly string bylocationsURI = "/emissions/bylocations";
private readonly string bylocationURI = "/emissions/bylocation";
private readonly string currentForecastURI = "/emissions/forecasts/current";
private readonly string batchForecastURI = "/emissions/forecasts/batch";
private readonly string averageCarbonIntensityURI = "/emissions/average-carbon-intensity";
private readonly string batchAverageCarbonIntensityURI = "/emissions/average-carbon-intensity/batch";


public CarbonAwareControllerTests(DataSourceType dataSource) : base(dataSource) { }

[Test]
Expand All @@ -47,9 +48,89 @@ public async Task FakeEndPoint_ReturnsNotFound()
}

//ISO8601: YYYY-MM-DD
[TestCase("2022-1-1T04:05:06Z", "2022-1-2T04:05:06Z", "eastus")]
[TestCase("2021-12-25", "2021-12-26", "westus")]
public async Task BestLocations_ReturnsOK(DateTimeOffset start, DateTimeOffset end, string location)
[TestCase("2022-1-1T04:05:06Z", "2022-1-2T04:05:06Z", "eastus", nameof(ByLocationURI_ReturnsOK) + "0")]
[TestCase("2021-12-25T00:00:00+06:00", "2021-12-26T00:00:00+06:00", "westus", nameof(ByLocationURI_ReturnsOK) + "1")]
public async Task ByLocationURI_ReturnsOK(DateTimeOffset start, DateTimeOffset end, string location, string expectedResultName)
{
//Sets up any data endpoints needed for mocking purposes
_dataSourceMocker?.SetupDataMock(start, end, location);

//Call the private method to construct with parameters
var queryStrings = new Dictionary<string, string>();
queryStrings["location"] = location;
queryStrings["time"] = $"{start:O}";
queryStrings["toTime"] = $"{end:O}";

var endpointURI = ConstructUriWithQueryString(bylocationURI, queryStrings);

//Get response and response content
var result = await _client.GetAsync(endpointURI);

Assert.That(result, Is.Not.Null);
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}

[TestCase("location", "", TestName = "empty location query string")]
[TestCase("non-location-param", "", TestName = "location param not present")]
public async Task ByLocation_EmptyLocationQueryString_ReturnsBadRequest(string queryString, string value)
{
//Call the private method to construct with parameters
var queryStrings = new Dictionary<string, string>();
queryStrings[queryString] = value;

var endpointURI = ConstructUriWithQueryString(bylocationURI, queryStrings);

//Get response and response content
var result = await _client.GetAsync(endpointURI);

Assert.That(result, Is.Not.Null);
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}

//ISO8601: YYYY-MM-DD
[TestCase("2022-1-1T04:05:06Z", "2022-1-2T04:05:06Z", "eastus", nameof(ByLocationsURI_ReturnsOK) + "0")]
[TestCase("2021-12-25T00:00:00+06:00", "2021-12-26T00:00:00+06:00", "westus", nameof(ByLocationsURI_ReturnsOK) + "1")]
public async Task ByLocationsURI_ReturnsOK(DateTimeOffset start, DateTimeOffset end, string location, string expectedResultName)
{
//Sets up any data endpoints needed for mocking purposes
_dataSourceMocker?.SetupDataMock(start, end, location);

//Call the private method to construct with parameters
var queryStrings = new Dictionary<string, string>();
queryStrings["location"] = location;
queryStrings["time"] = $"{start:O}";
queryStrings["toTime"] = $"{end:O}";

var endpointURI = ConstructUriWithQueryString(bylocationsURI, queryStrings);

//Get response and response content
var result = await _client.GetAsync(endpointURI);

Assert.That(result, Is.Not.Null);
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}

[TestCase("location", "", TestName = "empty location query string")]
[TestCase("non-location-param", "", TestName = "location param not present")]
public async Task ByLocations_EmptyLocationQueryString_ReturnsBadRequest(string queryString, string value)
{
//Call the private method to construct with parameters
var queryStrings = new Dictionary<string, string>();
queryStrings[queryString] = value;

var endpointURI = ConstructUriWithQueryString(bylocationsURI, queryStrings);

//Get response and response content
var result = await _client.GetAsync(endpointURI);

Assert.That(result, Is.Not.Null);
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}

//ISO8601: YYYY-MM-DD
[TestCase("2022-1-1T04:05:06Z", "2022-1-2T04:05:06Z", "eastus", nameof(BestLocations_ReturnsOK) + "0")]
[TestCase("2021-12-25T00:00:00+06:00", "2021-12-26T00:00:00+06:00", "westus", nameof(BestLocations_ReturnsOK) + "1")]
public async Task BestLocations_ReturnsOK(DateTimeOffset start, DateTimeOffset end, string location, string expectedResultName)
{
//Sets up any data endpoints needed for mocking purposes
_dataSourceMocker?.SetupDataMock(start, end, location);
Expand Down Expand Up @@ -102,6 +183,20 @@ public async Task EmissionsForecastsCurrent_SupportedDataSources_ReturnsOk()
var result = await _client.GetAsync(endpointURI);
Assert.That(result, Is.Not.Null);
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK));
using (var data = await result!.Content.ReadAsStreamAsync())
{
Assert.That(data, Is.Not.Null);
var forecasts = await JsonSerializer.DeserializeAsync<IEnumerable<EmissionsForecastDTO>>(data);
Assert.That(forecasts, Is.Not.Null);
Assert.AreEqual(forecasts!.Count(), 1);
foreach (var forecast in forecasts!)
{
Assert.That(forecast.RequestedAt, Is.Not.Null);
Assert.That(forecast.GeneratedAt, Is.Not.Null);
Assert.That(forecast.OptimalDataPoints, Is.Not.Null);
Assert.That(forecast.ForecastData, Is.Not.Null);
}
}
}

[Test]
Expand Down Expand Up @@ -205,9 +300,9 @@ public async Task EmissionsForecastsBatch_SupportedDataSources_ReturnsOk(string
}
}

[TestCase("2022-1-1T04:05:06Z", "2022-1-2T04:05:06Z", "eastus", TestName = "EmissionsMarginalCarbonIntensity expects OK for full datetime")]
[TestCase("2021-12-25", "2021-12-26", "westus", TestName = "EmissionsMarginalCarbonIntensity expects OK date only, no time")]
public async Task EmissionsMarginalCarbonIntensity_ReturnsOk(string start, string end, string location)
[TestCase("2022-1-1T04:05:06Z", "2022-1-2T04:05:06Z", "eastus", nameof(EmissionsMarginalCarbonIntensity_ReturnsOk) + "0", TestName = "EmissionsMarginalCarbonIntensity expects OK for full datetime")]
[TestCase("2021-12-25T00:00:00+06:00", "2021-12-26T00:00:00+06:00", "westus", nameof(EmissionsMarginalCarbonIntensity_ReturnsOk) + "1", TestName = "EmissionsMarginalCarbonIntensity expects OK date only, no time")]
public async Task EmissionsMarginalCarbonIntensity_ReturnsOk(string start, string end, string location, string expectedResultName)
{
IgnoreTestForDataSource($"data source does not implement '{averageCarbonIntensityURI}'", DataSourceType.ElectricityMapsFree);

Expand Down Expand Up @@ -271,9 +366,9 @@ public async Task EmissionsMarginalCarbonIntensityBatch_MissingRequiredParams_Re
Assert.That(result!.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}

[TestCase("2022-01-01T04:05:06Z", "2022-01-02T04:05:06Z", "eastus", 1, TestName = "EmissionsMarginalCarbonIntensityBatch expects OK for single element batch")]
[TestCase("2021-12-25", "2021-12-26", "westus", 3, TestName = "EmissionsMarginalCarbonIntensityBatch expects OK for multiple element batch")]
public async Task EmissionsMarginalCarbonIntensityBatch_SupportedDataSources_ReturnsOk(string start, string end, string location, int nelems)
[TestCase("2022-01-01T04:05:06Z", "2022-01-02T04:05:06Z", "eastus", 1, nameof(EmissionsMarginalCarbonIntensityBatch_SupportedDataSources_ReturnsOk) + "0", TestName = "EmissionsMarginalCarbonIntensityBatch expects OK for single element batch")]
[TestCase("2021-12-25T00:00:00+06:00", "2021-12-26T00:00:00+06:00", "westus", 3, nameof(EmissionsMarginalCarbonIntensityBatch_SupportedDataSources_ReturnsOk) + "1", TestName = "EmissionsMarginalCarbonIntensityBatch expects OK for multiple element batch")]
public async Task EmissionsMarginalCarbonIntensityBatch_SupportedDataSources_ReturnsOk(string start, string end, string location, int nelems, string expectedResultName)
{
IgnoreTestForDataSource($"data source does not implement '{batchAverageCarbonIntensityURI}'", DataSourceType.ElectricityMapsFree);

Expand All @@ -282,7 +377,7 @@ public async Task EmissionsMarginalCarbonIntensityBatch_SupportedDataSources_Ret
_dataSourceMocker?.SetupDataMock(startDate, endDate, location);
var intesityData = Enumerable.Range(0, nelems).Select(x => new
{
location = location,
location,
startTime = start,
endTime = end
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void Setup()
Environment.SetEnvironmentVariable("DataSources__EmissionsDataSource", "Json");
Environment.SetEnvironmentVariable("DataSources__ForecastDataSource", "");
Environment.SetEnvironmentVariable("DataSources__Configurations__Json__Type", "Json");
Environment.SetEnvironmentVariable("DataSources__Configurations__Json__DataFileLocation", "demo.json");
Environment.SetEnvironmentVariable("DataSources__Configurations__Json__DataFileLocation", "test-data-azure-emissions.json");
Environment.SetEnvironmentVariable("CarbonAwareVars__VerboseApi", "true");
_dataSourceMocker = new JsonDataSourceMocker();
break;
Expand Down Expand Up @@ -127,6 +127,15 @@ public void Setup()
[SetUp]
public void SetupTests()
{
if (_dataSource == DataSourceType.JSON)
{
// To force WebApplication to consume new JSON datasorce it needs to be restarted.
// This is a direct result of JSON caching in the CatbonAware code
_factory.Dispose();
_factory = new WebApplicationFactory<Program>();
_client = _factory.CreateClient();
}

_dataSourceMocker?.Initialize();
}

Expand Down

0 comments on commit 730a3bc

Please sign in to comment.