|
22 | 22 |
|
23 | 23 | using System; |
24 | 24 | using System.Collections.Concurrent; |
| 25 | +using System.Diagnostics; |
25 | 26 | using System.Linq; |
26 | 27 | using System.Threading; |
27 | 28 | using System.Threading.Tasks; |
@@ -124,40 +125,55 @@ public async Task ScheduleNow() |
124 | 125 | [Test] |
125 | 126 | public async Task ScheduleAsyncWithDateTime() |
126 | 127 | { |
127 | | - await ValidateScheduleHappens((j, s) => s.ScheduleAsync(j, DateTimeOffset.Now.AddSeconds(1), CancellationToken.None)).ConfigureAwait(false); |
| 128 | + var minimumDelay = TimeSpan.FromSeconds(0.75); |
| 129 | + var runAt = DateTimeOffset.UtcNow.AddSeconds(1); |
| 130 | + await ValidateScheduleHappens( |
| 131 | + (j, s) => s.ScheduleAsync(j, runAt, CancellationToken.None), |
| 132 | + minimumDelay).ConfigureAwait(false); |
128 | 133 | } |
129 | 134 |
|
130 | 135 | [Test] |
131 | 136 | public async Task ScheduleAsyncWithTimeSpan() |
132 | 137 | { |
133 | | - await ValidateScheduleHappens((j, s) => s.ScheduleAsync(j, TimeSpan.FromSeconds(1), CancellationToken.None)).ConfigureAwait(false); |
| 138 | + var minimumDelay = TimeSpan.FromSeconds(0.75); |
| 139 | + await ValidateScheduleHappens( |
| 140 | + (j, s) => s.ScheduleAsync(j, TimeSpan.FromSeconds(1), CancellationToken.None), |
| 141 | + minimumDelay).ConfigureAwait(false); |
134 | 142 | } |
135 | 143 |
|
136 | | - private async Task ValidateScheduleHappens(Func<IJob, IJobScheduler, Task<IJobId>> schedule) |
| 144 | + private async Task ValidateScheduleHappens(Func<IJob, IJobScheduler, Task<IJobId>> schedule, TimeSpan? minimumElapsed = null) |
137 | 145 | { |
138 | 146 | // Arrange |
139 | 147 | var testId = ThingyId.New; |
140 | 148 | var pingId = PingId.New; |
141 | 149 | var executeCommandJob = PublishCommandJob.Create(new ThingyPingCommand(testId, pingId), ServiceProvider); |
142 | 150 |
|
143 | 151 | // Act |
| 152 | + // Use a monotonic timer to avoid false negatives from wall-clock drift in CI. |
| 153 | + var stopwatch = Stopwatch.StartNew(); |
144 | 154 | var jobId = await schedule(executeCommandJob, _jobScheduler).ConfigureAwait(false); |
145 | 155 |
|
146 | 156 | // Assert |
147 | | - var start = DateTimeOffset.Now; |
148 | | - while (DateTimeOffset.Now < start + TimeSpan.FromSeconds(10)) |
| 157 | + while (stopwatch.Elapsed < TimeSpan.FromSeconds(10)) |
149 | 158 | { |
150 | 159 | var testAggregate = await AggregateStore.LoadAsync<ThingyAggregate, ThingyId>(testId, CancellationToken.None).ConfigureAwait(false); |
151 | 160 | if (!testAggregate.IsNew) |
152 | 161 | { |
| 162 | + var elapsed = stopwatch.Elapsed; |
| 163 | + stopwatch.Stop(); |
153 | 164 | await AssertJobIsSuccessfullyAsync(jobId).ConfigureAwait(false); |
| 165 | + if (minimumElapsed.HasValue) |
| 166 | + { |
| 167 | + elapsed.ShouldBeGreaterThanOrEqualTo(minimumElapsed.Value); |
| 168 | + } |
154 | 169 | Assert.Contains(pingId, testAggregate.PingsReceived.ToList()); |
155 | | - Assert.Pass(); |
| 170 | + return; |
156 | 171 | } |
157 | 172 |
|
158 | 173 | await Task.Delay(TimeSpan.FromSeconds(0.2)).ConfigureAwait(false); |
159 | 174 | } |
160 | 175 |
|
| 176 | + stopwatch.Stop(); |
161 | 177 | Assert.Fail("Aggregate did not receive the command as expected"); |
162 | 178 | } |
163 | 179 |
|
|
0 commit comments