diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 0000000..ec9d488 --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,23 @@ +name: Codecov + +on: + push: + branches-ignore: + - gh-pages + +jobs: + test_and_upload_coverage: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 3.1.100 + - name: Test with dotnet + run: dotnet test --configuration Release /p:CollectCoverage=true /p:CoverletOutputFormat=opencover + - name: Code coverage + uses: codecov/codecov-action@v1 + diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 3177d92..e4bddb4 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -19,4 +19,5 @@ jobs: - name: Build with dotnet run: dotnet build --configuration Release - name: Test with dotnet - run: dotnet test --configuration Release + run: dotnet test + diff --git a/README.md b/README.md index be70321..5df3f83 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,24 @@ [![NuGet Downloads](https://img.shields.io/nuget/dt/PVOutput.Net.svg?logo=nuget)](https://www.nuget.org/packages/PVOutput.Net/) [![fuget](https://www.fuget.org/packages/PVOutput.Net/badge.svg)](https://www.fuget.org/packages/PVOutput.Net) ![.NET Core](https://img.shields.io/github/workflow/status/pyrocumulus/PVOutput.Net/.NET%20Core/develop) +![Code coverage](https://img.shields.io/codecov/c/github/pyrocumulus/PVOutput.Net/develop) ## Installation -Installation can be done through installation of the [NuGet package](https://www.nuget.org/packages/PVOutput.Net/). +Installation can be done through installation of the [NuGet package](https://www.nuget.org/packages/PVOutput.Net/): -## Usage +```posh +PM> Install-Package PVOutput.Net +``` + +## Support + +This library is targeting .NET Standard 2.0 and above. For full compatibility details, check the [Microsoft Docs](https://docs.microsoft.com/nl-nl/dotnet/standard/net-standard#net-implementation-support). + +**Please note:** that the default branch of the repository is `develop`. This means that it can contain bugfixes/features that are not yet available in the NuGet package. +See [master](https://github.com/pyrocumulus/pvoutput.net/tree/master) for the source code, that was used for building the NuGet package. + +## Basic usage ### Getting data out of PVOutput.org @@ -60,7 +72,7 @@ For more information on usage, please see the [documentation](https://pyrocumulu ## API Coverage -The library covers almost nearly all the official PVOutput API exposes. See [documentation](https://pyrocumulus.github.io/pvoutput.net/) for details. +The library covers almost all of the methods the official PVOutput API exposes. See [documentation](https://pyrocumulus.github.io/pvoutput.net/) for details. ## Building the project diff --git a/docfx/index.md b/docfx/index.md index b977a5e..48c7471 100644 --- a/docfx/index.md +++ b/docfx/index.md @@ -5,7 +5,15 @@ ## Installation -Installation can be done through installation of the [NuGet package](https://www.nuget.org/packages/PVOutput.Net/). +Installation can be done through installation of the [NuGet package](https://www.nuget.org/packages/PVOutput.Net/): + +```posh +PM> Install-Package PVOutput.Net +``` + +## Support + +This library is targeting .NET Standard 2.0 and above. For full compatibility details, check the [Microsoft Docs](https://docs.microsoft.com/nl-nl/dotnet/standard/net-standard#net-implementation-support). ## Usage diff --git a/src/PVOutput.Net/Modules/ExtendedService.cs b/src/PVOutput.Net/Modules/ExtendedService.cs index 2d1d641..8e92580 100644 --- a/src/PVOutput.Net/Modules/ExtendedService.cs +++ b/src/PVOutput.Net/Modules/ExtendedService.cs @@ -3,6 +3,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Dawn; using PVOutput.Net.Objects; using PVOutput.Net.Requests.Handler; using PVOutput.Net.Requests.Modules; @@ -44,6 +45,9 @@ public Task> GetRecentExtendedDataAsync(Cancellation /// List of extended data objects. public Task> GetExtendedDataForPeriodAsync(DateTime fromDate, DateTime toDate, int? limit = null, CancellationToken cancellationToken = default) { + Guard.Argument(toDate, nameof(toDate)).GreaterThan(fromDate); + Guard.Argument(limit, nameof(limit)).LessThan(50); + var handler = new RequestHandler(Client); return handler.ExecuteArrayRequestAsync(new ExtendedRequest() { FromDate = fromDate, ToDate = toDate, Limit = limit }, cancellationToken); } diff --git a/src/PVOutput.Net/Modules/MissingService.cs b/src/PVOutput.Net/Modules/MissingService.cs index cf05a5b..bed652b 100644 --- a/src/PVOutput.Net/Modules/MissingService.cs +++ b/src/PVOutput.Net/Modules/MissingService.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Dawn; using PVOutput.Net.Objects; using PVOutput.Net.Requests.Handler; using PVOutput.Net.Requests.Modules; @@ -28,6 +29,8 @@ internal MissingService(PVOutputClient client) : base(client) /// List of missing dates public Task> GetMissingDaysInPeriodAsync(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken = default) { + Guard.Argument(toDate, nameof(toDate)).GreaterThan(fromDate); + var handler = new RequestHandler(Client); return handler.ExecuteSingleItemRequestAsync(new MissingRequest { FromDate = fromDate, ToDate = toDate }, cancellationToken); } diff --git a/src/PVOutput.Net/Modules/OutputService.cs b/src/PVOutput.Net/Modules/OutputService.cs index 15e3f2a..9bfa4fd 100644 --- a/src/PVOutput.Net/Modules/OutputService.cs +++ b/src/PVOutput.Net/Modules/OutputService.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Dawn; using PVOutput.Net.Enums; using PVOutput.Net.Objects; +using PVOutput.Net.Objects.Core; using PVOutput.Net.Requests.Handler; using PVOutput.Net.Requests.Modules; using PVOutput.Net.Responses; @@ -30,8 +32,9 @@ internal OutputService(PVOutputClient client) : base(client) /// Output for the requested date. public Task> GetOutputForDateAsync(DateTime date, bool getInsolation = false, int? systemId = null, CancellationToken cancellationToken = default) { - var handler = new RequestHandler(Client); + Guard.Argument(date, nameof(date)).Max(DateTime.Today); + var handler = new RequestHandler(Client); return handler.ExecuteSingleItemRequestAsync(new OutputRequest { FromDate = date, ToDate = date, SystemId = systemId, Insolation = getInsolation }, cancellationToken); } @@ -46,8 +49,10 @@ public Task> GetOutputForDateAsync(DateTime date, bool /// Outputs for the requested period. public Task> GetOutputsForPeriodAsync(DateTime fromDate, DateTime toDate, bool getInsolation = false, int? systemId = null, CancellationToken cancellationToken = default) { - var handler = new RequestHandler(Client); + Guard.Argument(toDate, nameof(toDate)).GreaterThan(fromDate).IsNoFutureDate().NoTimeComponent(); + Guard.Argument(fromDate, nameof(fromDate)).NoTimeComponent(); + var handler = new RequestHandler(Client); return handler.ExecuteArrayRequestAsync(new OutputRequest { FromDate = fromDate, ToDate = toDate, SystemId = systemId, Insolation = getInsolation }, cancellationToken); } @@ -60,8 +65,9 @@ public Task> GetOutputsForPeriodAsync(DateTime fr /// Team output for the requested date. public Task> GetTeamOutputForDateAsync(DateTime date, int teamId, CancellationToken cancellationToken = default) { - var handler = new RequestHandler(Client); + Guard.Argument(date, nameof(date)).Max(DateTime.Today); + var handler = new RequestHandler(Client); return handler.ExecuteSingleItemRequestAsync(new OutputRequest { FromDate = date, ToDate = date, TeamId = teamId }, cancellationToken); } @@ -75,8 +81,10 @@ public Task> GetTeamOutputForDateAsync(DateTime da /// Team outputs Outputs for the requested period. public Task> GetTeamOutputsForPeriodAsync(DateTime fromDate, DateTime toDate, int teamId, CancellationToken cancellationToken = default) { - var handler = new RequestHandler(Client); + Guard.Argument(toDate, nameof(toDate)).GreaterThan(fromDate).IsNoFutureDate().NoTimeComponent(); + Guard.Argument(fromDate, nameof(fromDate)).NoTimeComponent(); + var handler = new RequestHandler(Client); return handler.ExecuteArrayRequestAsync(new OutputRequest { FromDate = fromDate, ToDate = toDate, TeamId = teamId }, cancellationToken); } @@ -90,8 +98,10 @@ public Task> GetTeamOutputsForPeriodAsync(Dat /// Aggregated outputs for the requested period. public Task> GetAggregatedOutputsAsync(DateTime fromDate, DateTime toDate, AggregationPeriod period, CancellationToken cancellationToken = default) { - var handler = new RequestHandler(Client); + Guard.Argument(toDate, nameof(toDate)).GreaterThan(fromDate).IsNoFutureDate().NoTimeComponent(); + Guard.Argument(fromDate, nameof(fromDate)).NoTimeComponent(); + var handler = new RequestHandler(Client); return handler.ExecuteArrayRequestAsync(new OutputRequest { FromDate = fromDate, ToDate = toDate, Aggregation = period }, cancellationToken); } @@ -104,6 +114,8 @@ public Task> GetAggregatedOutputsAsync( /// If the operation succeeded. public Task AddOutputAsync(IOutputPost output, CancellationToken cancellationToken = default) { + Guard.Argument(output, nameof(output)).NotNull(); + var handler = new RequestHandler(Client); return handler.ExecutePostRequestAsync(new AddOutputRequest() { Output = output }, cancellationToken); } @@ -117,9 +129,10 @@ public Task AddOutputAsync(IOutputPost output, Cancellati /// If the operation succeeded. public Task AddBatchOutputAsync(IEnumerable outputs, CancellationToken cancellationToken = default) { + Guard.Argument(outputs, nameof(outputs)).NotNull().NotEmpty(); + var handler = new RequestHandler(Client); return handler.ExecutePostRequestAsync(new AddBatchOutputRequest() { Outputs = outputs }, cancellationToken); } - } } diff --git a/src/PVOutput.Net/Modules/SearchService.cs b/src/PVOutput.Net/Modules/SearchService.cs index 229920d..e1bea17 100644 --- a/src/PVOutput.Net/Modules/SearchService.cs +++ b/src/PVOutput.Net/Modules/SearchService.cs @@ -3,6 +3,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Dawn; using PVOutput.Net.Objects; using PVOutput.Net.Requests.Handler; using PVOutput.Net.Requests.Modules; @@ -31,8 +32,9 @@ internal SearchService(PVOutputClient client) : base(client) /// A list of search results. public Task> SearchAsync(string searchQuery, PVCoordinate? coordinate = null, CancellationToken cancellationToken = default) { - var handler = new RequestHandler(Client); + Guard.Argument(searchQuery, nameof(searchQuery)).NotEmpty().NotNull(); + var handler = new RequestHandler(Client); return handler.ExecuteArrayRequestAsync(new SearchRequest { SearchQuery = searchQuery, Coordinate = coordinate }, cancellationToken); } } diff --git a/src/PVOutput.Net/Modules/StatisticsService.cs b/src/PVOutput.Net/Modules/StatisticsService.cs index 9201d17..e546d21 100644 --- a/src/PVOutput.Net/Modules/StatisticsService.cs +++ b/src/PVOutput.Net/Modules/StatisticsService.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Dawn; using PVOutput.Net.Objects; using PVOutput.Net.Requests.Handler; using PVOutput.Net.Requests.Modules; @@ -46,6 +47,8 @@ public Task> GetLifetimeStatisticsAsync(bool includ /// Statistical information for the requested period. public Task> GetStatisticsForPeriodAsync(DateTime fromDate, DateTime toDate, bool includeConsumptionAndImport = false, bool includeCreditDebit = false, int? systemId = null, CancellationToken cancellationToken = default) { + Guard.Argument(toDate, nameof(toDate)).GreaterThan(fromDate); + var handler = new RequestHandler(Client); return handler.ExecuteSingleItemRequestAsync(new StatisticPeriodRequest { FromDate = fromDate, ToDate = toDate, SystemId = systemId, IncludeConsumptionImport = includeConsumptionAndImport, IncludeCreditDebit = includeCreditDebit }, cancellationToken); } diff --git a/src/PVOutput.Net/Modules/StatusService.cs b/src/PVOutput.Net/Modules/StatusService.cs index 75df167..1f1da8b 100644 --- a/src/PVOutput.Net/Modules/StatusService.cs +++ b/src/PVOutput.Net/Modules/StatusService.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Dawn; using PVOutput.Net.Objects; +using PVOutput.Net.Objects.Core; using PVOutput.Net.Requests.Handler; using PVOutput.Net.Requests.Modules; using PVOutput.Net.Responses; @@ -20,7 +22,7 @@ internal StatusService(PVOutputClient client) : base(client) } /// - /// Get the system status a a specific moment. + /// Get the system status at a specific moment. /// /// Moment to retrieve the system status for. /// Retrieve status for a specific system. Note: this is a donation only parameter. @@ -28,6 +30,8 @@ internal StatusService(PVOutputClient client) : base(client) /// Status at the specified moment. public Task> GetStatusForDateTimeAsync(DateTime moment, int? systemId = null, CancellationToken cancellationToken = default) { + Guard.Argument(moment, nameof(moment)).IsNoFutureDate(); + var handler = new RequestHandler(Client); return handler.ExecuteSingleItemRequestAsync(new GetStatusRequest { Date = moment, SystemId = systemId }, cancellationToken); } @@ -45,6 +49,8 @@ public Task> GetStatusForDateTimeAsync(DateTime moment /// Statusses for the specified period. public Task> GetHistoryForPeriodAsync(DateTime fromDateTime, DateTime toDateTime, bool ascending = false, int? systemId = null, bool extendedData = false, int? limit = null, CancellationToken cancellationToken = default) { + Guard.Argument(toDateTime, nameof(toDateTime)).GreaterThan(fromDateTime).IsNoFutureDate(); + var handler = new RequestHandler(Client); return handler.ExecuteArrayRequestAsync( new GetStatusRequest { Date = fromDateTime.Date, From = fromDateTime, To = toDateTime, Ascending = ascending, SystemId = systemId, Extended = extendedData, Limit = limit, History = true }, cancellationToken); @@ -56,10 +62,12 @@ public Task> GetHistoryForPeriodAsync(Date /// Minimum datetime for the requested range. /// Maximum datetime for the requested range. /// Retrieve statuses for a specific system. Note: this is a donation only parameter. - /// - /// + /// A cancellation token for the request. + /// Day statistics for the specified period. public Task> GetDayStatisticsForPeriodAsync(DateTime fromDateTime, DateTime toDateTime, int? systemId = null, CancellationToken cancellationToken = default) { + Guard.Argument(toDateTime, nameof(toDateTime)).GreaterThan(fromDateTime).IsNoFutureDate(); + var handler = new RequestHandler(Client); var response = handler.ExecuteSingleItemRequestAsync(new GetDayStatisticsRequest { Date = fromDateTime.Date, From = fromDateTime, To = toDateTime, SystemId = systemId }, cancellationToken); @@ -88,6 +96,8 @@ private static PVOutputResponse AddRequestedDate(TaskIf the operation succeeded. public Task AddStatusAsync(IStatusPost status, CancellationToken cancellationToken = default) { + Guard.Argument(status, nameof(status)).NotNull(); + var handler = new RequestHandler(Client); return handler.ExecutePostRequestAsync(new AddStatusRequest() { StatusPost = status }, cancellationToken); } @@ -101,6 +111,8 @@ public Task AddStatusAsync(IStatusPost status, Cancellati /// If the operation succeeded. public Task> AddBatchStatusAsync(IEnumerable statuses, CancellationToken cancellationToken = default) { + Guard.Argument(statuses, nameof(statuses)).NotNull().NotEmpty(); + var handler = new RequestHandler(Client); return handler.ExecuteArrayRequestAsync(new AddBatchStatusRequest() { StatusPosts = statuses }, cancellationToken); ; } @@ -114,6 +126,8 @@ public Task> AddBatchStatusAsync(I /// If the operation succeeded. public Task DeleteStatusAsync(DateTime moment, CancellationToken cancellationToken = default) { + Guard.Argument(moment, nameof(moment)).IsNoFutureDate(); + var handler = new RequestHandler(Client); return handler.ExecutePostRequestAsync(new DeleteStatusRequest() { Timestamp = moment }, cancellationToken); } diff --git a/src/PVOutput.Net/Modules/SupplyService.cs b/src/PVOutput.Net/Modules/SupplyService.cs index 75ae43f..fc565bb 100644 --- a/src/PVOutput.Net/Modules/SupplyService.cs +++ b/src/PVOutput.Net/Modules/SupplyService.cs @@ -31,9 +31,7 @@ internal SupplyService(PVOutputClient client) : base(client) public Task> GetSupplyAsync(string timeZone = null, string regionKey = null, CancellationToken cancellationToken = default) { var handler = new RequestHandler(Client); - - return handler.ExecuteArrayRequestAsync( - new SupplyRequest { TimeZone = timeZone, RegionKey = regionKey }, cancellationToken); + return handler.ExecuteArrayRequestAsync(new SupplyRequest { TimeZone = timeZone, RegionKey = regionKey }, cancellationToken); } } } diff --git a/src/PVOutput.Net/Modules/SystemService.cs b/src/PVOutput.Net/Modules/SystemService.cs index 7e2f3d9..b06497d 100644 --- a/src/PVOutput.Net/Modules/SystemService.cs +++ b/src/PVOutput.Net/Modules/SystemService.cs @@ -25,7 +25,6 @@ internal SystemService(PVOutputClient client) : base(client) public Task> GetOwnSystemAsync(CancellationToken cancellationToken = default) { var handler = new RequestHandler(Client); - return handler.ExecuteSingleItemRequestAsync(new SystemRequest(), cancellationToken); } @@ -39,7 +38,6 @@ public Task> GetOwnSystemAsync(CancellationToken cance public Task> GetOtherSystemAsync(int systemId, CancellationToken cancellationToken = default) { var handler = new RequestHandler(Client); - return handler.ExecuteSingleItemRequestAsync(new SystemRequest { SystemId = systemId, MonthlyEstimates = false }, cancellationToken); } } diff --git a/src/PVOutput.Net/Objects/Core/GuardExtensions.cs b/src/PVOutput.Net/Objects/Core/GuardExtensions.cs new file mode 100644 index 0000000..76f1e18 --- /dev/null +++ b/src/PVOutput.Net/Objects/Core/GuardExtensions.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Dawn; + +namespace PVOutput.Net.Objects.Core +{ + internal static class GuardExtensions + { + internal static Guard.ArgumentInfo IsNoFutureDate(this Guard.ArgumentInfo argument) + { + return argument.LessThan(DateTime.Today.AddDays(1)); + } + + public static ref readonly Guard.ArgumentInfo NoTimeComponent(in this Guard.ArgumentInfo argument) + { + if (argument.Value.TimeOfDay != TimeSpan.Zero) + { + throw Guard.Fail(new ArgumentException( + $"{argument.Name} has a time component." + + "Please only use DateTime.Date instead.", + argument.Name)); + } + + return ref argument; + } + } +} diff --git a/src/PVOutput.Net/Objects/ExtendedDataConfiguration.cs b/src/PVOutput.Net/Objects/ExtendedDataConfiguration.cs index 9627164..8b3d1d6 100644 --- a/src/PVOutput.Net/Objects/ExtendedDataConfiguration.cs +++ b/src/PVOutput.Net/Objects/ExtendedDataConfiguration.cs @@ -6,7 +6,7 @@ namespace PVOutput.Net.Objects /// /// Element describing a configured extended data value /// - public struct ExtendedDataConfiguration : IEquatable + public readonly struct ExtendedDataConfiguration : IEquatable { /// The label that the extended value has public string Label { get; } diff --git a/src/PVOutput.Net/Objects/OutputPostBuilder.cs b/src/PVOutput.Net/Objects/OutputPostBuilder.cs index e364635..a775a34 100644 --- a/src/PVOutput.Net/Objects/OutputPostBuilder.cs +++ b/src/PVOutput.Net/Objects/OutputPostBuilder.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Resources; using System.Text; +using Dawn; using PVOutput.Net.Enums; +using PVOutput.Net.Objects.Core; using PVOutput.Net.Objects.Modules; using PVOutput.Net.Objects.Modules.Implementations; @@ -31,6 +33,8 @@ public OutputPostBuilder() /// The builder. public OutputPostBuilder SetDate(DateTime date) { + Guard.Argument(date, nameof(date)).IsNoFutureDate().NoTimeComponent(); + _outputPost.OutputDate = date; return this; } @@ -42,6 +46,8 @@ public OutputPostBuilder SetDate(DateTime date) /// The builder. public OutputPostBuilder SetGenerated(int energyGenerated) { + Guard.Argument(energyGenerated, nameof(energyGenerated)).Min(0); + _outputPost.EnergyGenerated = energyGenerated; return this; } @@ -53,6 +59,8 @@ public OutputPostBuilder SetGenerated(int energyGenerated) /// The builder. public OutputPostBuilder SetExported(int energyExported) { + Guard.Argument(energyExported, nameof(energyExported)).Min(0); + _outputPost.EnergyExported = energyExported; return this; } @@ -75,6 +83,8 @@ public OutputPostBuilder SetPeakTime(DateTime peakTime) /// The builder. public OutputPostBuilder SetPeakPower(int peakPower) { + Guard.Argument(peakPower, nameof(peakPower)).Min(0); + _outputPost.PeakPower = peakPower; return this; } @@ -98,6 +108,13 @@ public OutputPostBuilder SetCondition(WeatherCondition condition) /// The builder. public OutputPostBuilder SetTemperatures(decimal? minimumTemperature, decimal? maximumTemperature) { + Guard.NotAllNull(Guard.Argument(minimumTemperature, nameof(minimumTemperature)), Guard.Argument(maximumTemperature, nameof(maximumTemperature))); + + if (minimumTemperature.HasValue && maximumTemperature.HasValue) + { + Guard.Argument(maximumTemperature.Value, nameof(maximumTemperature)).GreaterThan(minimumTemperature.Value); + } + _outputPost.MinimumTemperature = minimumTemperature; _outputPost.MaximumTemperature = maximumTemperature; return this; @@ -110,6 +127,8 @@ public OutputPostBuilder SetTemperatures(decimal? minimumTemperatur /// The builder. public OutputPostBuilder SetComments(string comments) { + Guard.Argument(comments, nameof(comments)).NotEmpty().NotNull(); + _outputPost.Comments = comments; return this; } @@ -121,6 +140,8 @@ public OutputPostBuilder SetComments(string comments) /// The builder. public OutputPostBuilder SetPeakEnergyImport(int peakImport) { + Guard.Argument(peakImport, nameof(peakImport)).Min(0); + _outputPost.PeakEnergyImport = peakImport; return this; } @@ -132,6 +153,8 @@ public OutputPostBuilder SetPeakEnergyImport(int peakImport) /// The builder. public OutputPostBuilder SetOffPeakEnergyImport(int offpeakImport) { + Guard.Argument(offpeakImport, nameof(offpeakImport)).Min(0); + _outputPost.OffPeakEnergyImport = offpeakImport; return this; } @@ -143,6 +166,8 @@ public OutputPostBuilder SetOffPeakEnergyImport(int offpeakImport) /// The builder. public OutputPostBuilder SetShoulderEnergyImport(int shoulderImport) { + Guard.Argument(shoulderImport, nameof(shoulderImport)).Min(0); + _outputPost.ShoulderEnergyImport = shoulderImport; return this; } @@ -154,6 +179,8 @@ public OutputPostBuilder SetShoulderEnergyImport(int shoulderImport /// The builder. public OutputPostBuilder SetHighShoulderEnergyImport(int highShoulderImport) { + Guard.Argument(highShoulderImport, nameof(highShoulderImport)).Min(0); + _outputPost.HighShoulderEnergyImport = highShoulderImport; return this; } @@ -166,6 +193,8 @@ public OutputPostBuilder SetHighShoulderEnergyImport(int highShould [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Exception messages are non translatable for now")] public OutputPostBuilder SetConsumption(int consumption) { + Guard.Argument(consumption, nameof(consumption)).Min(0); + if (typeof(TResultType) == typeof(IBatchOutputPost)) { throw new InvalidOperationException("Cannot set consumption for batch output"); diff --git a/src/PVOutput.Net/Objects/PVCoordinate.cs b/src/PVOutput.Net/Objects/PVCoordinate.cs index 605c763..3c1e3e2 100644 --- a/src/PVOutput.Net/Objects/PVCoordinate.cs +++ b/src/PVOutput.Net/Objects/PVCoordinate.cs @@ -8,7 +8,7 @@ namespace PVOutput.Net.Objects /// /// Describes a location using a Latitude and Longitude. /// - public struct PVCoordinate : IEquatable + public readonly struct PVCoordinate : IEquatable { /// /// Latitudinal part of the coordinate. diff --git a/src/PVOutput.Net/Objects/StatusPostBuilder.cs b/src/PVOutput.Net/Objects/StatusPostBuilder.cs index a312e06..0ecf15a 100644 --- a/src/PVOutput.Net/Objects/StatusPostBuilder.cs +++ b/src/PVOutput.Net/Objects/StatusPostBuilder.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Text; +using Dawn; using PVOutput.Net.Enums; +using PVOutput.Net.Objects.Core; using PVOutput.Net.Objects.Modules; using PVOutput.Net.Objects.Modules.Implementations; @@ -30,6 +32,8 @@ public StatusPostBuilder() /// The builder. public StatusPostBuilder SetTimeStamp(DateTime timestamp) { + Guard.Argument(timestamp, nameof(timestamp)).IsNoFutureDate(); + _statusPost.Timestamp = timestamp; return this; @@ -43,6 +47,9 @@ public StatusPostBuilder SetTimeStamp(DateTime timestamp) /// The builder. public StatusPostBuilder SetGeneration(int? energyGeneration, int? powerGeneration) { + Guard.Argument(energyGeneration, nameof(energyGeneration)).Min(0); + Guard.Argument(powerGeneration, nameof(powerGeneration)).Min(0); + _statusPost.EnergyGeneration = energyGeneration; _statusPost.PowerGeneration = powerGeneration; return this; @@ -56,6 +63,9 @@ public StatusPostBuilder SetGeneration(int? energyGeneration, int? /// The builder. public StatusPostBuilder SetConsumption(int? energyConsumption, int? powerConsumption) { + Guard.Argument(energyConsumption, nameof(energyConsumption)).Min(0); + Guard.Argument(powerConsumption, nameof(powerConsumption)).Min(0); + _statusPost.EnergyConsumption = energyConsumption; _statusPost.PowerConsumption = powerConsumption; return this; @@ -79,6 +89,8 @@ public StatusPostBuilder SetTemperature(decimal temperature) /// The builder. public StatusPostBuilder SetVoltage(decimal voltage) { + Guard.Argument(voltage, nameof(voltage)).InRange(0, 300); + _statusPost.Voltage = voltage; return this; } @@ -134,10 +146,7 @@ public StatusPostBuilder SetExtendedValues(decimal? value1, decimal [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Exception messages are non translatable for now")] public StatusPostBuilder SetTextMessage(string textMessage) { - if (textMessage?.Length > 30) - { - throw new ArgumentException("Length cannot be longer than 30 characters", nameof(textMessage)); - } + Guard.Argument(textMessage, nameof(textMessage)).NotEmpty().LengthInRange(1, 30); _statusPost.TextMessage = textMessage; return this; diff --git a/src/PVOutput.Net/PVOutput.Net.csproj b/src/PVOutput.Net/PVOutput.Net.csproj index 8023f79..d288d0c 100644 --- a/src/PVOutput.Net/PVOutput.Net.csproj +++ b/src/PVOutput.Net/PVOutput.Net.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 7.1 + 7.3 false false Marcel Boersma @@ -25,14 +25,19 @@ ..\..\artifacts\ ..\..\artifacts\$(AssemblyName).xml + full + true ..\..\artifacts\release\ ..\..\artifacts\release\$(AssemblyName).xml + full + true + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/PVOutput.Net.Tests/Handler/BaseRequestHandlingTests.cs b/tests/PVOutput.Net.Tests/Handler/BaseRequestHandlingTests.cs index b124397..9452d76 100644 --- a/tests/PVOutput.Net.Tests/Handler/BaseRequestHandlingTests.cs +++ b/tests/PVOutput.Net.Tests/Handler/BaseRequestHandlingTests.cs @@ -71,5 +71,33 @@ public async Task ClientWithNoThrowOption_OnErrorResponse_ReturnsErrorResponse() testProvider.VerifyNoOutstandingExpectation(); } + + [Test] + public async Task Response_WithApiRateInformation_ParsesCorrectInformation() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + var resetTimeStamp = new DateTime(2020, 3, 15, 11, 20, 0); + var offset = (DateTimeOffset)DateTime.SpecifyKind(resetTimeStamp, DateTimeKind.Utc); + + var responseHeaders = new Dictionary() + { + { "X-Rate-Limit-Remaining", "156" }, + { "X-Rate-Limit-Limit", "300" }, + { "X-Rate-Limit-Reset", offset.ToUnixTimeSeconds().ToString() } + }; + + testProvider.ExpectUriFromBase("getsystem.jsp") + .Respond(responseHeaders, "text/plain", ""); + + var response = await client.System.GetOwnSystemAsync(); + testProvider.VerifyNoOutstandingExpectation(); + + Assert.Multiple(() => { + Assert.AreEqual(156, response.ApiRateInformation.LimitRemaining); + Assert.AreEqual(300, response.ApiRateInformation.CurrentLimit); + Assert.AreEqual(resetTimeStamp, response.ApiRateInformation.LimitResetAt); + }); + } } } diff --git a/tests/PVOutput.Net.Tests/Modules/Extended/ExtendedServiceTests.cs b/tests/PVOutput.Net.Tests/Modules/Extended/ExtendedServiceTests.cs index 2b4e78c..8569cc7 100644 --- a/tests/PVOutput.Net.Tests/Modules/Extended/ExtendedServiceTests.cs +++ b/tests/PVOutput.Net.Tests/Modules/Extended/ExtendedServiceTests.cs @@ -54,6 +54,28 @@ public async Task ExtendedService_WithPeriodAndLimit_CallsCorrectUri() AssertStandardResponse(response); } + [Test] + public void ExtendedService_GetExtendedDataForPeriod_WithReversedRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Extended.GetExtendedDataForPeriodAsync(new DateTime(2016, 3, 7), new DateTime(2016, 3, 6)); + }); + } + + [Test] + public void ExtendedService_GetExtendedDataForPeriod_WithLimitThatIsTooHigh_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Extended.GetExtendedDataForPeriodAsync(DateTime.Today.AddDays(-1), DateTime.Today, 100); + }); + } + /* * Deserialisation tests below */ diff --git a/tests/PVOutput.Net.Tests/Modules/Missing/MissingServiceTests.cs b/tests/PVOutput.Net.Tests/Modules/Missing/MissingServiceTests.cs index a229610..7169a51 100644 --- a/tests/PVOutput.Net.Tests/Modules/Missing/MissingServiceTests.cs +++ b/tests/PVOutput.Net.Tests/Modules/Missing/MissingServiceTests.cs @@ -26,6 +26,17 @@ public async Task MissingService_GetMissingDaysInPeriod_CallsCorrectUri() AssertStandardResponse(response); } + [Test] + public void MissingService_GetMissingDaysInPeriod_WithReversedRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Missing.GetMissingDaysInPeriodAsync(new DateTime(2016, 8, 30), new DateTime(2016, 8, 29)); + }); + } + /* * Deserialisation tests below */ diff --git a/tests/PVOutput.Net.Tests/Modules/Output/OutputBuilderTests.cs b/tests/PVOutput.Net.Tests/Modules/Output/OutputBuilderTests.cs index be1d15a..d3b7f6e 100644 --- a/tests/PVOutput.Net.Tests/Modules/Output/OutputBuilderTests.cs +++ b/tests/PVOutput.Net.Tests/Modules/Output/OutputBuilderTests.cs @@ -25,6 +25,15 @@ public void OutputPostBuilder_WithDate_SetsDate() Assert.AreEqual(DateTime.Today, builder._outputPost.OutputDate); } + [Test] + public void OutputPostBuilder_WithTimeComponent_Throws() + { + Assert.Throws(() => + { + _ = new OutputPostBuilder().SetDate(DateTime.Today.AddMinutes(10)); + }); + } + [Test] public void OutputPostBuilder_WithGeneration_SetsGeneration() { @@ -34,6 +43,15 @@ public void OutputPostBuilder_WithGeneration_SetsGeneration() Assert.AreEqual(12121, builder._outputPost.EnergyGenerated); } + [Test] + public void OutputPostBuilder_WithNegativeGeneration_Throws() + { + Assert.Throws(() => + { + _ = new OutputPostBuilder().SetGenerated(-1); + }); + } + [Test] public void OutputPostBuilder_WithExported_SetsExported() { @@ -43,6 +61,16 @@ public void OutputPostBuilder_WithExported_SetsExported() Assert.AreEqual(12121, builder._outputPost.EnergyExported); } + + [Test] + public void OutputPostBuilder_WithNegativeExported_Throws() + { + Assert.Throws(() => + { + _ = new OutputPostBuilder().SetExported(-1); + }); + } + [Test] public void OutputPostBuilder_WithPeakTime_SetsPeakTime() { @@ -74,6 +102,15 @@ public void OutputPostBuilder_WithPeakPower_SetsPeakPower() Assert.AreEqual(13131, builder._outputPost.PeakPower); } + [Test] + public void OutputPostBuilder_WithNegativePeakPower_Throws() + { + Assert.Throws(() => + { + _ = new OutputPostBuilder().SetPeakPower(-1); + }); + } + [Test] public void OutputPostBuilder_WithCondition_SetsCondition() { @@ -97,6 +134,28 @@ public void OutputPostBuilder_WithTemperatures_SetsCorrectTemperature(decimal? m Assert.AreEqual(maximum, builder._outputPost.MaximumTemperature); } + [Test] + public void OutputPostBuilder_WithReversedTemperatures_Throws() + { + var builder = new OutputPostBuilder().SetDate(DateTime.Today); + + Assert.Throws(() => + { + builder.SetTemperatures(15, 10); + }); + } + + [Test] + public void OutputPostBuilder_WithBothNullTemperatures_Throws() + { + var builder = new OutputPostBuilder().SetDate(DateTime.Today); + + Assert.Throws(() => + { + builder.SetTemperatures(null, null); + }); + } + [Test] public void OutputPostBuilder_WithComment_SetsComment() { @@ -107,6 +166,28 @@ public void OutputPostBuilder_WithComment_SetsComment() Assert.AreEqual(testComment, builder._outputPost.Comments); } + [Test] + public void OutputPostBuilder_WithEmptyComment_Throws() + { + var builder = new OutputPostBuilder().SetDate(DateTime.Today); + + Assert.Throws(() => + { + builder.SetComments(""); + }); + } + + [Test] + public void OutputPostBuilder_WithNullComment_Throws() + { + var builder = new OutputPostBuilder().SetDate(DateTime.Today); + + Assert.Throws(() => + { + builder.SetComments(null); + }); + } + [Test] public void OutputPostBuilder_WithPeakEnergyImport_SetsPeakEnergyImport() { @@ -116,6 +197,15 @@ public void OutputPostBuilder_WithPeakEnergyImport_SetsPeakEnergyImport() Assert.AreEqual(13131, builder._outputPost.PeakEnergyImport); } + [Test] + public void OutputPostBuilder_WithNegativePeakEnergyImport_Throws() + { + Assert.Throws(() => + { + _ = new OutputPostBuilder().SetPeakEnergyImport(-1); + }); + } + [Test] public void OutputPostBuilder_WithOffPeakImport_SetsOffPeakEnergyImport() { @@ -125,6 +215,15 @@ public void OutputPostBuilder_WithOffPeakImport_SetsOffPeakEnergyImport() Assert.AreEqual(13131, builder._outputPost.OffPeakEnergyImport); } + [Test] + public void OutputPostBuilder_WithNegativeOffPeakEnergyImport_Throws() + { + Assert.Throws(() => + { + _ = new OutputPostBuilder().SetOffPeakEnergyImport(-1); + }); + } + [Test] public void OutputPostBuilder_WithShoulderEnergyImport_SetShoulderEnergyImport() { @@ -134,6 +233,15 @@ public void OutputPostBuilder_WithShoulderEnergyImport_SetShoulderEnergyImport() Assert.AreEqual(13131, builder._outputPost.ShoulderEnergyImport); } + [Test] + public void OutputPostBuilder_WithNegativeShoulderEnergyImport_Throws() + { + Assert.Throws(() => + { + _ = new OutputPostBuilder().SetShoulderEnergyImport(-1); + }); + } + [Test] public void OutputPostBuilder_WithHighShoulderEnergyImport_SetHighShoulderEnergyImport() { @@ -143,6 +251,15 @@ public void OutputPostBuilder_WithHighShoulderEnergyImport_SetHighShoulderEnergy Assert.AreEqual(13131, builder._outputPost.HighShoulderEnergyImport); } + [Test] + public void OutputPostBuilder_WithNegativeHighShoulderEnergyImport_Throws() + { + Assert.Throws(() => + { + _ = new OutputPostBuilder().SetHighShoulderEnergyImport(-1); + }); + } + [Test] public void OutputPostBuilder_WithConsumption_SetConsumption() { @@ -152,6 +269,15 @@ public void OutputPostBuilder_WithConsumption_SetConsumption() Assert.AreEqual(25000, builder._outputPost.Consumption); } + [Test] + public void OutputPostBuilder_WithNegativeConsumption_Throws() + { + Assert.Throws(() => + { + _ = new OutputPostBuilder().SetConsumption(-1); + }); + } + [Test] public void OutputPostBuilder_WithoutDate_CannotBuild() { diff --git a/tests/PVOutput.Net.Tests/Modules/Output/OutputServiceTests.cs b/tests/PVOutput.Net.Tests/Modules/Output/OutputServiceTests.cs index d8c9474..00ed398 100644 --- a/tests/PVOutput.Net.Tests/Modules/Output/OutputServiceTests.cs +++ b/tests/PVOutput.Net.Tests/Modules/Output/OutputServiceTests.cs @@ -123,10 +123,131 @@ public async Task OutputService_GetAggregatedByYear_CallsCorrectUri() AssertStandardResponse(response); } + [Test] + public void OutputService_GetOutputForDate_WithFutureDate_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.GetOutputForDateAsync(DateTime.Today.AddDays(1)); + }); + } + + [Test] + public void OutputService_GetOutputsForPeriod_WithFutureRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.GetOutputsForPeriodAsync(DateTime.Today, DateTime.Today.AddDays(1)); + }); + } + + [Test] + public void OutputService_GetOutputsForPeriod_WithReversedRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.GetOutputsForPeriodAsync(new DateTime(2016, 8, 30), new DateTime(2016, 8, 29)); + }); + } + + [Test] + public void OutputService_GetTeamOutputForDate_WithFutureDate_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.GetTeamOutputForDateAsync(DateTime.Today.AddDays(1), 1234); + }); + } + + [Test] + public void OutputService_GetTeamOutputsForPeriod_WithFutureRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.GetTeamOutputsForPeriodAsync(DateTime.Today, DateTime.Today.AddDays(1), 1234); + }); + } + + [Test] + public void OutputService_GetTeamOutputsForPeriod_WithReversedRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.GetTeamOutputsForPeriodAsync(new DateTime(2016, 8, 30), new DateTime(2016, 8, 29), 1234); + }); + } + + [Test] + public void OutputService_GetAggregatedOutputs_WithFutureRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.GetAggregatedOutputsAsync(DateTime.Today, DateTime.Today.AddDays(1), AggregationPeriod.Month); + }); + } + + [Test] + public void OutputService_GetAggregatedOutputs_WithReversedRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.GetAggregatedOutputsAsync(new DateTime(2016, 8, 30), new DateTime(2016, 8, 29), AggregationPeriod.Month); + }); + } + /* Adding outputs */ + [Test] + public void OutputService_AddOutput_WithNullOutput_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.AddOutputAsync(null); + }); + } + + [Test] + public void OutputService_AddBatchOutput_WithNullOutputs_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.AddBatchOutputAsync(null); + }); + } + + [Test] + public void OutputService_AddBatchOutput_WithEmptyOutputs_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Output.AddBatchOutputAsync(new List()); + }); + } + public static IEnumerable AddOutputTestCases { get diff --git a/tests/PVOutput.Net.Tests/Modules/Search/SearchServiceTests.cs b/tests/PVOutput.Net.Tests/Modules/Search/SearchServiceTests.cs index 30a9fcc..a8bfea2 100644 --- a/tests/PVOutput.Net.Tests/Modules/Search/SearchServiceTests.cs +++ b/tests/PVOutput.Net.Tests/Modules/Search/SearchServiceTests.cs @@ -17,17 +17,42 @@ namespace PVOutput.Net.Tests.Modules.Search public partial class SearchServiceTests : BaseRequestsTest { [Test] - public async Task SearchService_WithNoParameters_CallsCorrectUri() + public async Task SearchService_WithQuery_CallsCorrectUri() { PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); testProvider.ExpectUriFromBase(SEARCH_URL) .RespondPlainText(""); - var response = await client.Search.SearchAsync(""); + var response = await client.Search.SearchAsync("test query"); testProvider.VerifyNoOutstandingExpectation(); AssertStandardResponse(response); } + + [Test] + public void SearchService_Search_WithNullOrEmptyQuery_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Search.SearchAsync(null); + }); + } + + + [Test] + public void SearchService_Search_WithEmptyQuery_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Search.SearchAsync(""); + }); + } + + /* * Deserialisation tests below */ diff --git a/tests/PVOutput.Net.Tests/Modules/Statistic/StatisticServiceTests.cs b/tests/PVOutput.Net.Tests/Modules/Statistic/StatisticServiceTests.cs index 1178db9..1a8421a 100644 --- a/tests/PVOutput.Net.Tests/Modules/Statistic/StatisticServiceTests.cs +++ b/tests/PVOutput.Net.Tests/Modules/Statistic/StatisticServiceTests.cs @@ -39,6 +39,17 @@ public async Task StatisticsService_GetPeriodStatistics_CallsCorrectUri() AssertStandardResponse(response); } + [Test] + public void StatisticsService_GetStatisticsForPeriod_WithReversedRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Statistics.GetStatisticsForPeriodAsync(new DateTime(2016, 8, 30), new DateTime(2016, 8, 29)); + }); + } + /* * Deserialisation tests below */ diff --git a/tests/PVOutput.Net.Tests/Modules/Status/StatusBuilderTests.cs b/tests/PVOutput.Net.Tests/Modules/Status/StatusBuilderTests.cs index 9bb28c1..0a8bf80 100644 --- a/tests/PVOutput.Net.Tests/Modules/Status/StatusBuilderTests.cs +++ b/tests/PVOutput.Net.Tests/Modules/Status/StatusBuilderTests.cs @@ -26,6 +26,18 @@ public void StatusPostBuilder_WithTimeStamp_SetsTimeStamp() Assert.AreEqual(timeStamp, builder._statusPost.Timestamp); } + [Test] + public void StatusPostBuilder_WithFutureTimeStamp_Throws() + { + var timeStamp = DateTime.Now.AddDays(1); + var builder = new StatusPostBuilder(); + + Assert.Throws(() => + { + builder.SetTimeStamp(timeStamp); + }); + } + [Test] [TestCase(new object[] { 10, 20 })] [TestCase(new object[] { null, 30 })] @@ -38,6 +50,17 @@ public void StatusPostBuilder_WithGeneration_SetsGeneration(int? energyGeneratio Assert.AreEqual(powerGeneration, builder._statusPost.PowerGeneration); } + [Test] + [TestCase(new object[] { null, -1 })] + [TestCase(new object[] { -1, null })] + public void StatusPostBuilder_WithNegativeGeneration_Throws(int? energyGeneration, int? powerGeneration) + { + Assert.Throws(() => + { + _ = new StatusPostBuilder().SetGeneration(energyGeneration, powerGeneration); + }); + } + [Test] [TestCase(new object[] { 10, 20 })] [TestCase(new object[] { null, 30 })] @@ -50,6 +73,17 @@ public void StatusPostBuilder_WithConsumption_SetsConsumption(int? energyConsump Assert.AreEqual(powerConsumption, builder._statusPost.PowerConsumption); } + [Test] + [TestCase(new object[] { null, -1 })] + [TestCase(new object[] { -1, null })] + public void StatusPostBuilder_WithNegativeConsumption_Throws(int? energyConsumption, int? powerConsumption) + { + Assert.Throws(() => + { + _ = new StatusPostBuilder().SetConsumption(energyConsumption, powerConsumption); + }); + } + [Test] public void StatusPostBuilder_WithTemperature_SetsTemperature() { @@ -66,6 +100,17 @@ public void StatusPostBuilder_WithVoltage_SetsVoltage() Assert.AreEqual(231.2m, builder._statusPost.Voltage); } + [Test] + [TestCase(-1)] + [TestCase(301)] + public void StatusPostBuilder_WithVoltageOutOfRange_Throws(decimal voltage) + { + Assert.Throws(() => + { + _ = new StatusPostBuilder().SetVoltage(voltage); + }); + } + [Test] public void StatusPostBuilder_WithCumulativeType_SetsCumulativeType() { @@ -114,9 +159,11 @@ public void StatusPostBuilder_WithTextMessage_SetsTextMessage() [Test] public void StatusPostBuilder_WithTooLongTextMessage_Throws() { - var exception = Assert.Throws(() => + var builder = new StatusPostBuilder(); + + Assert.Throws(() => { - var builder = new StatusPostBuilder().SetTextMessage("0123456789001234567890012345678901"); + builder.SetTextMessage("0123456789001234567890012345678901"); }); } @@ -145,6 +192,7 @@ public void StatusPostBuilder_AfterBuildAndReset_HasNoStateLeft() public void StatusPostBuilder_WithoutPowerOrConsumption_CannotBuild() { var builder = new StatusPostBuilder().SetTextMessage("Test"); + Assert.Throws(() => { builder.Build(); diff --git a/tests/PVOutput.Net.Tests/Modules/Status/StatusServiceTests.cs b/tests/PVOutput.Net.Tests/Modules/Status/StatusServiceTests.cs index bc0eed7..b43218c 100644 --- a/tests/PVOutput.Net.Tests/Modules/Status/StatusServiceTests.cs +++ b/tests/PVOutput.Net.Tests/Modules/Status/StatusServiceTests.cs @@ -51,7 +51,7 @@ public async Task StatusService_GetDayStatistics_CallsCorrectUri() .WithQueryString("d=20200131&from=10:00&to=16:00&stats=1") .RespondPlainText(STATUS_RESPONSE_DAYSTATISTICS_MEDIUM); - var response = await client.Status.GetDayStatisticsForPeriodAsync(new DateTime(2020, 1, 31, 10, 0, 0), new DateTime(2019, 1, 31, 16, 0, 0)); + var response = await client.Status.GetDayStatisticsForPeriodAsync(new DateTime(2020, 1, 31, 10, 0, 0), new DateTime(2020, 1, 31, 16, 0, 0)); testProvider.VerifyNoOutstandingExpectation(); AssertStandardResponse(response); @@ -59,6 +59,108 @@ public async Task StatusService_GetDayStatistics_CallsCorrectUri() Assert.AreEqual(new DateTime(2020, 1, 31, 15, 5, 0), response.Value.StandbyPowerTime); } + [Test] + public void StatusService_GetStatusForDateTime_WithFutureDate_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Status.GetStatusForDateTimeAsync(DateTime.Today.AddDays(1)); + }); + } + + [Test] + public void StatusService_DeleteStatus_WithFutureDate_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Status.DeleteStatusAsync(DateTime.Today.AddDays(1)); + }); + } + + + [Test] + public void StatusService_GetHistoryForPeriod_WithFutureRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Status.GetHistoryForPeriodAsync(DateTime.Today, DateTime.Today.AddDays(1)); + }); + } + + [Test] + public void StatusService_GetHistoryForPeriod_WithReversedRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Status.GetHistoryForPeriodAsync(new DateTime(2016, 8, 30), new DateTime(2016, 8, 29)); + }); + } + + [Test] + public void StatusService_GetDayStatisticsForPeriod_WithFutureRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Status.GetDayStatisticsForPeriodAsync(DateTime.Today, DateTime.Today.AddDays(1)); + }); + } + + [Test] + public void StatusService_GetDayStatisticsForPeriod_WithReversedRange_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Status.GetDayStatisticsForPeriodAsync(new DateTime(2016, 8, 30), new DateTime(2016, 8, 29)); + }); + } + + + [Test] + public void StatusService_AddStatus_WithNullStatus_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Status.AddStatusAsync(null); + }); + } + + [Test] + public void StatusService_AddBatchStatus_WithNullStatuses_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Status.AddBatchStatusAsync(null); + }); + } + + [Test] + public void StatusService_AddBatchStatus_WithEmptyStatuses_Throws() + { + PVOutputClient client = TestUtility.GetMockClient(out MockHttpMessageHandler testProvider); + + Assert.ThrowsAsync(async () => + { + _ = await client.Status.AddBatchStatusAsync(new List()); + }); + } + + /* * Deserialisation tests below */ diff --git a/tests/PVOutput.Net.Tests/PVOutput.Net.Tests.csproj b/tests/PVOutput.Net.Tests/PVOutput.Net.Tests.csproj index c5e640f..f2e255e 100644 --- a/tests/PVOutput.Net.Tests/PVOutput.Net.Tests.csproj +++ b/tests/PVOutput.Net.Tests/PVOutput.Net.Tests.csproj @@ -6,6 +6,10 @@ ..\..\artifacts\ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive +