diff --git a/.github/workflows/verify-azure-function-with-packages.yaml b/.github/workflows/verify-azure-function-with-packages.yaml index d5faa3dba..0ef34fef0 100644 --- a/.github/workflows/verify-azure-function-with-packages.yaml +++ b/.github/workflows/verify-azure-function-with-packages.yaml @@ -26,10 +26,11 @@ jobs: continue-on-error: true run: docker rm -f ${{ env.CONTAINER_RUNTIME_NAME }} + # Build and tag final image, remove dangling images only if there are any - name: Docker Target Final run: | docker build --no-cache . -f ${{ env.DOCKERFILE_PATH }} -t ${{ env.CONTAINER_IMAGE_NAME }} - docker rmi -f $(docker images -f "dangling=true" -q) + docker images -qf dangling=true | xargs --no-run-if-empty docker rmi - name: Docker Run Container run: docker run -d --name ${{ env.CONTAINER_RUNTIME_NAME }} -p 8080:80 ${{ env.CONTAINER_IMAGE_NAME }} diff --git a/docs/overview.md b/docs/overview.md index 6ceddccf9..b83998618 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -6,14 +6,14 @@ you different data than the WebAPI for the same query). We provide a number of different endpoints to provide the most flexibility to integrate to your environment: -- You can run the application using the [CLI](./src/CarbonAware.CLI) and refer +- You can run the application using the [CLI](../src/CarbonAware.CLI) and refer to more documentation [here](./carbon-aware-cli.md). -- You can build a container containing the [WebAPI](./src/CarbonAware.WebApi) +- You can build a container containing the [WebAPI](../src/CarbonAware.WebApi) and connect via REST requests and refer to more documentation [here](./carbon-aware-webapi.md). -- You can reference the [Carbon Aware C# Library](./src/GSF.CarbonAware) in your +- You can reference the [Carbon Aware C# Library](../src/GSF.CarbonAware) in your projects and make use of its functionalities and features. - (Future) You can install the Nuget package and make requests directly. diff --git a/docs/quickstart.md b/docs/quickstart.md index a3b68dfa7..463901e8e 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -96,7 +96,7 @@ Expected output: For example, to get emissions in the `eastus` and `uksouth` region between `2022-08-23 at 11:15am` and `2022-08-23 at 11:20am`, run: -`dotnet run -l eastus,uksouth -t 2022-08-23T11:15 --toTime 2022-08-23T11:20` +`dotnet run emissions -l eastus,uksouth -s 2022-08-23T11:15 -e 2022-08-23T11:20` Expected output: @@ -147,7 +147,7 @@ hour window on the 23rd of August in the regions: `eastus`, `westus`, `westus3`,`uksouth`, run the command: ```bash -dotnet run -l eastus,westus,westus3,uksouth -t 2022-08-23T00:00 --toTime 2022-08-23T23:59 --best +dotnet run -l eastus,westus,westus3,uksouth -s 2022-08-23T00:00 -e 2022-08-23T23:59 --best ``` Expected output: diff --git a/src/CarbonAware.CLI/test/integrationTests/Commands/Emissions/EmissionsCommandTests.cs b/src/CarbonAware.CLI/test/integrationTests/Commands/Emissions/EmissionsCommandTests.cs index af0316ec5..21473e37b 100644 --- a/src/CarbonAware.CLI/test/integrationTests/Commands/Emissions/EmissionsCommandTests.cs +++ b/src/CarbonAware.CLI/test/integrationTests/Commands/Emissions/EmissionsCommandTests.cs @@ -72,7 +72,7 @@ public async Task Emissions_StartAndEndOptions_ReturnsExpectedData() _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"); + var exitCode = await InvokeCliAsync($"emissions -l {location} -s 2022-09-01T00:00:00Z -e 2022-09-01T00:04:00Z"); // Assert Assert.AreEqual(0, exitCode); @@ -117,7 +117,7 @@ public async Task Emissions_BestOption_ReturnsExpectedData() _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 -b"); + var exitCode = await InvokeCliAsync($"emissions -l {location} -s 2022-09-01T00:00:00Z -e 2022-09-01T00:04:00Z -b"); // Assert Assert.AreEqual(0, exitCode); @@ -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)) diff --git a/src/CarbonAware.CLI/test/integrationTests/Commands/EmissionsForecasts/EmissionsForecastsCommandTests.cs b/src/CarbonAware.CLI/test/integrationTests/Commands/EmissionsForecasts/EmissionsForecastsCommandTests.cs index e5703f974..2c232d187 100644 --- a/src/CarbonAware.CLI/test/integrationTests/Commands/EmissionsForecasts/EmissionsForecastsCommandTests.cs +++ b/src/CarbonAware.CLI/test/integrationTests/Commands/EmissionsForecasts/EmissionsForecastsCommandTests.cs @@ -71,8 +71,8 @@ public async Task EmissionsForecasts_StartAndEndOptions_ReturnsExpectedData() var location = "eastus"; var start = DateTimeOffset.UtcNow.AddMinutes(10); var end = start.AddHours(5); - var dataStartAt = start.ToString("yyyy-MM-ddTHH:mm:ss"); - var dataEndAt = end.ToString("yyyy-MM-ddTHH:mm:ss"); + var dataStartAt = start.ToString("yyyy-MM-ddTHH:mm:ssZ"); + var dataEndAt = end.ToString("yyyy-MM-ddTHH:mm:ssZ"); _dataSourceMocker.SetupForecastMock(); // Act diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/src/ElectricityMapsDataSource.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/src/ElectricityMapsDataSource.cs index ea4a99b0e..01e494bbd 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/src/ElectricityMapsDataSource.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/src/ElectricityMapsDataSource.cs @@ -45,8 +45,8 @@ public async Task GetCurrentCarbonIntensityForecastAsync(Loca { ForecastedCarbonIntensityData forecast; var geolocation = await this._locationSource.ToGeopositionLocationAsync(location); - if (geolocation.Latitude != null && geolocation.Latitude != null) - forecast = await this._electricityMapsClient.GetForecastedCarbonIntensityAsync (geolocation.Latitude.ToString() ?? "", geolocation.Longitude.ToString() ?? ""); + if (geolocation.Latitude != null && geolocation.Longitude != null) + forecast = await this._electricityMapsClient.GetForecastedCarbonIntensityAsync (geolocation.LatitudeAsCultureInvariantString(), geolocation.LongitudeAsCultureInvariantString()); else { forecast = await this._electricityMapsClient.GetForecastedCarbonIntensityAsync (geolocation.Name ?? ""); diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/src/ElectricityMapsFreeDataSource.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/src/ElectricityMapsFreeDataSource.cs index 60ad2a61c..d4268e352 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/src/ElectricityMapsFreeDataSource.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/src/ElectricityMapsFreeDataSource.cs @@ -86,15 +86,29 @@ public async Task> GetCarbonIntensityAsync(Location l } List EmissionsDataList = new List(); + var emissionDateTime = gridEmissionData.Data.Datetime; - var emissionData = new EmissionsData() + // periodStartTime should be less than current date time because this method should not handle forecast data. + var shouldReturn = periodStartTime <= DateTimeOffset.UtcNow; + if (shouldReturn && emissionDateTime != null && periodStartTime < periodEndTime) { - Location = location.Name ?? "", - Time = gridEmissionData.Data.Datetime ?? new DateTimeOffset(), - Rating = gridEmissionData.Data.CarbonIntensity ?? 0.0, - Duration = new TimeSpan(0, 0, 0) - }; - EmissionsDataList.Add(emissionData); + // periodEndTime would be set periodStartTime in EmissionHandler if it is not specified. + // So we can assume we should return the most recent data if they equal. + // If not, we should return the data after checking it is within specified time range. + shouldReturn = periodStartTime <= emissionDateTime && emissionDateTime < periodEndTime; + } + + if (shouldReturn) + { + var emissionData = new EmissionsData() + { + Location = location.Name ?? "", + Time = emissionDateTime ?? new DateTimeOffset(), + Rating = gridEmissionData.Data.CarbonIntensity ?? 0.0, + Duration = new TimeSpan(0, 0, 0) + }; + EmissionsDataList.Add(emissionData); + } return EmissionsDataList; } diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/test/ElectricityMapsFreeDataSourceTests.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/test/ElectricityMapsFreeDataSourceTests.cs index 8a8eb39c8..21203ef35 100644 --- a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/test/ElectricityMapsFreeDataSourceTests.cs +++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/test/ElectricityMapsFreeDataSourceTests.cs @@ -36,8 +36,9 @@ public ElectricityMapsFreeDataSourceTests() _dataSource = new ElectricityMapsFreeDataSource(_logger.Object, _ElectricityMapsFreeClient.Object, _locationSource.Object); } - [Test] - public async Task GetCarbonIntensity_ReturnsResultsWhenRecordsFound() + [TestCase(false, TestName = "GetCarbonIntensity_ReturnsResultsWhenRecordsFound without emission date time")] + [TestCase(true, TestName = "GetCarbonIntensity_ReturnsResultsWhenRecordsFound with emission date time")] + public async Task GetCarbonIntensity_ReturnsResultsWhenRecordsFound(bool withEmissionDateTime) { var startDate = DateTimeOffset.UtcNow.AddHours(-10); var endDate = startDate.AddHours(1); @@ -49,6 +50,7 @@ public async Task GetCarbonIntensity_ReturnsResultsWhenRecordsFound() { Data = new Data() { + Datetime = withEmissionDateTime ? startDate.AddMinutes(30) : null, CarbonIntensity = expectedCarbonIntensity, } }; @@ -71,8 +73,9 @@ public async Task GetCarbonIntensity_ReturnsResultsWhenRecordsFound() this._locationSource.Verify(l => l.ToGeopositionLocationAsync(_defaultLocation)); } - [Test] - public async Task GetCarbonIntensity_UseRegionNameWhenCoordinatesNotAvailable() + [TestCase(false, TestName = "GetCarbonIntensity_UseRegionNameWhenCoordinatesNotAvailable without emission date time")] + [TestCase(true, TestName = "GetCarbonIntensity_UseRegionNameWhenCoordinatesNotAvailable with emission date time")] + public async Task GetCarbonIntensity_UseRegionNameWhenCoordinatesNotAvailable(bool withEmissionDateTime) { var startDate = new DateTimeOffset(2022, 4, 18, 12, 32, 42, TimeSpan.FromHours(-6)); var endDate = startDate.AddMinutes(1); @@ -84,6 +87,7 @@ public async Task GetCarbonIntensity_UseRegionNameWhenCoordinatesNotAvailable() { Data = new Data() { + Datetime = withEmissionDateTime ? startDate.AddSeconds(30) : null, CarbonIntensity = expectedCarbonIntensity, } }; diff --git a/src/CarbonAware.WebApi/src/Controllers/CarbonAwareController.cs b/src/CarbonAware.WebApi/src/Controllers/CarbonAwareController.cs index c4fb2e73f..130953ddc 100644 --- a/src/CarbonAware.WebApi/src/Controllers/CarbonAwareController.cs +++ b/src/CarbonAware.WebApi/src/Controllers/CarbonAwareController.cs @@ -74,9 +74,9 @@ public async Task GetEmissionsDataForLocationsByTime([FromQuery] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ValidationProblemDetails))] [HttpGet("bylocation")] public async Task 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 { diff --git a/src/CarbonAware.WebApi/src/Models/CarbonIntensityDTO.cs b/src/CarbonAware.WebApi/src/Models/CarbonIntensityDTO.cs index bcaf7fccf..4396c9fbe 100644 --- a/src/CarbonAware.WebApi/src/Models/CarbonIntensityDTO.cs +++ b/src/CarbonAware.WebApi/src/Models/CarbonIntensityDTO.cs @@ -5,6 +5,8 @@ namespace CarbonAware.WebApi.Models; [Serializable] public record CarbonIntensityDTO { + private DateTimeOffset? _startTime; + private DateTimeOffset? _endTime; /// the location name where workflow is run /// eastus [JsonPropertyName("location")] @@ -13,12 +15,12 @@ public record CarbonIntensityDTO /// the time at which the workflow we are measuring carbon intensity for started /// 2022-03-01T15:30:00Z [JsonPropertyName("startTime")] - public DateTimeOffset? StartTime { get; set; } + public DateTimeOffset? StartTime { get => _startTime; set => _startTime = value?.ToUniversalTime(); } /// the time at which the workflow we are measuring carbon intensity for ended /// 2022-03-01T18:30:00Z [JsonPropertyName("endTime")] - public DateTimeOffset? EndTime { get; set; } + public DateTimeOffset? EndTime { get => _endTime; set => _endTime = value?.ToUniversalTime(); } /// Value of the marginal carbon intensity in grams per kilowatt-hour. /// 345.434 diff --git a/src/CarbonAware.WebApi/test/integrationTests/CarbonAwareControllerTests.cs b/src/CarbonAware.WebApi/test/integrationTests/CarbonAwareControllerTests.cs index 700c3ffc8..d4954bbd6 100644 --- a/src/CarbonAware.WebApi/test/integrationTests/CarbonAwareControllerTests.cs +++ b/src/CarbonAware.WebApi/test/integrationTests/CarbonAwareControllerTests.cs @@ -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] @@ -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(); + 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(); + 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(); + 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(); + 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); @@ -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>(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] @@ -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); @@ -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); @@ -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 }); diff --git a/src/CarbonAware.WebApi/test/integrationTests/IntegrationTestingBase.cs b/src/CarbonAware.WebApi/test/integrationTests/IntegrationTestingBase.cs index 5dcb499bd..5dd7310b5 100644 --- a/src/CarbonAware.WebApi/test/integrationTests/IntegrationTestingBase.cs +++ b/src/CarbonAware.WebApi/test/integrationTests/IntegrationTestingBase.cs @@ -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; @@ -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(); + _client = _factory.CreateClient(); + } + _dataSourceMocker?.Initialize(); }