feat(schedules)!: reworked schedule names and added a new style
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details

This commit is contained in:
D. Moonfire 2023-01-20 23:28:41 -06:00
parent 070cf2bfb8
commit 82e1bc3c28
9 changed files with 621 additions and 92 deletions

View File

@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.Linq;
using MfGames.Gallium;
namespace MfGames.Nitride.Temporal.Schedules;
/// <summary>
/// An indexed-based schedule that uses the regular expression to get a
/// numerical value and then uses that to determine the schedule.
/// </summary>
public partial class IndexedPathRegexSchedule<TSchedule> : PathRegexScheduleBase
where TSchedule : IndexedSchedule
{
public IndexedPathRegexSchedule()
{
this.Indexes = new Dictionary<int, TSchedule>();
}
/// <summary>
/// Gets or sets the dictionary that indexes the various numerical indexes
/// to provide the scheduling.
/// </summary>
public Dictionary<int, TSchedule> Indexes { get; set; }
public IndexedPathRegexSchedule<TSchedule> WithIndexes(
Dictionary<int, TSchedule> value)
{
this.Indexes = value;
return this;
}
/// <inheritdoc />
protected override Entity Apply(
Entity entity,
int number,
Timekeeper timekeeper)
{
// Figure out the entry in the index.
var applicableKeys = this.Indexes.Keys
.Where(a => a <= number)
.ToList();
if (applicableKeys.Count == 0)
{
return entity;
}
int startIndex = applicableKeys.Max(a => a);
TSchedule schedule = this.Indexes[startIndex];
// Pass everything into the schedule to perform the applying.
int startOffset = number - startIndex;
return schedule.Apply(entity, startOffset, timekeeper);
}
}

View File

@ -0,0 +1,79 @@
using System;
using MfGames.Gallium;
using NodaTime;
namespace MfGames.Nitride.Temporal.Schedules;
/// <summary>
/// Describes a simplified schedule object that contains a start and period
/// for the schedule and is designed to work with the numbers provided by
/// the <see cref="IndexedPathRegexSchedule" />.
/// </summary>
public partial class IndexedSchedule
{
/// <summary>
/// Gets or sets the period between each matching entity. The period is
/// amount of time between each item based on the number. So the first will
/// be on the ScheduleDate, the second SchedulePeriod later, the third
/// SchedulePeriod after the second, etc. More precisely, the date for
/// any item TimeSpan * ((int)CaptureGroup + (int)CaptureOffset).
/// </summary>
public string? SchedulePeriod { get; set; }
/// <summary>
/// Gets or sets the schedule period as a TimeSpan object. This is converted
/// from SchedulePeriod using https://github.com/pengowray/TimeSpanParser.
/// If the value is null or blank or "immediate", then this will be instant
/// (TimeSpan.Zero).
/// </summary>
public virtual TimeSpan? SchedulePeriodTimeSpan
{
get => TimeSpanHelper.Parse(this.SchedulePeriod);
set => this.SchedulePeriod = value?.ToString();
}
/// <summary>
/// Gets or sets when the first item is scheduled.
/// </summary>
public DateTime? ScheduleStart { get; set; }
public virtual Entity Apply(
Entity entity,
int number,
Timekeeper timekeeper)
{
// If we have a "never", then we skip it.
TimeSpan? period = this.SchedulePeriodTimeSpan;
DateTime? start = this.ScheduleStart;
if (period == null || start == null)
{
return entity;
}
// Figure out the time from the start.
DateTime when = start.Value + period.Value * number;
Instant instant = timekeeper.CreateInstant(when);
// If the time hasn't past, then we don't apply it.
Instant now = timekeeper.Clock.GetCurrentInstant();
return instant > now
? entity
: this.Apply(entity, number, instant);
}
/// <summary>
/// Applies the schedule to the entity based on the number and instant
/// given.
/// </summary>
protected virtual Entity Apply(
Entity entity,
int number,
Instant instant)
{
return entity.Set(instant);
}
}

View File

