@@ -19,17 +19,15 @@ impl Subscriber {
19
19
Self { rx }
20
20
}
21
21
22
- pub fn watch (
22
+ pub async fn watch (
23
23
self ,
24
24
// We use a tokio::sync::Mutex here because we want this future to be Send.
25
25
#[ allow( clippy:: type_complexity) ] state : Arc < Mutex < RefCell < WebUIState > > > ,
26
26
) {
27
- tokio:: spawn ( async move {
28
- let mut rx = self . rx ;
29
- while let Some ( event) = rx. recv ( ) . await {
30
- Self :: add_message ( & state, event) . await ;
31
- }
32
- } ) ;
27
+ let mut rx = self . rx ;
28
+ while let Some ( event) = rx. recv ( ) . await {
29
+ Self :: add_message ( & state, event) . await ;
30
+ }
33
31
}
34
32
35
33
async fn add_message ( state : & Arc < Mutex < RefCell < WebUIState > > > , event : WebUIEvent ) {
@@ -69,8 +67,10 @@ impl Subscriber {
69
67
} => {
70
68
let mut state_ref = state. borrow_mut ( ) ;
71
69
70
+ if result == CacheResult :: Hit {
71
+ state_ref. tasks . get_mut ( & task) . unwrap ( ) . status = TaskStatus :: Cached ;
72
+ }
72
73
state_ref. tasks . get_mut ( & task) . unwrap ( ) . cache_result = Some ( result) ;
73
-
74
74
state_ref. tasks . get_mut ( & task) . unwrap ( ) . cache_message = Some ( message) ;
75
75
}
76
76
WebUIEvent :: Stop => {
@@ -118,13 +118,13 @@ pub enum TaskStatus {
118
118
Running ,
119
119
Cached ,
120
120
Failed ,
121
- Success ,
121
+ Succeeded ,
122
122
}
123
123
124
124
impl From < TaskResult > for TaskStatus {
125
125
fn from ( result : TaskResult ) -> Self {
126
126
match result {
127
- TaskResult :: Success => Self :: Success ,
127
+ TaskResult :: Success => Self :: Succeeded ,
128
128
TaskResult :: CacheHit => Self :: Cached ,
129
129
TaskResult :: Failure => Self :: Failed ,
130
130
}
@@ -150,3 +150,139 @@ impl WebUIState {
150
150
& self . tasks
151
151
}
152
152
}
153
+
154
+ #[ cfg( test) ]
155
+ mod test {
156
+ use async_graphql:: { EmptyMutation , EmptySubscription , Schema } ;
157
+
158
+ use super :: * ;
159
+ use crate :: {
160
+ tui:: event:: OutputLogs ,
161
+ wui:: { sender:: WebUISender , server:: Query } ,
162
+ } ;
163
+
164
+ #[ tokio:: test]
165
+ async fn test_web_ui_state ( ) -> Result < ( ) , crate :: Error > {
166
+ let ( tx, rx) = tokio:: sync:: mpsc:: unbounded_channel ( ) ;
167
+ let state = Arc :: new ( Mutex :: new ( RefCell :: new ( WebUIState :: default ( ) ) ) ) ;
168
+ let subscriber = Subscriber :: new ( rx) ;
169
+
170
+ let sender = WebUISender :: new ( tx) ;
171
+
172
+ // Start a successful task
173
+ sender. start_task ( "task" . to_string ( ) , OutputLogs :: Full ) ;
174
+ sender. output ( "task" . to_string ( ) , b"this is my output" . to_vec ( ) ) ?;
175
+ sender. end_task ( "task" . to_string ( ) , TaskResult :: Success ) ;
176
+
177
+ // Start a cached task
178
+ sender. start_task ( "task2" . to_string ( ) , OutputLogs :: Full ) ;
179
+ sender. status ( "task2" . to_string ( ) , "status" . to_string ( ) , CacheResult :: Hit ) ;
180
+
181
+ // Start a failing task
182
+ sender. start_task ( "task3" . to_string ( ) , OutputLogs :: Full ) ;
183
+ sender. end_task ( "task3" . to_string ( ) , TaskResult :: Failure ) ;
184
+
185
+ // Drop the sender so the subscriber can terminate
186
+ drop ( sender) ;
187
+
188
+ // Run the subscriber blocking
189
+ subscriber. watch ( state. clone ( ) ) . await ;
190
+
191
+ let state_handle = state. lock ( ) . await . borrow ( ) . clone ( ) ;
192
+ assert_eq ! ( state_handle. tasks( ) . len( ) , 3 ) ;
193
+ assert_eq ! (
194
+ state_handle. tasks( ) . get( "task2" ) . unwrap( ) . status,
195
+ TaskStatus :: Cached
196
+ ) ;
197
+ assert_eq ! (
198
+ state_handle. tasks( ) . get( "task" ) . unwrap( ) . status,
199
+ TaskStatus :: Succeeded
200
+ ) ;
201
+ assert_eq ! (
202
+ state_handle. tasks( ) . get( "task" ) . unwrap( ) . output,
203
+ b"this is my output"
204
+ ) ;
205
+ assert_eq ! (
206
+ state_handle. tasks( ) . get( "task3" ) . unwrap( ) . status,
207
+ TaskStatus :: Failed
208
+ ) ;
209
+
210
+ // Now let's check with the GraphQL API
211
+ let schema = Schema :: new ( Query :: new ( state) , EmptyMutation , EmptySubscription ) ;
212
+ let result = schema
213
+ . execute ( "query { currentRun { tasks { name state { status } } } }" )
214
+ . await ;
215
+ assert ! ( result. errors. is_empty( ) ) ;
216
+ assert_eq ! (
217
+ result. data,
218
+ async_graphql:: Value :: from_json( serde_json:: json!( {
219
+ "currentRun" : {
220
+ "tasks" : [
221
+ {
222
+ "name" : "task" ,
223
+ "state" : {
224
+ "status" : "SUCCEEDED"
225
+ }
226
+ } ,
227
+ {
228
+ "name" : "task2" ,
229
+ "state" : {
230
+ "status" : "CACHED"
231
+ }
232
+ } ,
233
+ {
234
+ "name" : "task3" ,
235
+ "state" : {
236
+ "status" : "FAILED"
237
+ }
238
+ }
239
+ ]
240
+ }
241
+ } ) )
242
+ . unwrap( )
243
+ ) ;
244
+
245
+ Ok ( ( ) )
246
+ }
247
+
248
+ #[ tokio:: test]
249
+ async fn test_restart_tasks ( ) -> Result < ( ) , crate :: Error > {
250
+ let ( tx, rx) = tokio:: sync:: mpsc:: unbounded_channel ( ) ;
251
+ let state = Arc :: new ( Mutex :: new ( RefCell :: new ( WebUIState :: default ( ) ) ) ) ;
252
+ let subscriber = Subscriber :: new ( rx) ;
253
+
254
+ let sender = WebUISender :: new ( tx) ;
255
+
256
+ // Start a successful task
257
+ sender. start_task ( "task" . to_string ( ) , OutputLogs :: Full ) ;
258
+ sender. output ( "task" . to_string ( ) , b"this is my output" . to_vec ( ) ) ?;
259
+ sender. end_task ( "task" . to_string ( ) , TaskResult :: Success ) ;
260
+
261
+ // Start a cached task
262
+ sender. start_task ( "task2" . to_string ( ) , OutputLogs :: Full ) ;
263
+ sender. status ( "task2" . to_string ( ) , "status" . to_string ( ) , CacheResult :: Hit ) ;
264
+
265
+ // Restart a task
266
+ sender. restart_tasks ( vec ! [ "task" . to_string( ) ] ) ?;
267
+
268
+ // Drop the sender so the subscriber can terminate
269
+ drop ( sender) ;
270
+
271
+ // Run the subscriber blocking
272
+ subscriber. watch ( state. clone ( ) ) . await ;
273
+
274
+ let state_handle = state. lock ( ) . await . borrow ( ) . clone ( ) ;
275
+ assert_eq ! ( state_handle. tasks( ) . len( ) , 3 ) ;
276
+ assert_eq ! (
277
+ state_handle. tasks( ) . get( "task2" ) . unwrap( ) . status,
278
+ TaskStatus :: Cached
279
+ ) ;
280
+ assert_eq ! (
281
+ state_handle. tasks( ) . get( "task" ) . unwrap( ) . status,
282
+ TaskStatus :: Running
283
+ ) ;
284
+ assert_eq ! ( state_handle. tasks( ) . get( "task" ) . unwrap( ) . output, vec![ ] ) ;
285
+
286
+ Ok ( ( ) )
287
+ }
288
+ }
0 commit comments