1+ /*---------------------------------------------------------------------------------------------
2+ * Copyright (c) Microsoft Corporation. All rights reserved.
3+ * Licensed under the MIT License. See License.txt in the project root for license information.
4+ *--------------------------------------------------------------------------------------------*/
5+
6+ import { TaskState } from '../taskService' ;
7+ import { DummyTask } from './DummyTask' ;
8+
9+ describe ( 'DummyTask' , ( ) => {
10+ let task : DummyTask ;
11+
12+ beforeEach ( ( ) => {
13+ task = new DummyTask ( 'Test Dummy Task' ) ;
14+ } ) ;
15+
16+ afterEach ( async ( ) => {
17+ await task . delete ( ) ;
18+ } ) ;
19+
20+ describe ( 'constructor' , ( ) => {
21+ it ( 'should initialize with correct properties' , ( ) => {
22+ const status = task . getStatus ( ) ;
23+
24+ expect ( task . id ) . toMatch ( / ^ d u m m y - t a s k - \d + $ / ) ;
25+ expect ( task . type ) . toBe ( 'dummy-task' ) ;
26+ expect ( task . name ) . toBe ( 'Test Dummy Task' ) ;
27+ expect ( status . state ) . toBe ( TaskState . Pending ) ;
28+ expect ( status . progress ) . toBe ( 0 ) ;
29+ expect ( status . message ) . toBe ( 'Task created and ready to start' ) ;
30+ } ) ;
31+
32+ it ( 'should generate unique IDs for multiple instances' , ( ) => {
33+ const task1 = new DummyTask ( ) ;
34+ const task2 = new DummyTask ( ) ;
35+
36+ expect ( task1 . id ) . not . toBe ( task2 . id ) ;
37+
38+ // Cleanup
39+ void task1 . delete ( ) ;
40+ void task2 . delete ( ) ;
41+ } ) ;
42+
43+ it ( 'should use default name when none provided' , ( ) => {
44+ const defaultTask = new DummyTask ( ) ;
45+ expect ( defaultTask . name ) . toMatch ( / ^ D u m m y T a s k \d + $ / ) ;
46+
47+ void defaultTask . delete ( ) ;
48+ } ) ;
49+ } ) ;
50+
51+ describe ( 'start' , ( ) => {
52+ it ( 'should transition through initialization states' , async ( ) => {
53+ const startPromise = task . start ( ) ;
54+
55+ // Should be initializing briefly
56+ await new Promise ( resolve => setTimeout ( resolve , 50 ) ) ;
57+ let status = task . getStatus ( ) ;
58+ expect ( status . state ) . toBe ( TaskState . Initializing ) ;
59+ expect ( status . message ) . toBe ( 'Initializing task...' ) ;
60+
61+ await startPromise ;
62+
63+ // Should now be running
64+ status = task . getStatus ( ) ;
65+ expect ( status . state ) . toBe ( TaskState . Running ) ;
66+ expect ( status . message ) . toBe ( 'Task execution started' ) ;
67+ } ) ;
68+
69+ it ( 'should not allow starting twice' , async ( ) => {
70+ await task . start ( ) ;
71+
72+ await expect ( task . start ( ) ) . rejects . toThrow ( 'Cannot start task in state: running' ) ;
73+ } ) ;
74+
75+ it ( 'should update progress over time' , async ( ) => {
76+ await task . start ( ) ;
77+
78+ // Wait for at least one progress update
79+ await new Promise ( resolve => setTimeout ( resolve , 1100 ) ) ;
80+
81+ const status = task . getStatus ( ) ;
82+ expect ( status . state ) . toBe ( TaskState . Running ) ;
83+ expect ( status . progress ) . toBeGreaterThan ( 0 ) ;
84+ expect ( status . progress ) . toBeLessThanOrEqual ( 100 ) ;
85+ expect ( status . message ) . toContain ( 'Processing...' ) ;
86+ expect ( status . message ) . toContain ( '% complete' ) ;
87+ } ) ;
88+
89+ it ( 'should complete after approximately 10 seconds' , async ( ) => {
90+ await task . start ( ) ;
91+
92+ // Wait for completion (with some buffer for timing)
93+ await new Promise ( resolve => setTimeout ( resolve , 11000 ) ) ;
94+
95+ const status = task . getStatus ( ) ;
96+ expect ( status . state ) . toBe ( TaskState . Completed ) ;
97+ expect ( status . progress ) . toBe ( 100 ) ;
98+ expect ( status . message ) . toBe ( 'Task completed successfully' ) ;
99+ } , 15000 ) ; // Increase Jest timeout for this test
100+ } ) ;
101+
102+ describe ( 'stop' , ( ) => {
103+ it ( 'should stop a running task' , async ( ) => {
104+ await task . start ( ) ;
105+
106+ // Let it run for a bit
107+ await new Promise ( resolve => setTimeout ( resolve , 1100 ) ) ;
108+
109+ await task . stop ( ) ;
110+
111+ const status = task . getStatus ( ) ;
112+ expect ( status . state ) . toBe ( TaskState . Stopped ) ;
113+ expect ( status . message ) . toBe ( 'Task was stopped' ) ;
114+ } ) ;
115+
116+ it ( 'should handle stopping before start' , async ( ) => {
117+ await task . stop ( ) ;
118+
119+ const status = task . getStatus ( ) ;
120+ expect ( status . state ) . toBe ( TaskState . Stopped ) ;
121+ } ) ;
122+
123+ it ( 'should handle multiple stop calls' , async ( ) => {
124+ await task . start ( ) ;
125+ await task . stop ( ) ;
126+
127+ // Second stop should not throw
128+ await expect ( task . stop ( ) ) . resolves . toBeUndefined ( ) ;
129+
130+ const status = task . getStatus ( ) ;
131+ expect ( status . state ) . toBe ( TaskState . Stopped ) ;
132+ } ) ;
133+
134+ it ( 'should preserve progress when stopped' , async ( ) => {
135+ await task . start ( ) ;
136+
137+ // Wait for some progress
138+ await new Promise ( resolve => setTimeout ( resolve , 2100 ) ) ;
139+
140+ const progressBeforeStop = task . getStatus ( ) . progress ;
141+ await task . stop ( ) ;
142+
143+ const status = task . getStatus ( ) ;
144+ expect ( status . progress ) . toBe ( progressBeforeStop ) ;
145+ } ) ;
146+ } ) ;
147+
148+ describe ( 'delete' , ( ) => {
149+ it ( 'should stop and cleanup the task' , async ( ) => {
150+ await task . start ( ) ;
151+
152+ // Let it run briefly
153+ await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
154+
155+ await task . delete ( ) ;
156+
157+ const status = task . getStatus ( ) ;
158+ expect ( status . state ) . toBe ( TaskState . Stopped ) ;
159+ } ) ;
160+
161+ it ( 'should handle delete on pending task' , async ( ) => {
162+ await expect ( task . delete ( ) ) . resolves . toBeUndefined ( ) ;
163+
164+ const status = task . getStatus ( ) ;
165+ expect ( status . state ) . toBe ( TaskState . Stopped ) ;
166+ } ) ;
167+ } ) ;
168+
169+ describe ( 'abort signal handling' , ( ) => {
170+ it ( 'should handle abort during initialization' , async ( ) => {
171+ const startPromise = task . start ( ) ;
172+
173+ // Give it a tiny bit of time to enter initialization, then stop
174+ await new Promise ( resolve => setTimeout ( resolve , 10 ) ) ;
175+ await task . stop ( ) ;
176+ await startPromise ;
177+
178+ const status = task . getStatus ( ) ;
179+ // The task might be in running state if start() completed before stop()
180+ // or stopped if stop() was processed first
181+ expect ( [ TaskState . Stopped , TaskState . Running ] . includes ( status . state ) ) . toBe ( true ) ;
182+ } ) ;
183+
184+ it ( 'should handle abort during execution' , async ( ) => {
185+ await task . start ( ) ;
186+
187+ // Let it start running
188+ await new Promise ( resolve => setTimeout ( resolve , 1100 ) ) ;
189+
190+ await task . stop ( ) ;
191+
192+ const status = task . getStatus ( ) ;
193+ expect ( status . state ) . toBe ( TaskState . Stopped ) ;
194+ } ) ;
195+ } ) ;
196+
197+ describe ( 'getStatus' , ( ) => {
198+ it ( 'should return a copy of status to prevent mutation' , ( ) => {
199+ const status1 = task . getStatus ( ) ;
200+ const status2 = task . getStatus ( ) ;
201+
202+ expect ( status1 ) . toEqual ( status2 ) ;
203+ expect ( status1 ) . not . toBe ( status2 ) ; // Different object references
204+ } ) ;
205+ } ) ;
206+ } ) ;
0 commit comments