@@ -10,9 +10,9 @@ namespace Microsoft.DurableTask.ScheduledTasks.Tests.Entity;
1010
1111public class ScheduleTests
1212{
13- private readonly Schedule schedule ;
14- private readonly string scheduleId = "test-schedule" ;
15- private readonly TestLogger logger ;
13+ readonly Schedule schedule ;
14+ readonly string scheduleId = "test-schedule" ;
15+ readonly TestLogger logger ;
1616
1717 public ScheduleTests ( ITestOutputHelper output )
1818 {
@@ -21,9 +21,9 @@ public ScheduleTests(ITestOutputHelper output)
2121 }
2222
2323 // Simple TestLogger implementation for capturing logs
24- private class TestLogger : ILogger < Schedule >
24+ class TestLogger : ILogger < Schedule >
2525 {
26- public List < ( LogLevel Level , string Message ) > Logs { get ; } = new List < ( LogLevel , string ) > ( ) ;
26+ public List < ( LogLevel Level , string Message ) > Logs { get ; } = new ( ) ;
2727
2828 public IDisposable ? BeginScope < TState > ( TState state ) where TState : notnull => null ;
2929
@@ -55,7 +55,7 @@ public async Task CreateSchedule_WithValidOptions_CreatesSchedule()
5555 await this . schedule . RunAsync ( operation ) ;
5656
5757 // Assert
58- object ? state = operation . State . GetState ( typeof ( ScheduleState ) ) ;
58+ var state = operation . State . GetState ( typeof ( ScheduleState ) ) ;
5959 Assert . NotNull ( state ) ;
6060 ScheduleState scheduleState = Assert . IsType < ScheduleState > ( state ) ;
6161 Assert . NotNull ( scheduleState . ScheduleConfiguration ) ;
@@ -81,18 +81,36 @@ public async Task PauseSchedule_WhenAlreadyPaused_ThrowsInvalidTransitionExcepti
8181 createOptions ) ;
8282 await this . schedule . RunAsync ( createOperation ) ;
8383
84+ // assert after create
85+ var state = createOperation . State . GetState ( typeof ( ScheduleState ) ) ;
86+ Assert . NotNull ( state ) ;
87+ ScheduleState scheduleState = Assert . IsType < ScheduleState > ( state ) ;
88+ Assert . Equal ( ScheduleStatus . Active , scheduleState . Status ) ;
89+
8490 // Pause first time
8591 TestEntityOperation pauseOperation = new TestEntityOperation (
8692 nameof ( Schedule . PauseSchedule ) ,
8793 createOperation . State ,
8894 null ) ;
8995 await this . schedule . RunAsync ( pauseOperation ) ;
9096
97+ // assert after first pause
98+ var stateAfterFirstPause = pauseOperation . State . GetState ( typeof ( ScheduleState ) ) ;
99+ Assert . NotNull ( stateAfterFirstPause ) ;
100+ ScheduleState scheduleStateAfterFirstPause = Assert . IsType < ScheduleState > ( stateAfterFirstPause ) ;
101+ Assert . Equal ( ScheduleStatus . Paused , scheduleStateAfterFirstPause . Status ) ;
102+
103+ // Pause second time
104+ TestEntityOperation pauseOperation2 = new TestEntityOperation (
105+ nameof ( Schedule . PauseSchedule ) ,
106+ pauseOperation . State ,
107+ null ) ;
108+
91109 // Act & Assert
92110 await Assert . ThrowsAsync < ScheduleInvalidTransitionException > ( ( ) =>
93111 this . schedule . RunAsync ( new TestEntityOperation (
94112 nameof ( Schedule . PauseSchedule ) ,
95- pauseOperation . State ,
113+ pauseOperation2 . State ,
96114 null ) ) . AsTask ( ) ) ;
97115 }
98116
@@ -112,21 +130,37 @@ public async Task ResumeSchedule_WhenPaused_ResumesSchedule()
112130 createOptions ) ;
113131 await this . schedule . RunAsync ( createOperation ) ;
114132
133+ // Assert initial state is active
134+ var stateAfterCreate = createOperation . State . GetState ( typeof ( ScheduleState ) ) ;
135+ Assert . NotNull ( stateAfterCreate ) ;
136+ ScheduleState scheduleStateAfterCreate = Assert . IsType < ScheduleState > ( stateAfterCreate ) ;
137+ Assert . Equal ( ScheduleStatus . Active , scheduleStateAfterCreate . Status ) ;
138+
115139 // Pause
116140 TestEntityOperation pauseOperation = new TestEntityOperation (
117141 nameof ( Schedule . PauseSchedule ) ,
118142 createOperation . State ,
119143 null ) ;
120144 await this . schedule . RunAsync ( pauseOperation ) ;
121145
146+ // Assert paused state
147+ var stateAfterPause = pauseOperation . State . GetState ( typeof ( ScheduleState ) ) ;
148+ Assert . NotNull ( stateAfterPause ) ;
149+ ScheduleState scheduleStateAfterPause = Assert . IsType < ScheduleState > ( stateAfterPause ) ;
150+ Assert . Equal ( ScheduleStatus . Paused , scheduleStateAfterPause . Status ) ;
151+
122152 // Act
123153 TestEntityOperation resumeOperation = new TestEntityOperation (
124154 nameof ( Schedule . ResumeSchedule ) ,
125155 pauseOperation . State ,
126156 null ) ;
127157 await this . schedule . RunAsync ( resumeOperation ) ;
128158
129- // Assert
159+ // Assert resumed state
160+ var stateAfterResume = resumeOperation . State . GetState ( typeof ( ScheduleState ) ) ;
161+ Assert . NotNull ( stateAfterResume ) ;
162+ ScheduleState scheduleStateAfterResume = Assert . IsType < ScheduleState > ( stateAfterResume ) ;
163+ Assert . Equal ( ScheduleStatus . Active , scheduleStateAfterResume . Status ) ;
130164 }
131165
132166 [ Fact ]
@@ -145,6 +179,12 @@ public async Task ResumeSchedule_WhenActive_ThrowsInvalidTransitionException()
145179 createOptions ) ;
146180 await this . schedule . RunAsync ( createOperation ) ;
147181
182+ // Assert initial state is active
183+ object ? stateAfterCreate = createOperation . State . GetState ( typeof ( ScheduleState ) ) ;
184+ Assert . NotNull ( stateAfterCreate ) ;
185+ ScheduleState scheduleStateAfterCreate = Assert . IsType < ScheduleState > ( stateAfterCreate ) ;
186+ Assert . Equal ( ScheduleStatus . Active , scheduleStateAfterCreate . Status ) ;
187+
148188 // Act & Assert
149189 await Assert . ThrowsAsync < ScheduleInvalidTransitionException > ( ( ) =>
150190 this . schedule . RunAsync ( new TestEntityOperation (
@@ -169,6 +209,12 @@ public async Task UpdateSchedule_WithValidOptions_UpdatesSchedule()
169209 createOptions ) ;
170210 await this . schedule . RunAsync ( createOperation ) ;
171211
212+ // Assert initial state
213+ object ? stateAfterCreate = createOperation . State . GetState ( typeof ( ScheduleState ) ) ;
214+ Assert . NotNull ( stateAfterCreate ) ;
215+ ScheduleState scheduleStateAfterCreate = Assert . IsType < ScheduleState > ( stateAfterCreate ) ;
216+ Assert . Equal ( TimeSpan . FromMinutes ( 5 ) , scheduleStateAfterCreate . ScheduleConfiguration ? . Interval ) ;
217+
172218 ScheduleUpdateOptions updateOptions = new ScheduleUpdateOptions
173219 {
174220 Interval = TimeSpan . FromMinutes ( 10 )
@@ -181,7 +227,11 @@ public async Task UpdateSchedule_WithValidOptions_UpdatesSchedule()
181227 updateOptions ) ;
182228 await this . schedule . RunAsync ( updateOperation ) ;
183229
184- // Assert
230+ // Assert updated state
231+ object ? stateAfterUpdate = updateOperation . State . GetState ( typeof ( ScheduleState ) ) ;
232+ Assert . NotNull ( stateAfterUpdate ) ;
233+ ScheduleState scheduleStateAfterUpdate = Assert . IsType < ScheduleState > ( stateAfterUpdate ) ;
234+ Assert . Equal ( TimeSpan . FromMinutes ( 10 ) , scheduleStateAfterUpdate . ScheduleConfiguration ? . Interval ) ;
185235 }
186236
187237 [ Fact ]
@@ -200,11 +250,17 @@ public async Task UpdateSchedule_WithNullOptions_ThrowsArgumentNullException()
200250 createOptions ) ;
201251 await this . schedule . RunAsync ( createOperation ) ;
202252
253+ // Assert initial state
254+ object ? stateAfterCreate = createOperation . State . GetState ( typeof ( ScheduleState ) ) ;
255+ Assert . NotNull ( stateAfterCreate ) ;
256+ ScheduleState scheduleStateAfterCreate = Assert . IsType < ScheduleState > ( stateAfterCreate ) ;
257+ Assert . Equal ( TimeSpan . FromMinutes ( 5 ) , scheduleStateAfterCreate . ScheduleConfiguration ? . Interval ) ;
258+
203259 // Act & Assert
204- await Assert . ThrowsAsync < ScheduleClientValidationException > ( ( ) =>
260+ await Assert . ThrowsAsync < InvalidOperationException > ( ( ) =>
205261 this . schedule . RunAsync ( new TestEntityOperation (
206262 nameof ( Schedule . UpdateSchedule ) ,
207- createOperation . State ,
263+ stateAfterCreate ,
208264 null ) ) . AsTask ( ) ) ;
209265 }
210266
@@ -224,19 +280,33 @@ public async Task RunSchedule_WhenNotActive_ThrowsInvalidOperationException()
224280 createOptions ) ;
225281 await this . schedule . RunAsync ( createOperation ) ;
226282
283+ // Assert initial state is active
284+ object ? stateAfterCreate = createOperation . State . GetState ( typeof ( ScheduleState ) ) ;
285+ Assert . NotNull ( stateAfterCreate ) ;
286+ ScheduleState scheduleStateAfterCreate = Assert . IsType < ScheduleState > ( stateAfterCreate ) ;
287+ Assert . Equal ( ScheduleStatus . Active , scheduleStateAfterCreate . Status ) ;
288+
227289 // Pause
228290 TestEntityOperation pauseOperation = new TestEntityOperation (
229291 nameof ( Schedule . PauseSchedule ) ,
230292 createOperation . State ,
231293 null ) ;
232294 await this . schedule . RunAsync ( pauseOperation ) ;
233295
296+ // Assert paused state
297+ object ? stateAfterPause = pauseOperation . State . GetState ( typeof ( ScheduleState ) ) ;
298+ Assert . NotNull ( stateAfterPause ) ;
299+ ScheduleState scheduleStateAfterPause = Assert . IsType < ScheduleState > ( stateAfterPause ) ;
300+ Assert . Equal ( ScheduleStatus . Paused , scheduleStateAfterPause . Status ) ;
301+
302+ // run schedule op
303+ TestEntityOperation runOp = new TestEntityOperation (
304+ nameof ( Schedule . RunSchedule ) ,
305+ scheduleStateAfterPause ,
306+ scheduleStateAfterPause . ExecutionToken ) ;
234307 // Act & Assert
235308 await Assert . ThrowsAsync < InvalidOperationException > ( ( ) =>
236- this . schedule . RunAsync ( new TestEntityOperation (
237- nameof ( Schedule . RunSchedule ) ,
238- pauseOperation . State ,
239- "token" ) ) . AsTask ( ) ) ;
309+ this . schedule . RunAsync ( runOp ) . AsTask ( ) ) ;
240310 }
241311
242312 [ Fact ]
@@ -255,12 +325,26 @@ public async Task RunSchedule_WithInvalidToken_DoesNotRun()
255325 createOptions ) ;
256326 await this . schedule . RunAsync ( createOperation ) ;
257327
328+ // Assert initial state
329+ object ? stateAfterCreate = createOperation . State . GetState ( typeof ( ScheduleState ) ) ;
330+ Assert . NotNull ( stateAfterCreate ) ;
331+ ScheduleState scheduleStateAfterCreate = Assert . IsType < ScheduleState > ( stateAfterCreate ) ;
332+ Assert . Equal ( ScheduleStatus . Active , scheduleStateAfterCreate . Status ) ;
333+ string ? initialToken = scheduleStateAfterCreate . ExecutionToken ;
334+
258335 // Act
259- await this . schedule . RunAsync ( new TestEntityOperation (
336+ TestEntityOperation runOperation = new TestEntityOperation (
260337 nameof ( Schedule . RunSchedule ) ,
261338 createOperation . State ,
262- "invalid-token" ) ) ;
263-
264- // Assert
339+ "invalid-token" ) ;
340+ await this . schedule . RunAsync ( runOperation ) ;
341+
342+ // Assert state unchanged
343+ object ? stateAfterRun = runOperation . State . GetState ( typeof ( ScheduleState ) ) ;
344+ Assert . NotNull ( stateAfterRun ) ;
345+ ScheduleState scheduleStateAfterRun = Assert . IsType < ScheduleState > ( stateAfterRun ) ;
346+ Assert . Equal ( ScheduleStatus . Active , scheduleStateAfterRun . Status ) ;
347+ Assert . Equal ( initialToken , scheduleStateAfterRun . ExecutionToken ) ;
348+ Assert . Null ( scheduleStateAfterRun . LastRunAt ) ;
265349 }
266350}
0 commit comments