33 * SPDX-License-Identifier: MPL-2.0
44 */
55
6- import { createActor , fromCallback , getNextSnapshot } from "xstate" ;
6+ import { createActor , fromCallback , getNextSnapshot , waitFor } from "xstate" ;
77import { spawnInteractive } from "../../lib/models/interactive-process" ;
88import {
99 deployMachine ,
@@ -60,7 +60,7 @@ describe("transitions", () => {
6060 expect ( actualState . matches ( "running" ) ) . toBe ( true ) ;
6161 } ) ;
6262
63- it ( "transitions to processing upon external approval" , ( done ) => {
63+ it ( "transitions to processing upon external approval" , async ( ) => {
6464 const mockDeployMachine = deployMachine . provide ( {
6565 actors : {
6666 runTerraformInPty : fromCallback < DeployEvent , PtyActorInput > (
@@ -90,14 +90,12 @@ describe("transitions", () => {
9090 } ,
9191 } ) ;
9292
93- ptyService . subscribe ( ( state ) => {
94- if (
93+ const processing = waitFor (
94+ ptyService ,
95+ ( state ) =>
9596 state . matches ( { running : "processing" } ) &&
96- lastEvent === "APPROVED_EXTERNALLY"
97- ) {
98- done ( ) ;
99- }
100- } ) ;
97+ lastEvent === "APPROVED_EXTERNALLY" ,
98+ ) ;
10199
102100 ptyService . start ( ) ;
103101
@@ -111,6 +109,8 @@ describe("transitions", () => {
111109 } ,
112110 } ,
113111 } ) ;
112+
113+ await processing ;
114114 } ) ;
115115} ) ;
116116
@@ -143,8 +143,7 @@ function mockPty(ptyEvents: string[]): typeof spawnInteractive {
143143}
144144
145145describe ( "pty events" , ( ) => {
146- it ( "transitions when pty receives an approval question" , ( done ) => {
147- let isDone = false ;
146+ it ( "transitions when pty receives an approval question" , async ( ) => {
148147 const mockDeployMachine = deployMachine . provide ( {
149148 actors : {
150149 runTerraformInPty : makeTerraformPtyService (
@@ -154,12 +153,9 @@ describe("pty events", () => {
154153 } ) ;
155154
156155 const ptyService = createActor ( mockDeployMachine ) ;
157- ptyService . subscribe ( ( state ) => {
158- if ( state . matches ( { running : "awaiting_approval" } ) && ! isDone ) {
159- isDone = true ;
160- done ( ) ;
161- }
162- } ) ;
156+ const awaitingApproval = waitFor ( ptyService , ( state ) =>
157+ state . matches ( { running : "awaiting_approval" } ) ,
158+ ) ;
163159
164160 ptyService . start ( ) ;
165161
@@ -173,10 +169,11 @@ describe("pty events", () => {
173169 } ,
174170 } ,
175171 } ) ;
172+
173+ await awaitingApproval ;
176174 } ) ;
177175
178- it ( "transitions when pty receives an approval question that is split across two buffers" , ( done ) => {
179- let isDone = false ;
176+ it ( "transitions when pty receives an approval question that is split across two buffers" , async ( ) => {
180177 const mockDeployMachine = deployMachine . provide ( {
181178 actors : {
182179 runTerraformInPty : makeTerraformPtyService (
@@ -186,12 +183,9 @@ describe("pty events", () => {
186183 } ) ;
187184
188185 const ptyService = createActor ( mockDeployMachine ) ;
189- ptyService . subscribe ( ( state ) => {
190- if ( state . matches ( { running : "awaiting_approval" } ) && ! isDone ) {
191- isDone = true ;
192- done ( ) ;
193- }
194- } ) ;
186+ const awaitingApproval = waitFor ( ptyService , ( state ) =>
187+ state . matches ( { running : "awaiting_approval" } ) ,
188+ ) ;
195189
196190 ptyService . start ( ) ;
197191
@@ -205,9 +199,11 @@ describe("pty events", () => {
205199 } ,
206200 } ,
207201 } ) ;
202+
203+ await awaitingApproval ;
208204 } ) ;
209205
210- it ( "transitions when pty receives an override question" , ( done ) => {
206+ it ( "transitions when pty receives an override question" , async ( ) => {
211207 const mockDeployMachine = deployMachine . provide ( {
212208 actors : {
213209 runTerraformInPty : makeTerraformPtyService (
@@ -222,11 +218,9 @@ describe("pty events", () => {
222218 } ) ;
223219
224220 const ptyService = createActor ( mockDeployMachine ) ;
225- ptyService . subscribe ( ( state ) => {
226- if ( state . matches ( { running : "awaiting_sentinel_override" } ) ) {
227- done ( ) ;
228- }
229- } ) ;
221+ const awaitingOverride = waitFor ( ptyService , ( state ) =>
222+ state . matches ( { running : "awaiting_sentinel_override" } ) ,
223+ ) ;
230224
231225 ptyService . start ( ) ;
232226
@@ -240,9 +234,11 @@ describe("pty events", () => {
240234 } ,
241235 } ,
242236 } ) ;
237+
238+ await awaitingOverride ;
243239 } ) ;
244240
245- it ( "waits for terraform to exit on STOP rather than stopping immediately" , ( done ) => {
241+ it ( "waits for terraform to exit on STOP rather than stopping immediately" , async ( ) => {
246242 // The pty stays alive until we resolve its exit, letting us assert the machine waits for terraform's own exit
247243 // before reaching "stopped".
248244 const stop = jest . fn ( ) ;
@@ -260,22 +256,10 @@ describe("pty events", () => {
260256 } ,
261257 } ) ;
262258
263- let interrupted = false ;
264259 const ptyService = createActor ( mockDeployMachine ) ;
265- ptyService . subscribe ( ( state ) => {
266- if ( state . matches ( { running : "stopping" } ) && ! interrupted ) {
267- interrupted = true ;
268- // We are waiting for terraform to exit, not yet at the final "stopped" state, and we have not re-signalled it.
269- expect ( state . matches ( "stopped" ) ) . toBe ( false ) ;
270- expect ( stop ) . not . toHaveBeenCalled ( ) ;
271- setTimeout ( ( ) => resolveExit ( 0 ) , 50 ) ; // terraform finishes exiting, releasing its lock
272- }
273-
274- if ( state . matches ( "stopped" ) ) {
275- expect ( state . context . cancelled ) . toBe ( true ) ;
276- done ( ) ;
277- }
278- } ) ;
260+ const stopping = waitFor ( ptyService , ( state ) =>
261+ state . matches ( { running : "stopping" } ) ,
262+ ) ;
279263
280264 ptyService . start ( ) ;
281265
@@ -286,9 +270,17 @@ describe("pty events", () => {
286270
287271 // Give the pty service a tick to start before requesting a stop.
288272 setTimeout ( ( ) => ptyService . send ( { type : "STOP" } ) , 50 ) ;
273+
274+ // We are waiting for terraform to exit, not yet at the final "stopped" state, and we have not re-signalled it.
275+ expect ( ( await stopping ) . matches ( "stopped" ) ) . toBe ( false ) ;
276+ expect ( stop ) . not . toHaveBeenCalled ( ) ;
277+
278+ const stopped = waitFor ( ptyService , ( state ) => state . matches ( "stopped" ) ) ;
279+ setTimeout ( ( ) => resolveExit ( 0 ) , 50 ) ; // terraform finishes exiting, releasing its lock
280+ expect ( ( await stopped ) . context . cancelled ) . toBe ( true ) ;
289281 } ) ;
290282
291- it ( "transitions to rejected state when done externally" , ( done ) => {
283+ it ( "transitions to rejected state when done externally" , async ( ) => {
292284 const mockDeployMachine = deployMachine . provide ( {
293285 actors : {
294286 runTerraformInPty : makeTerraformPtyService (
@@ -304,7 +296,6 @@ describe("pty events", () => {
304296 } ) ;
305297
306298 // Record events seen by the machine via inspection.
307- let enteredAwaiting = false ;
308299 const states : string [ ] = [ ] ;
309300 const ptyService = createActor ( mockDeployMachine , {
310301 inspect : ( inspectionEvent ) => {
@@ -317,17 +308,9 @@ describe("pty events", () => {
317308 } ,
318309 } ) ;
319310
320- ptyService . subscribe ( ( state ) => {
321- if ( enteredAwaiting && state . matches ( "exited" ) ) {
322- expect ( states ) . toEqual (
323- expect . arrayContaining ( [ "OVERRIDE_REJECTED_EXTERNALLY" ] ) ,
324- ) ;
325- done ( ) ;
326- }
327- if ( state . matches ( { running : "awaiting_sentinel_override" } ) ) {
328- enteredAwaiting = true ;
329- }
330- } ) ;
311+ const awaitingOverride = waitFor ( ptyService , ( state ) =>
312+ state . matches ( { running : "awaiting_sentinel_override" } ) ,
313+ ) ;
331314
332315 ptyService . start ( ) ;
333316
@@ -341,6 +324,12 @@ describe("pty events", () => {
341324 } ,
342325 } ,
343326 } ) ;
327+
328+ await awaitingOverride ;
329+ await waitFor ( ptyService , ( state ) => state . matches ( "exited" ) ) ;
330+ expect ( states ) . toEqual (
331+ expect . arrayContaining ( [ "OVERRIDE_REJECTED_EXTERNALLY" ] ) ,
332+ ) ;
344333 } ) ;
345334} ) ;
346335
0 commit comments