Merge remote-tracking branch 'origin/netcore/netcore' into netcore/feature/migrate_custom_view_engines
This commit is contained in:
10
src/Umbraco.Core/Configuration/ICronTabParser.cs
Normal file
10
src/Umbraco.Core/Configuration/ICronTabParser.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public interface ICronTabParser
|
||||
{
|
||||
bool IsValidCronTab(string cronTab);
|
||||
DateTime GetNextOccurrence(string cronTab, DateTime time);
|
||||
}
|
||||
}
|
||||
@@ -42,17 +42,7 @@ namespace Umbraco.Core.Configuration.Models.Validation
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ValidateOptionalTime(string configPath, string value, out string message)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value) && !value.IsValidTimeSpan())
|
||||
{
|
||||
message = $"Configuration entry {configPath} contains an invalid time value.";
|
||||
return false;
|
||||
}
|
||||
|
||||
message = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,13 @@ namespace Umbraco.Core.Configuration.Models.Validation
|
||||
{
|
||||
public class HealthChecksSettingsValidator : ConfigurationValidatorBase, IValidateOptions<HealthChecksSettings>
|
||||
{
|
||||
private readonly ICronTabParser _cronTabParser;
|
||||
|
||||
public HealthChecksSettingsValidator(ICronTabParser cronTabParser)
|
||||
{
|
||||
_cronTabParser = cronTabParser;
|
||||
}
|
||||
|
||||
public ValidateOptionsResult Validate(string name, HealthChecksSettings options)
|
||||
{
|
||||
if (!ValidateNotificationFirstRunTime(options.Notification.FirstRunTime, out var message))
|
||||
@@ -16,7 +23,19 @@ namespace Umbraco.Core.Configuration.Models.Validation
|
||||
|
||||
private bool ValidateNotificationFirstRunTime(string value, out string message)
|
||||
{
|
||||
return ValidateOptionalTime($"{Constants.Configuration.ConfigHealthChecks}:{nameof(HealthChecksSettings.Notification)}:{nameof(HealthChecksSettings.Notification.FirstRunTime)}", value, out message);
|
||||
return ValidateOptionalCronTab($"{Constants.Configuration.ConfigHealthChecks}:{nameof(HealthChecksSettings.Notification)}:{nameof(HealthChecksSettings.Notification.FirstRunTime)}", value, out message);
|
||||
}
|
||||
|
||||
public bool ValidateOptionalCronTab(string configPath, string value, out string message)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value) && !_cronTabParser.IsValidCronTab(value))
|
||||
{
|
||||
message = $"Configuration entry {configPath} contains an invalid cron expression.";
|
||||
return false;
|
||||
}
|
||||
|
||||
message = string.Empty;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,42 +43,5 @@ namespace Umbraco.Core
|
||||
Minute,
|
||||
Second
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
|
||||
/// date time is before the time, calculate onto next day).
|
||||
/// </summary>
|
||||
/// <param name="fromDateTime">Date to start from</param>
|
||||
/// <param name="scheduledTime">Time to compare against (in Hmm form, e.g. 330, 2200)</param>
|
||||
/// <returns></returns>
|
||||
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
|
||||
{
|
||||
// Ensure time provided is 4 digits long
|
||||
if (scheduledTime.Length == 3)
|
||||
{
|
||||
scheduledTime = "0" + scheduledTime;
|
||||
}
|
||||
|
||||
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
|
||||
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
|
||||
|
||||
DateTime scheduledDateTime;
|
||||
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
|
||||
{
|
||||
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nextDay = fromDateTime.AddDays(1);
|
||||
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
|
||||
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
|
||||
}
|
||||
|
||||
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
|
||||
{
|
||||
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1478,20 +1478,5 @@ namespace Umbraco.Core
|
||||
{
|
||||
return shortStringHelper.CleanStringForSafeFileName(text, culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a string matches a time stamp.
|
||||
/// </summary>
|
||||
/// <param name="input">String with timespan representation (in standard timespan format: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings)</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValidTimeSpan(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TimeSpan.TryParse(input, out var _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace Umbraco.Web
|
||||
IMainDom mainDom,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
IScopeProvider scopeProvider,
|
||||
ISqlContext sqlContext,
|
||||
IProfilingLogger proflog,
|
||||
ILogger<BatchedDatabaseServerMessenger> logger,
|
||||
IServerRegistrar serverRegistrar,
|
||||
@@ -40,7 +39,7 @@ namespace Umbraco.Web
|
||||
CacheRefresherCollection cacheRefreshers,
|
||||
IRequestCache requestCache,
|
||||
IRequestAccessor requestAccessor)
|
||||
: base(mainDom, scopeProvider, sqlContext, proflog, logger, serverRegistrar, true, options, hostingEnvironment, cacheRefreshers)
|
||||
: base(mainDom, scopeProvider, databaseFactory, proflog, logger, serverRegistrar, true, options, hostingEnvironment, cacheRefreshers)
|
||||
{
|
||||
_databaseFactory = databaseFactory;
|
||||
_requestCache = requestCache;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using System;
|
||||
using NCrontab;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Models.Extensions
|
||||
namespace Umbraco.Infrastructure.Configuration.Extensions
|
||||
{
|
||||
public static class HealthCheckSettingsExtensions
|
||||
{
|
||||
public static TimeSpan GetNotificationDelay(this HealthChecksSettings settings, DateTime now, TimeSpan defaultDelay)
|
||||
public static TimeSpan GetNotificationDelay(this HealthChecksSettings settings, ICronTabParser cronTabParser, DateTime now, TimeSpan defaultDelay)
|
||||
{
|
||||
// If first run time not set, start with just small delay after application start.
|
||||
var firstRunTime = settings.Notification.FirstRunTime;
|
||||
@@ -14,9 +17,10 @@ namespace Umbraco.Core.Configuration.Models.Extensions
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise start at scheduled time.
|
||||
var delay = TimeSpan.FromMinutes(now.PeriodicMinutesFrom(firstRunTime));
|
||||
return (delay < defaultDelay)
|
||||
// Otherwise start at scheduled time according to cron expression, unless within the default delay period.
|
||||
var firstRunOccurance = cronTabParser.GetNextOccurrence(firstRunTime, now);
|
||||
var delay = firstRunOccurance - now;
|
||||
return delay < defaultDelay
|
||||
? defaultDelay
|
||||
: delay;
|
||||
}
|
||||
24
src/Umbraco.Infrastructure/Configuration/NCronTabParser.cs
Normal file
24
src/Umbraco.Infrastructure/Configuration/NCronTabParser.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using NCrontab;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
|
||||
public class NCronTabParser : ICronTabParser
|
||||
{
|
||||
public bool IsValidCronTab(string cronTab)
|
||||
{
|
||||
var result = CrontabSchedule.TryParse(cronTab);
|
||||
|
||||
return !(result is null);
|
||||
}
|
||||
|
||||
public DateTime GetNextOccurrence(string cronTab, DateTime time)
|
||||
{
|
||||
var result = CrontabSchedule.Parse(cronTab);
|
||||
|
||||
return result.GetNextOccurrence(time);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,12 +4,13 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Configuration.Models.Extensions;
|
||||
using Umbraco.Core.HealthCheck;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Infrastructure.Configuration.Extensions;
|
||||
using Umbraco.Infrastructure.HealthCheck;
|
||||
using Umbraco.Web.HealthCheck;
|
||||
|
||||
@@ -39,9 +40,10 @@ namespace Umbraco.Infrastructure.HostedServices
|
||||
IMainDom mainDom,
|
||||
IScopeProvider scopeProvider,
|
||||
ILogger<HealthCheckNotifier> logger,
|
||||
IProfilingLogger profilingLogger)
|
||||
IProfilingLogger profilingLogger,
|
||||
ICronTabParser cronTabParser)
|
||||
: base(healthChecksSettings.Value.Notification.Period,
|
||||
healthChecksSettings.Value.GetNotificationDelay(DateTime.Now, DefaultDelay))
|
||||
healthChecksSettings.Value.GetNotificationDelay(cronTabParser, DateTime.Now, DefaultDelay))
|
||||
{
|
||||
_healthChecksSettings = healthChecksSettings.Value;
|
||||
_healthChecks = healthChecks;
|
||||
@@ -53,7 +55,7 @@ namespace Umbraco.Infrastructure.HostedServices
|
||||
_logger = logger;
|
||||
_profilingLogger = profilingLogger;
|
||||
}
|
||||
|
||||
|
||||
internal override async Task PerformExecuteAsync(object state)
|
||||
{
|
||||
if (_healthChecksSettings.Notification.Enabled == false)
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Umbraco.Core.Runtime
|
||||
=> new DatabaseServerMessenger(
|
||||
factory.GetRequiredService<IMainDom>(),
|
||||
factory.GetRequiredService<IScopeProvider>(),
|
||||
factory.GetRequiredService<ISqlContext>(),
|
||||
factory.GetRequiredService<IUmbracoDatabaseFactory>(),
|
||||
factory.GetRequiredService<IProfilingLogger>(),
|
||||
factory.GetRequiredService<ILogger<DatabaseServerMessenger>>(),
|
||||
factory.GetRequiredService<IServerRegistrar>(),
|
||||
@@ -208,7 +208,7 @@ namespace Umbraco.Core.Runtime
|
||||
|
||||
// Config manipulator
|
||||
composition.Services.AddUnique<IConfigManipulator, JsonConfigManipulator>();
|
||||
|
||||
|
||||
// register the umbraco context factory
|
||||
// composition.Services.AddUnique<IUmbracoContextFactory, UmbracoContextFactory>();
|
||||
composition.Services.AddUnique<IPublishedUrlProvider, UrlProvider>();
|
||||
@@ -370,6 +370,8 @@ namespace Umbraco.Core.Runtime
|
||||
composition.Services.AddUnique<IBackOfficeExamineSearcher, NoopBackOfficeExamineSearcher>();
|
||||
|
||||
composition.Services.AddUnique<UploadAutoFillProperties>();
|
||||
|
||||
composition.Services.AddUnique<ICronTabParser, NCronTabParser>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,13 +30,14 @@ namespace Umbraco.Core.Sync
|
||||
public class DatabaseServerMessenger : ServerMessengerBase, IDatabaseServerMessenger
|
||||
{
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory;
|
||||
private readonly ManualResetEvent _syncIdle;
|
||||
private readonly object _locko = new object();
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly IServerRegistrar _serverRegistrar;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly CacheRefresherCollection _cacheRefreshers;
|
||||
private readonly ISqlContext _sqlContext;
|
||||
|
||||
private readonly Lazy<string> _distCacheFilePath;
|
||||
private int _lastId = -1;
|
||||
private DateTime _lastSync;
|
||||
@@ -48,13 +49,13 @@ namespace Umbraco.Core.Sync
|
||||
public DatabaseServerMessengerOptions Options { get; }
|
||||
|
||||
public DatabaseServerMessenger(
|
||||
IMainDom mainDom, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, ILogger<DatabaseServerMessenger> logger, IServerRegistrar serverRegistrar,
|
||||
IMainDom mainDom, IScopeProvider scopeProvider, IUmbracoDatabaseFactory umbracoDatabaseFactory, IProfilingLogger proflog, ILogger<DatabaseServerMessenger> logger, IServerRegistrar serverRegistrar,
|
||||
bool distributedEnabled, DatabaseServerMessengerOptions options, IHostingEnvironment hostingEnvironment, CacheRefresherCollection cacheRefreshers)
|
||||
: base(distributedEnabled)
|
||||
{
|
||||
ScopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider));
|
||||
_sqlContext = sqlContext;
|
||||
_mainDom = mainDom;
|
||||
_umbracoDatabaseFactory = umbracoDatabaseFactory;
|
||||
_profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog));
|
||||
_serverRegistrar = serverRegistrar;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
@@ -77,7 +78,7 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
protected IScopeProvider ScopeProvider { get; }
|
||||
|
||||
protected Sql<ISqlContext> Sql() => _sqlContext.Sql();
|
||||
protected Sql<ISqlContext> Sql() => _umbracoDatabaseFactory.SqlContext.Sql();
|
||||
|
||||
private string DistCacheFilePath => _distCacheFilePath.Value;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
||||
<PackageReference Include="MiniProfiler.Shared" Version="4.2.1" />
|
||||
<PackageReference Include="ncrontab" Version="3.3.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NPoco" Version="4.0.2" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Configuration.Models.Extensions;
|
||||
using Umbraco.Infrastructure.Configuration.Extensions;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Core.Configuration.Models.Extensions
|
||||
{
|
||||
[TestFixture]
|
||||
public class HealthCheckSettingsExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void Returns_Notification_Delay_From_Provided_Time()
|
||||
private ICronTabParser CronTabParser => new NCronTabParser();
|
||||
|
||||
[TestCase("30 12 * * *", 30)]
|
||||
[TestCase("15 18 * * *", 60 * 6 + 15)]
|
||||
[TestCase("0 3 * * *", 60 * 15)]
|
||||
[TestCase("0 3 2 * *", 24 * 60 * 1 + 60 * 15)]
|
||||
[TestCase("0 6 * * 3", 24 * 60 * 3 + 60 * 18)]
|
||||
public void Returns_Notification_Delay_From_Provided_Time(string firstRunTimeCronExpression, int expectedDelayInMinutes)
|
||||
{
|
||||
var settings = new HealthChecksSettings
|
||||
{
|
||||
Notification = new HealthChecksNotificationSettings
|
||||
{
|
||||
FirstRunTime = "1230",
|
||||
FirstRunTime = firstRunTimeCronExpression,
|
||||
}
|
||||
};
|
||||
var now = DateTime.Now.Date.AddHours(12);
|
||||
var result = settings.GetNotificationDelay(now, TimeSpan.Zero);
|
||||
Assert.AreEqual(30, result.Minutes);
|
||||
var now = new DateTime(2020, 10, 31, 12, 0, 0);
|
||||
var result = settings.GetNotificationDelay(CronTabParser, now, TimeSpan.Zero);
|
||||
Assert.AreEqual(expectedDelayInMinutes, result.TotalMinutes);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -30,12 +37,12 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Configuration.Models.Extensions
|
||||
{
|
||||
Notification = new HealthChecksNotificationSettings
|
||||
{
|
||||
FirstRunTime = "1230",
|
||||
FirstRunTime = "30 12 * * *",
|
||||
}
|
||||
};
|
||||
var now = DateTime.Now.Date.AddHours(12).AddMinutes(25);
|
||||
var result = settings.GetNotificationDelay(now, TimeSpan.FromMinutes(10));
|
||||
Assert.AreEqual(10, result.Minutes);
|
||||
var now = new DateTime(2020, 10, 31, 12, 25, 0);
|
||||
var result = settings.GetNotificationDelay(CronTabParser, now, TimeSpan.FromMinutes(10));
|
||||
Assert.AreEqual(10, result.TotalMinutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Configuration.Models.Validation;
|
||||
|
||||
@@ -11,7 +12,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Configuration.Models.Validation
|
||||
[Test]
|
||||
public void Returns_Success_ForValid_Configuration()
|
||||
{
|
||||
var validator = new HealthChecksSettingsValidator();
|
||||
var validator = new HealthChecksSettingsValidator(new NCronTabParser());
|
||||
var options = BuildHealthChecksSettings();
|
||||
var result = validator.Validate("settings", options);
|
||||
Assert.True(result.Succeeded);
|
||||
@@ -20,13 +21,13 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Configuration.Models.Validation
|
||||
[Test]
|
||||
public void Returns_Fail_For_Configuration_With_Invalid_Notification_FirstRunTime()
|
||||
{
|
||||
var validator = new HealthChecksSettingsValidator();
|
||||
var options = BuildHealthChecksSettings(firstRunTime: "25:00");
|
||||
var validator = new HealthChecksSettingsValidator(new NCronTabParser());
|
||||
var options = BuildHealthChecksSettings(firstRunTime: "0 3 *");
|
||||
var result = validator.Validate("settings", options);
|
||||
Assert.False(result.Succeeded);
|
||||
}
|
||||
|
||||
private static HealthChecksSettings BuildHealthChecksSettings(string firstRunTime = "12:00")
|
||||
private static HealthChecksSettings BuildHealthChecksSettings(string firstRunTime = "0 3 * * *")
|
||||
{
|
||||
return new HealthChecksSettings
|
||||
{
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Core.Configurations
|
||||
{
|
||||
[TestFixture]
|
||||
public class NCronTabParserTests
|
||||
{
|
||||
private ICronTabParser Sut => new NCronTabParser();
|
||||
|
||||
[TestCase("", ExpectedResult = false)]
|
||||
[TestCase("* * * * 1", ExpectedResult = true)]
|
||||
[TestCase("* * * * * 1", ExpectedResult = false)]
|
||||
[TestCase("* * * 1", ExpectedResult = false)]
|
||||
[TestCase("Invalid", ExpectedResult = false)]
|
||||
[TestCase("I n v a l", ExpectedResult = false)]
|
||||
[TestCase("23 0-20/2 * * *", ExpectedResult = true)]
|
||||
[TestCase("5 4 * * sun", ExpectedResult = true)]
|
||||
[TestCase("0 0,12 1 */2 *", ExpectedResult = true)]
|
||||
[TestCase("0 0 1,15 * 3", ExpectedResult = true)]
|
||||
[TestCase("5 0 * 8 *", ExpectedResult = true)]
|
||||
[TestCase("22 * * 1-5 *", ExpectedResult = true)]
|
||||
[TestCase("23 0-20/2 * * *", ExpectedResult = true)]
|
||||
[TestCase("23 0-20/2 * * sun-sat", ExpectedResult = true)]
|
||||
[TestCase("23 0-20/2 * jan-dec sun-sat", ExpectedResult = true)]
|
||||
[TestCase("* * 32 * *", ExpectedResult = false)]
|
||||
public bool IsValidCronTab(string input)
|
||||
{
|
||||
return Sut.IsValidCronTab(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Core
|
||||
{
|
||||
[TestFixture]
|
||||
public class DateTimeExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_PostTime_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "1145";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(75, minutesBetween);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_PriorTime_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "900";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(1350, minutesBetween);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_PriorTime_WithLeadingZero_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "0900";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(1350, minutesBetween);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_SameTime_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "1030";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(0, minutesBetween);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,15 +301,5 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.ShortStringHelper
|
||||
Assert.AreEqual(expected, output);
|
||||
}
|
||||
|
||||
[TestCase("", false)]
|
||||
[TestCase("12:34", true)]
|
||||
[TestCase("1:14:23", true)]
|
||||
[TestCase("25:03", false)]
|
||||
[TestCase("18:61", false)]
|
||||
public void IsValidTimeSpan(string input, bool expected)
|
||||
{
|
||||
var result = input.IsValidTimeSpan();
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.HealthCheck;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -141,7 +142,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.HostedServices
|
||||
|
||||
return new HealthCheckNotifier(Options.Create(settings), checks, notifications,
|
||||
mockRunTimeState.Object, mockServerRegistrar.Object, mockMainDom.Object, mockScopeProvider.Object,
|
||||
mockLogger.Object, mockProfilingLogger.Object);
|
||||
mockLogger.Object, mockProfilingLogger.Object, Mock.Of<ICronTabParser>());
|
||||
}
|
||||
|
||||
private void VerifyNotificationsNotSent()
|
||||
|
||||
Reference in New Issue
Block a user