@ -4,27 +4,22 @@ using System.Text.RegularExpressions;
using MfGames.Gallium;
using MfGames.Nitride.Generators;
using NodaTime;
using TimeSpanParserUtil;
using Zio;
namespace MfGames.Nitride.Temporal.Schedules;
/// <summary>
/// A schedule that uses the `UPath` of the entity to determine an offset from
/// a starting point and calculates the information from there.
/// A common base for a schedule that is built off the path associated with
/// the entity.
/// </summary>
[WithProperties]
public partial class NumericalPathSchedule : ISchedule
public abstract partial class PathRegexScheduleBase : ISchedule
{
public NumericalPathSchedule()
protected PathRegexScheduleBase()
{
this.PathRegex = @"^.*?(\d+)[^\d]*$";
this.GetPath = entity => entity.Get<UPath>().ToString();
this.CaptureGroup = 1;
this.CaptureOffset = -1;
}
/// <summary>
@ -49,46 +44,15 @@ public partial class NumericalPathSchedule : ISchedule
/// </summary>
public string? PathRegex { get; set; }
/// <summary>
/// Gets or sets the period between each matching entity. The period is
/// amount of time between each item based on the number. So the first will
/// be on the ScheduleDate, the second SchedulePeriod later, the third
/// SchedulePeriod after the second, etc. More precisely, the date for
/// any item TimeSpan * ((int)CaptureGroup + (int)CaptureOffset).
/// </summary>
public string? SchedulePeriod { get; set; }
/// <summary>
/// Gets or sets the schedule period as a TimeSpan object. This is converted
/// from SchedulePeriod using https://github.com/pengowray/TimeSpanParser.
/// If the value is null or blank or "immediate", then this will be instant
/// (TimeSpan.Zero).
/// </summary>
public virtual TimeSpan SchedulePeriodTimeSpan
{
get => string.IsNullOrWhiteSpace(this.SchedulePeriod)
|| this.SchedulePeriod.Equals(
"immediate",
StringComparison.InvariantCultureIgnoreCase)
? TimeSpan.Zero
: TimeSpanParser.Parse(this.SchedulePeriod);
set => this.SchedulePeriod = value.ToString();
}
/// <summary>
/// Gets or sets when the first item is scheduled.
/// </summary>
public DateTime? ScheduleStart { get; set; }
/// <inheritdoc />
public virtual Entity Apply(
Entity entity,
Timekeeper timekeeper)
{
// Get the path and match it.
string? path = this.GetPath(entity);
string path = this.GetPath(entity);
Regex? regex = this.GetRegex();
Match? match = regex?.Match(path)
Match match = regex?.Match(path)
?? throw new NullReferenceException(
"PathRegex was not configured for the "
+ this.GetType().Name
@ -118,19 +82,8 @@ public partial class NumericalPathSchedule : ISchedule
number += this.CaptureOffset;
// Figure out the time from the start.
DateTime start = this.ScheduleStart
?? throw new NullReferenceException(
"Cannot use a schedule without a start date.");
DateTime when = start + this.SchedulePeriodTimeSpan * number;
Instant instant = timekeeper.CreateInstant(when);
// If the time hasn't past, then we don't apply it.
Instant now = timekeeper.Clock.GetCurrentInstant();
return instant > now
? entity
: this.Apply(entity, number, instant);
// Pass it onto the extending class.
return this.Apply(entity, number, timekeeper);
}
/// <inheritdoc />
@ -138,23 +91,28 @@ public partial class NumericalPathSchedule : ISchedule
{
string path = this.GetPath(entity);
Regex? regex = this.GetRegex();
bool match = regex?.IsMatch(path) ?? false;
Match match = regex?.Match(path)
?? throw new NullReferenceException(
"PathRegex was not configured for the "
+ this.GetType().Name
+ ".");
return match;
return match.Success;
}
public Regex? GetRegex()
/// <summary>
/// Applies the schedule according to the normalized number (after
/// CaptureOffset).
/// </summary>
protected abstract Entity Apply(
Entity entity,
int number,
Timekeeper timekeeper);
private Regex? GetRegex()
{
return this.PathRegex == null
? null
: new Regex(this.PathRegex);
}
protected virtual Entity Apply(
Entity entity,
int number,
Instant instant)
{
return entity.Set(instant);
}
}

View File

@ -0,0 +1,86 @@
using System;
using MfGames.Gallium;
using MfGames.Nitride.Generators;
using NodaTime;
namespace MfGames.Nitride.Temporal.Schedules;
/// <summary>
/// A schedule that uses the `UPath` of the entity to determine an offset from
/// a starting point and calculates the information from there.
/// </summary>
[WithProperties]
public partial class PeriodicPathRegexSchedule : PathRegexScheduleBase
{
public PeriodicPathRegexSchedule()
{
this.CaptureOffset = -1;
}
/// <summary>
/// Gets or sets the period between each matching entity. The period is
/// amount of time between each item based on the number. So the first will
/// be on the ScheduleDate, the second SchedulePeriod later, the third
/// SchedulePeriod after the second, etc. More precisely, the date for
/// any item TimeSpan * ((int)CaptureGroup + (int)CaptureOffset).
/// </summary>
public string? SchedulePeriod { get; set; }
/// <summary>
/// Gets or sets the schedule period as a TimeSpan object. This is converted
/// from SchedulePeriod using https://github.com/pengowray/TimeSpanParser.
/// If the value is null or blank or "immediate", then this will be instant
/// (TimeSpan.Zero).
/// </summary>
public virtual TimeSpan? SchedulePeriodTimeSpan
{
get => TimeSpanHelper.Parse(this.SchedulePeriod);
set => this.SchedulePeriod = value?.ToString();
}
/// <summary>
/// Gets or sets when the first item is scheduled.
/// </summary>
public DateTime? ScheduleStart { get; set; }
/// <inheritdoc />
protected override Entity Apply(
Entity entity,
int number,
Timekeeper timekeeper)
{
// If we have a "never", then we skip it.
TimeSpan? period = this.SchedulePeriodTimeSpan;
DateTime? start = this.ScheduleStart;
if (period == null || start == null)
{
return entity;
}
// Figure out the time from the start.
DateTime when = start.Value + period.Value * number;
Instant instant = timekeeper.CreateInstant(when);
// If the time hasn't past, then we don't apply it.
Instant now = timekeeper.Clock.GetCurrentInstant();
return instant > now
? entity
: this.Apply(entity, number, instant);
}
/// <summary>
/// Applies the schedule to the entity based on the number and instant
/// given.
/// </summary>
protected virtual Entity Apply(
Entity entity,
int number,
Instant instant)
{
return entity.Set(instant);
}
}

View File

@ -57,14 +57,28 @@ The following properties are available (along with their corresponding `With*` m
- Used to determine when the site is being generated.
- Defaults to the one provided by `MfGames.Nitride.Temporal`.
### Numerical Path-Based Schedules
## Provided Schedules
A number of schedules are provided as part of this library.
### Schedule Periods
In all cases, the `SchedulePeriod` is a string that parses into a `TimeSpan` object
that determines the amount of time between two successive entities of the same
schedule. It is parsed using [TimeSpanParser](https://github.com/pengowray/TimeSpanParser)
and allow for `TimeSpan` formatting (such as "1.00:00:00"), descriptions such as
"4 days" or "1 month". In addition, it also handles `instant` (non-case sensitive)
for a zero length time or effectively all at once or `never` (also not case sensitive)
to never apply to it.
### Periodic Path Regex Schedules
A common pattern for using schedules is to dole out a numerical series of posts over
a period of time. For example, a weekly posting of chapters or comic strips. In these
cases, there is usually a number in the path such as `chapter-01` or `comic-0244`. The
schedule starts at a certain point and then continues every day or week as needed.
The `NumericalPathSchedule` encapsulates this pattern. It uses the `UPath` component
The `PeriodicPathRegexSchedule` encapsulates this pattern. It uses the `UPath` component
of the entity and compares it against a regular expression that captures the numerical
part of the path. If it matches, then the schedule sets the date equal to the starting
point plus the "period" for every one past the first.
@ -81,7 +95,7 @@ var entities = new List<Entity>
};
var schedules = new List<ISchedule>
{
new NumericalPathSchedule
new PeriodicPathRegexSchedule
{
PathRegex = "chapter-(\d+),
ScheduleStart = DateTime.Parse("2023-01-01"),
@ -96,7 +110,7 @@ return entities.Run(op.WithSchedules(schedules));
This will have chapter-01 have an `Instant` component set to 2023-01-01, the second
chapter will be set to 2023-01-08, and the third at 2023-01-15.
`NumericalPathSchedule` has the following properties:
`PeriodicPathRegexSchedule` has the following properties:
- `Func<Entity, string> GetPath`
- An override to allow retrieving a different function.
@ -127,7 +141,7 @@ There is also a virtual method for applying the schedule.
#### Overriding Logic
The default operation of a `NumericalPathSchedule` is to only set the `Instant`
The default operation of a `PeriodicPathRegexSchedule` is to only set the `Instant`
component of the entity. The class can be extended to have more site-specific
entries including adding more properties to the schedule and applying them.
@ -168,14 +182,54 @@ Commonly, this schedule will be put into a JSON or YAML file.
```yaml
schedules:
# Patron and Ko-Fi subscribers get it all at once
- pathRegex: chapters/chapter-\d+
scheduleDate: 2025-01-01
- pathRegex: chapters/chapter-(\d+)
scheduleStart: 2025-01-01
schedulePeriod: instant # Because we overrode the default to be weekly.
access: subscribers
# The first fifteen chapters (01-15) were released at the rate of one
# per week starting in 2024. This will replace all the schedules above
# it.
- path: chapters/chapter-(0\d|1[1-5])
scheduleDate: 2030-01-01
scheduleStart: 2030-01-01
access: public
```
### Indexed Path Regex Schedules
A variant on the periodic schedule is the `IndexedPathRegexSchedule`. This evolved
from the periodic one in order to simplify the regular expressions at the expense
of having a more complex structure. It uses the same regular expression as
`PeriodicPathRegexSchedule` including `CaptureGroup` (still defaults to 1) and
`CaptureOffset` (which defaults to 0) but instead of having the schedule date
and period, those are relegated to a `Dictionary<int, IndexedSchedule>` (or a
class that extends `IndexedSchedule`).
Using the below example:
```yaml
schedules:
pathRegex: chapters/chapter-(\d+)
indexes:
1:
scheduleStart: 2025-01-01
schedulePeriod: instant
access: subscribers
10:
scheduleStart: 2030-01-01
schedulePeriod: 1 week
access: subscribers
30:
schedulePeriod: never
```
For a given calculated number (such as `chapter-01` being `1`), the schedule picks the
highest index that is not replaced by a higher number. So, chapters 1-9 would use the 2025
date and be instantly available on that date while chapters 10 and higher would use the
2030 date and be doled out a week at a time.
While this example doesn't use the top level as a sequence, it could be used to handle
multiple overlapping schedules (like the first where two changes), but it is more
verbose while describing it.
For the inner schedule, the number is normalized to be 0-based automatically for each one
so the above example would have 1, 10, and 30 all pass in `0` into the `Apply` function.

View File

@ -0,0 +1,35 @@
using System;
using TimeSpanParserUtil;
namespace MfGames.Nitride.Temporal.Schedules;
public static class TimeSpanHelper
{
public static TimeSpan? Parse(string? input)
{
if (string.IsNullOrWhiteSpace(input))
{
return null;
}
if (input.Equals(
"immediate",
StringComparison.InvariantCultureIgnoreCase)
|| input.Equals(
"instant",
StringComparison.InvariantCultureIgnoreCase))
{
return TimeSpan.Zero;
}
if (input.Equals(
"never",
StringComparison.InvariantCultureIgnoreCase))
{
return null;
}
return TimeSpanParser.Parse(input);
}
}

View File

@ -0,0 +1,260 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MfGames.Gallium;
using MfGames.Nitride.Tests;
using NodaTime;
using NodaTime.Testing;
using Xunit;
using Xunit.Abstractions;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using Zio;
namespace MfGames.Nitride.Temporal.Schedules.Tests;
public class IndexedPathRegexScheduleTest : TemporalSchedulesTestBase
{
public IndexedPathRegexScheduleTest(ITestOutputHelper output)
: base(output)
{
}
[Fact]
public void DeserializedSetupWorks()
{
using TemporalSchedulesTestContext context = this.CreateContext();
// Create a numerical series of entities.
var input = new List<Entity>
{
new Entity().SetAll((UPath)"/chapter-01.md", new TestModel()),
new Entity().SetAll((UPath)"/chapter-02.md", new TestModel()),
new Entity().SetAll((UPath)"/chapter-03.md", new TestModel()),
};
TestModel model = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build()
.Deserialize<TestModel>(
string.Join(
"\n",
"---",
"access: custom",
"schedules:",
" pathRegex: chapter-(\\d+)",
" indexes:",
" 1:",
" scheduleStart: 2020-01-01",
" schedulePeriod: instant",
" access: t-1",
" 2:",
" scheduleStart: 2023-01-02",
" schedulePeriod: 1 week",
" access: t-2",
""));
var schedules = model.Schedules!;
// Create the operation and run it, but treat it as being set after the
// second but before the third item.
Timekeeper time = context.Resolve<Timekeeper>();
ApplySchedules op = context.Resolve<ApplySchedules>()
.WithGetSchedules(_ => new ISchedule[] { schedules });
var now = Instant.FromUtc(2023, 1, 3, 0, 0);
time.Clock = new FakeClock(now);
var actual = op
.Run(input)
.Select(
a => string.Format(
"{0} -- {1} -- {2}",
a.Get<UPath>().ToString(),
a.Has<Instant>()
? time
.ToDateTime(a.Get<Instant>())
.ToString("yyyy-MM-dd")
: "none",
a.Get<TestModel>().Access))
.ToList();
var expected = new List<string>
{
"/chapter-01.md -- 2020-01-01 -- t-1",
"/chapter-02.md -- 2023-01-02 -- t-2",
"/chapter-03.md -- none -- private",
};
TestHelper.CompareObjects(expected, actual);
}
[Fact]
public void ManualSetupWorks()
{
using TemporalSchedulesTestContext context = this.CreateContext();
// Create a numerical series of entities.
var input = new List<Entity>
{
new Entity().SetAll((UPath)"/chapter-01.md", new TestModel()),
new Entity().SetAll((UPath)"/chapter-02.md", new TestModel()),
new Entity().SetAll((UPath)"/chapter-03.md", new TestModel()),
};
var schedules = new TestRegexSchedule
{
Indexes = new Dictionary<int, TestSchedule>
{
[1] = new TestSchedule
{
ScheduleStart = DateTime.Parse("2023-01-01"),
Access = "public",
},
},
};
// Create the operation and run it, but treat it as being set after the
// second but before the third item.
Timekeeper time = context.Resolve<Timekeeper>();
ApplySchedules op = context.Resolve<ApplySchedules>()
.WithGetSchedules(_ => new ISchedule[] { schedules });
var now = Instant.FromUtc(2023, 1, 9, 0, 0);
time.Clock = new FakeClock(now);
var actual = op
.Run(input)
.Select(
a => string.Format(
"{0} -- {1} -- {2}",
a.Get<UPath>().ToString(),
a.Has<Instant>()
? time
.ToDateTime(a.Get<Instant>())
.ToString("yyyy-MM-dd")
: "none",
a.Get<TestModel>().Access))
.ToList();
var expected = new List<string>
{
"/chapter-01.md -- 2023-01-01 -- public",
"/chapter-02.md -- 2023-01-08 -- public",
"/chapter-03.md -- none -- private",
};
TestHelper.CompareObjects(expected, actual);
}
[Fact]
public void SequencedScheduleWorks()
{
using TemporalSchedulesTestContext context = this.CreateContext();
// Create a numerical series of entities.
var input = new List<Entity>
{
new Entity().SetAll((UPath)"/chapter-01.md", new TestModel()),
new Entity().SetAll((UPath)"/chapter-02.md", new TestModel()),
new Entity().SetAll((UPath)"/chapter-03.md", new TestModel()),
new Entity().SetAll((UPath)"/chapter-04.md", new TestModel()),
new Entity().SetAll((UPath)"/chapter-05.md", new TestModel()),
new Entity().SetAll((UPath)"/chapter-06.md", new TestModel()),
};
var schedules = new TestRegexSchedule()
{
Indexes = new Dictionary<int, TestSchedule>
{
[1] = new()
{
ScheduleStart = DateTime.Parse("2020-01-01"),
Access = "subscriber",
SchedulePeriodTimeSpan = TimeSpan.FromDays(7),
},
[3] = new()
{
ScheduleStart = DateTime.Parse("2023-01-07"),
Access = "public",
SchedulePeriodTimeSpan = TimeSpan.Zero,
},
[5] = new()
{
SchedulePeriod = "never",
},
},
};
// Create the operation and run it, but treat it as being set after the
// second but before the third item.
Timekeeper time = context.Resolve<Timekeeper>();
ApplySchedules op = context.Resolve<ApplySchedules>()
.WithGetSchedules(_ => new ISchedule[] { schedules });
var now = Instant.FromUtc(2023, 1, 9, 0, 0);
time.Clock = new FakeClock(now);
var actual = op
.Run(input)
.Select(
a => string.Format(
"{0} -- {1} -- {2}",
a.Get<UPath>().ToString(),
a.Has<Instant>()
? time
.ToDateTime(a.Get<Instant>())
.ToString("yyyy-MM-dd")
: "none",
a.Get<TestModel>().Access))
.ToList();
var expected = new List<string>
{
"/chapter-01.md -- 2020-01-01 -- subscriber",
"/chapter-02.md -- 2020-01-08 -- subscriber",
"/chapter-03.md -- 2023-01-07 -- public",
"/chapter-04.md -- 2023-01-07 -- public",
"/chapter-05.md -- none -- private",
"/chapter-06.md -- none -- private",
};
TestHelper.CompareObjects(expected, actual);
}
public class TestModel
{
public string? Access { get; set; } = "private";
public TestRegexSchedule? Schedules { get; set; }
}
public class TestRegexSchedule : IndexedPathRegexSchedule<TestSchedule>
{
}
public class TestSchedule : IndexedSchedule
{
public TestSchedule()
{
this.SchedulePeriod = "1 week";
}
public string? Access { get; set; }
/// <inheritdoc />
protected override Entity Apply(
Entity entity,
int number,
Instant instant)
{
TestModel model = entity.Get<TestModel>();
model.Access = this.Access;
return entity.SetAll(instant, model);
}
}
}

View File

@ -6,17 +6,17 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\MfGames.Nitride.Temporal.Schedules\MfGames.Nitride.Temporal.Schedules.csproj"/>
<ProjectReference Include="..\MfGames.Nitride.Tests\MfGames.Nitride.Tests.csproj"/>
<ProjectReference Include="..\..\src\MfGames.Nitride.Temporal.Schedules\MfGames.Nitride.Temporal.Schedules.csproj" />
<ProjectReference Include="..\MfGames.Nitride.Tests\MfGames.Nitride.Tests.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CompareNETObjects" Version="4.78.0"/>
<PackageReference Include="MfGames.Gallium" Version="0.4.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1"/>
<PackageReference Include="JunitXml.TestLogger" Version="3.0.114"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.2"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="CompareNETObjects" Version="4.78.0" />
<PackageReference Include="MfGames.Gallium" Version="0.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="JunitXml.TestLogger" Version="3.0.114" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@ -25,7 +25,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="YamlDotNet" Version="12.0.0"/>
<PackageReference Include="YamlDotNet" Version="12.0.0" />
</ItemGroup>
</Project>

View File

@ -18,9 +18,9 @@ using Zio;
namespace MfGames.Nitride.Temporal.Schedules.Tests;
public class NumericalPathScheduleTests : TemporalSchedulesTestBase
public class PeriodicPathRegexScheduleTest : TemporalSchedulesTestBase
{
public NumericalPathScheduleTests(ITestOutputHelper output)
public PeriodicPathRegexScheduleTest(ITestOutputHelper output)
: base(output)
{
}
@ -52,7 +52,7 @@ public class NumericalPathScheduleTests : TemporalSchedulesTestBase
" schedulePeriod: 1 week",
" access: public",
""));
List<TestSchedule>? schedules = model.Schedules!;
List<TestRegexSchedule>? schedules = model.Schedules!;
// Create the operation and run it, but treat it as being set after the
// second but before the third item.
@ -100,7 +100,7 @@ public class NumericalPathScheduleTests : TemporalSchedulesTestBase
new Entity().SetAll((UPath)"/chapter-03.md", new TestModel()),
};
var schedules = new List<TestSchedule>
var schedules = new List<TestRegexSchedule>
{
new()
{
@ -155,7 +155,7 @@ public class NumericalPathScheduleTests : TemporalSchedulesTestBase
new Entity().SetAll((UPath)"/chapter-13.md", new TestModel()),
};
var schedules = new List<TestSchedule>
var schedules = new List<TestRegexSchedule>
{
new()
{
@ -211,7 +211,7 @@ public class NumericalPathScheduleTests : TemporalSchedulesTestBase
new Entity().SetAll((UPath)"/chapter-03.md", new TestModel()),
};
var schedules = new List<TestSchedule>
var schedules = new List<TestRegexSchedule>
{
new()
{
@ -262,12 +262,12 @@ public class NumericalPathScheduleTests : TemporalSchedulesTestBase
{
public string? Access { get; set; } = "private";
public List<TestSchedule>? Schedules { get; set; }
public List<TestRegexSchedule>? Schedules { get; set; }
}
public class TestSchedule : NumericalPathSchedule
public class TestRegexSchedule : PeriodicPathRegexSchedule
{
public TestSchedule()
public TestRegexSchedule()
{
this.SchedulePeriod = "1 week";
}