11const test = require ( 'node:test' ) ;
22const assert = require ( 'assert' ) ;
3- const fs = require ( 'fs' ) ;
43const { Application, MailSystem } = require ( './main' ) ;
54
65// TODO: write your tests here
7- < << << << HEAD
8- // Remember to use Stub, Mock, and Spy when necessary
9- test ( 'Test getNames' , async ( t ) => {
10- const mockContent = 'John\nJane\nJim' ;
11- const tempFilePath = 'name_list.txt' ;
12-
13- // create mock file
14- fs . writeFileSync ( tempFilePath , mockContent , 'utf-8' ) ;
15-
16- // create instance
17- const app = new Application ( ) ;
18- const names = await app . getNames ( ) ;
19- app . people = mockContent . split ( '\n' ) ;
20-
21- assert . deepStrictEqual ( names [ 0 ] , [ 'John' , 'Jane' , 'Jim' ] ) ;
22- assert . deepStrictEqual ( names [ 1 ] , [ ] ) ;
23-
24- if ( fs . existsSync ( tempFilePath ) ) {
25- fs . unlinkSync ( tempFilePath ) ;
26- }
27- } ) ;
28- test ( 'Test getNames with empty file' , async ( t ) => {
29- const tempFilePath = 'name_list.txt' ;
30-
31- // 創建空文件
32- fs . writeFileSync ( tempFilePath , "" , 'utf-8' ) ;
33-
34- const app = new Application ( ) ;
35- const names = await app . getNames ( ) ;
36-
37- assert . deepStrictEqual ( names [ 0 ] , [ '' ] ) ;
38- assert . deepStrictEqual ( names [ 1 ] , [ ] ) ;
39-
40- if ( fs . existsSync ( tempFilePath ) ) {
41- fs . unlinkSync ( tempFilePath ) ;
42- }
43- } ) ;
44- test ( 'Test getNames with file read error' , async ( t ) => {
45- const tempFilePath = 'name_list.txt' ;
46- // create mock file
47- fs . writeFileSync ( tempFilePath , '' , 'utf-8' ) ;
48-
49- // 禁用實際的 constructor 調用
50- const originalConstructor = Application . prototype . constructor ;
51-
52- try {
53- const app = new Application ( ) ;
54- // await app.getNames();
55- assert . fail ( "ENOENT: no such file or directory, open 'name_list.txt'" ) ;
56- } catch ( error ) {
57- assert . strictEqual ( error . message , "ENOENT: no such file or directory, open 'name_list.txt'" ) ;
58- } finally {
59- // 恢復原始的 constructor
60- Application . prototype . constructor = originalConstructor ;
61- }
62- if ( fs . existsSync ( tempFilePath ) ) {
63- fs . unlinkSync ( tempFilePath ) ;
64- }
65- } ) ;
66- test ( 'Test getRandomPerson' , async ( t ) => {
67- const mockContent = 'John\nJane\nJim' ;
68- const tempFilePath = 'name_list.txt' ;
69-
70- // create mock file
71- fs . writeFileSync ( tempFilePath , mockContent , 'utf-8' ) ;
72-
73- // create instance
74- const app = new Application ( ) ;
75- app . people = mockContent . split ( '\n' ) ;
76- const person = app . getRandomPerson ( ) ;
77- // console.log('person = ', person);
78- assert ( [ 'John' , 'Jane' , 'Jim' ] . includes ( person ) ) ;
79-
80- if ( fs . existsSync ( tempFilePath ) ) {
81- fs . unlinkSync ( tempFilePath ) ;
82- }
83- } ) ;
84-
85- test ( 'Test getRandomPerson with empty people array' , async ( t ) => {
86- const tempFilePath = 'name_list.txt' ;
87-
88- // 創建臨時文件
89- fs . writeFileSync ( tempFilePath , '' , 'utf-8' ) ;
90-
91- try {
92- // 創建實例
93- const app = new Application ( ) ;
94-
95- // 設置空的 people 陣列
96- app . people = [ ] ;
97-
98- // 測試 getRandomPerson
99- const person = app . getRandomPerson ( ) ;
100-
101- // 驗證結果
102- // 在空陣列的情況下,getRandomPerson 可能返回 undefined
103- // 或者拋出異常(這取決於代碼的實現)
104- assert . strictEqual ( person , undefined ) ;
105-
106- } finally {
107- // 無論測試成功或失敗,都確保刪除臨時文件
108- if ( fs . existsSync ( tempFilePath ) ) {
109- fs . unlinkSync ( tempFilePath ) ;
110- }
111- }
112- } ) ;
113- test ( 'Test selectNextPerson' , async ( t ) => {
114- const mockContent = 'John\nJane\nJim' ;
115- const tempFilePath = 'name_list.txt' ;
116- const mockContent2 = '' ;
117- // create mock file
118- fs . writeFileSync ( tempFilePath , mockContent , 'utf-8' ) ;
119-
120- // 創建實例
121- const app = new Application ( ) ;
122- await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ;
123-
124- app . people = mockContent . split ( '\n' ) ;
125- app . selected = [ ] ;
126- const person = app . selectNextPerson ( ) ;
127-
128- // 驗證結果
129- assert ( [ 'John' , 'Jane' , 'Jim' ] . includes ( person ) ) ;
130- assert . strictEqual ( app . selected . length , 1 ) ;
131- assert ( [ 'John' , 'Jane' , 'Jim' ] . includes ( app . selected [ 0 ] ) ) ;
132-
133- // 設置所有人都已被選擇
134- app . people = mockContent2 . split ( '\n' ) ;
135- app . selected = [ ...app . people ] ; // 所有人都已選擇
136-
137- // 測試選擇下一個人應該返回 null
138- const person2 = app . selectNextPerson ( ) ;
139-
140- // 驗證結果
141- assert . strictEqual ( person2 , null ) ;
142-
143- if ( fs . existsSync ( tempFilePath ) ) {
144- fs . unlinkSync ( tempFilePath ) ;
145- }
146- } ) ;
147- test ( 'Test selectNextPerson with already selected person' , async ( t ) => {
148- const tempFilePath = 'name_list.txt' ;
149-
150- // 創建臨時文件
151- fs . writeFileSync ( tempFilePath , 'John\nJane\nJim' , 'utf-8' ) ;
152-
153- try {
154- // 創建實例
155- const app = new Application ( ) ;
156- await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ; // 等待初始化完成
157-
158- // 設置特定的 people 陣列和 selected 陣列
159- app . people = [ 'John' , 'Jane' , 'Jim' ] ;
160- app . selected = [ 'John' , 'Jane' ] ; // 已經選擇了 John 和 Jane
161-
162- // 強制 getRandomPerson 返回已選擇的人,然後返回未選擇的人
163- let callCount = 0 ;
164- const originalGetRandomPerson = app . getRandomPerson ;
165- app . getRandomPerson = function ( ) {
166- callCount ++ ;
167- if ( callCount === 1 ) {
168- return 'John' ; // 第一次返回已選擇的人
169- } else {
170- return 'Jim' ; // 第二次返回未選擇的人
171- }
172- } ;
173-
174- // 測試 selectNextPerson
175- const person = app . selectNextPerson ( ) ;
176-
177- // 恢復原始 getRandomPerson 方法
178- app . getRandomPerson = originalGetRandomPerson ;
179-
180- // 驗證結果
181- assert . strictEqual ( person , 'Jim' ) ;
182- assert . strictEqual ( callCount , 2 ) ; // 確保 getRandomPerson 被調用了兩次
183- assert . strictEqual ( app . selected . length , 3 ) ;
184- assert ( app . selected . includes ( 'Jim' ) ) ;
185-
186- } finally {
187- // 無論測試成功或失敗,都確保刪除臨時文件
188- if ( fs . existsSync ( tempFilePath ) ) {
189- fs . unlinkSync ( tempFilePath ) ;
190- }
191- }
192- } ) ;
193- test ( 'Test notifySelected' , async ( t ) => {
194- const mockContent = 'John\nJane\nJim' ;
195- const tempFilePath = 'name_list.txt' ;
196-
197- // 創建臨時文件
198- fs . writeFileSync ( tempFilePath , mockContent , 'utf8' ) ;
199-
200- try {
201- // 使用原始的構造函數
202- const app = new Application ( ) ;
203-
204- // 等待初始化完成
205- await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ;
206-
207- // 測試前準備
208- app . selected = [ 'John' ] ;
209-
210- // 創建 spy 來追踪 MailSystem 的方法調用
211- let writeCallCount = 0 ;
212- let sendCallCount = 0 ;
213-
214- await t . mock . method ( app . mailSystem , 'write' , ( name ) => {
215- writeCallCount ++ ;
216- assert . strictEqual ( name , 'John' ) ;
217- return 'Mocked content' ;
218- } ) ;
219-
220- await t . mock . method ( app . mailSystem , 'send' , ( name , content ) => {
221- sendCallCount ++ ;
222- assert . strictEqual ( name , 'John' ) ;
223- assert . strictEqual ( content , 'Mocked content' ) ;
224- return true ;
225- } ) ;
226-
227- // test notifySelected
228- app . notifySelected ( ) ;
229-
230- // verify result
231- assert . strictEqual ( writeCallCount , 1 ) ;
232- assert . strictEqual ( sendCallCount , 1 ) ;
233- } finally {
234- // ensure delete temp file
235- if ( fs . existsSync ( tempFilePath ) ) {
236- fs . unlinkSync ( tempFilePath ) ;
237- }
238- }
239- } ) ;
240-
241- test ( 'Test notifySelected with multiple people' , async ( t ) => {
242- const mockContent = 'John\nJane\nJim' ;
243- const tempFilePath = 'name_list.txt' ;
244-
245- // 創建臨時文件
246- fs . writeFileSync ( tempFilePath , mockContent , 'utf8' ) ;
247-
248- try {
249- // 使用原始的構造函數
250- const app = new Application ( ) ;
251-
252- // 等待初始化完成
253- await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ;
254-
255- // 測試前準備 - 設置多個人被選中
256- app . selected = [ 'John' , 'Jane' , 'Jim' ] ;
257-
258- // 跟踪每個人的通知情況
259- const notifiedPeople = [ ] ;
260- const writtenContents = { } ;
261-
262- // 創建 spy 來追踪 MailSystem 的方法調用
263- await t . mock . method ( app . mailSystem , 'write' , ( name ) => {
264- notifiedPeople . push ( name ) ;
265- const content = `Mocked content for ${ name } ` ;
266- writtenContents [ name ] = content ;
267- return content ;
268- } ) ;
269-
270- await t . mock . method ( app . mailSystem , 'send' , ( name , content ) => {
271- assert . strictEqual ( content , writtenContents [ name ] ) ;
272- return true ;
273- } ) ;
274-
275- // 測試 notifySelected
276- app . notifySelected ( ) ;
277-
278- // 驗證結果 - 確保所有人都被通知到
279- assert . strictEqual ( notifiedPeople . length , 3 ) ;
280- assert ( notifiedPeople . includes ( 'John' ) ) ;
281- assert ( notifiedPeople . includes ( 'Jane' ) ) ;
282- assert ( notifiedPeople . includes ( 'Jim' ) ) ;
283- } finally {
284- // ensure delete temp file
285- if ( fs . existsSync ( tempFilePath ) ) {
286- fs . unlinkSync ( tempFilePath ) ;
287- }
288- }
289- } ) ;
290-
291- // Test MailSystem
292- test ( 'Test MailSystem write' , async ( t ) => {
293- const mailSystem = new MailSystem ( ) ;
294- const name = 'John' ;
295- const result = mailSystem . write ( name ) ;
296- assert . strictEqual ( result , 'Congrats, John!' ) ;
297- } ) ;
298- test ( 'Test MailSystem send success path' , async ( t ) => {
299- const mailSystem = new MailSystem ( ) ;
300-
301- // mock Math.random method
302- const originalRandom = Math . random ;
303- Math . random = ( ) => 1 ; // 強制返回 1,確保大於 0.5
304-
305- try {
306- const result = mailSystem . send ( 'John' , 'Congrats, John!' ) ;
307- assert . strictEqual ( result , true ) ;
308- } finally {
309- // restore original method
310- Math . random = originalRandom ;
311- }
312- } ) ;
313-
314- test ( 'Test MailSystem send failure path' , async ( t ) => {
315- const mailSystem = new MailSystem ( ) ;
316-
317- // mock Math.random method
318- const originalRandom = Math . random ;
319- Math . random = ( ) => 0 ; // 強制返回 0,確保小於 0.5
320-
321- try {
322- const result = mailSystem . send ( 'John' , 'Congrats, John!' ) ;
323- assert . strictEqual ( result , false ) ;
324- } finally {
325- // 恢復原始方法
326- Math . random = originalRandom ;
327- }
328- } ) ;
6+ // Remember to use Stub, Mock, and Spy when necessary
0 commit comments