11const test = require ( 'node:test' ) ;
22const assert = require ( 'assert' ) ;
3- const { Application, MailSystem } = require ( './main' ) ;
3+ const fs = require ( 'fs' ) ;
4+ const util = require ( 'util' ) ;
45
5- // TODO: write your tests here
6- // Remember to use Stub, Mock, and Spy when necessary
6+ // 1️⃣ Stub `fs.readFile` 為 Promise 版本
7+ fs . readFile = util . promisify ( ( path , encoding , callback ) => {
8+ callback ( null , 'Alice\nBob\nCharlie\nDavid' ) ; // 回傳假名單
9+ } ) ;
10+
11+ const { Application, MailSystem } = require ( './main' ) ; // 確保這是你的主程式
12+
13+ test ( 'Application getNames should return list of names from stubbed file' , async ( ) => {
14+ const app = new Application ( ) ;
15+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ; // 確保 `getNames` 完成
16+ assert . deepStrictEqual ( app . people , [ 'Alice' , 'Bob' , 'Charlie' , 'David' ] ) ;
17+ } ) ;
18+
19+ test ( 'MailSystem write should generate mail content' , ( ) => {
20+ const mailSystem = new MailSystem ( ) ;
21+ const name = 'Alice' ;
22+ const result = mailSystem . write ( name ) ;
23+ assert . strictEqual ( result , 'Congrats, Alice!' ) ;
24+ } ) ;
25+
26+ test ( 'MailSystem send should return both true and false' , ( ) => {
27+ const mailSystem = new MailSystem ( ) ;
28+ const name = 'Alice' ;
29+ const context = 'Congrats, Alice!' ;
30+
31+ let seenTrue = false ;
32+ let seenFalse = false ;
33+ let attempts = 0 ;
34+ const maxAttempts = 100 ; // 限制最大迴圈次數避免無窮迴圈
35+
36+ while ( ! ( seenTrue && seenFalse ) && attempts < maxAttempts ) {
37+ const result = mailSystem . send ( name , context ) ;
38+ if ( result ) {
39+ seenTrue = true ;
40+ } else {
41+ seenFalse = true ;
42+ }
43+ attempts ++ ;
44+ }
45+
46+ assert . strictEqual ( seenTrue , true , 'MailSystem.send() should return true at least once' ) ;
47+ assert . strictEqual ( seenFalse , true , 'MailSystem.send() should return false at least once' ) ;
48+ } ) ;
49+
50+
51+ test ( 'Application getRandomPerson should return a valid person' , async ( ) => {
52+ const app = new Application ( ) ;
53+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ; // 確保 getNames 完成
54+ const person = app . getRandomPerson ( ) ;
55+ assert . ok ( [ 'Alice' , 'Bob' , 'Charlie' , 'David' ] . includes ( person ) ) ;
56+ } ) ;
57+
58+ test ( 'Application selectNextPerson should return null when all are selected' , async ( ) => {
59+ const app = new Application ( ) ;
60+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
61+ app . people = [ 'Alice' , 'Bob' ] ;
62+ app . selected = [ 'Alice' , 'Bob' ] ;
63+ const result = app . selectNextPerson ( ) ;
64+ assert . strictEqual ( result , null ) ;
65+ } ) ;
66+
67+ test ( 'Application selectNextPerson should select a new person each time' , async ( ) => {
68+ const app = new Application ( ) ;
69+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
70+ const selected1 = app . selectNextPerson ( ) ;
71+ const selected2 = app . selectNextPerson ( ) ;
72+ assert . notStrictEqual ( selected1 , null ) ;
73+ assert . notStrictEqual ( selected2 , null ) ;
74+ assert . notStrictEqual ( selected1 , selected2 ) ;
75+ } ) ;
76+
77+ test ( 'Application selectNextPerson should avoid duplicate selection' , async ( ) => {
78+ const app = new Application ( ) ;
79+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
80+ app . people = [ 'Alice' , 'Bob' , 'Charlie' , 'David' ] ;
81+ app . selected = [ 'Alice' ] ;
82+ const selected = new Set ( app . selected ) ;
83+ for ( let i = 0 ; i < 4 ; i ++ ) {
84+ const newPerson = app . selectNextPerson ( ) ;
85+ assert . ok ( ! selected . has ( newPerson ) ) ;
86+ selected . add ( newPerson ) ;
87+ }
88+ } ) ;
89+
90+ test ( 'Application notifySelected should send emails to selected people' , async ( ) => {
91+ const app = new Application ( ) ;
92+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
93+ app . selectNextPerson ( ) ;
94+ app . selectNextPerson ( ) ;
95+
96+ // Spy: 監視方法呼叫次數
97+ let writeCallCount = 0 ;
98+ let sendCallCount = 0 ;
99+
100+ const originalWrite = app . mailSystem . write ;
101+ const originalSend = app . mailSystem . send ;
102+
103+ // Mock: 取代方法回傳預期值
104+ app . mailSystem . write = ( ) => {
105+ writeCallCount ++ ;
106+ return 'Mock Content' ;
107+ } ;
108+
109+ app . mailSystem . send = ( ) => {
110+ sendCallCount ++ ;
111+ return true ;
112+ } ;
113+
114+ app . notifySelected ( ) ;
115+
116+ assert . strictEqual ( writeCallCount , app . selected . length ) ;
117+ assert . strictEqual ( sendCallCount , app . selected . length ) ;
118+
119+ // 還原原始方法
120+ app . mailSystem . write = originalWrite ;
121+ app . mailSystem . send = originalSend ;
122+ } ) ;
0 commit comments