Skip to content

Commit dc76ca7

Browse files
authored
Merge pull request #151 from MinKuanIsHere/313552009
[LAB2] 313552009
2 parents 6fa9a3b + 1ddba7b commit dc76ca7

1 file changed

Lines changed: 155 additions & 5 deletions

File tree

lab2/main_test.js

Lines changed: 155 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,156 @@
1-
const test = require('node:test');
2-
const assert = require('assert');
3-
const { Application, MailSystem } = require('./main');
1+
import fs from 'fs';
2+
import path from 'path';
3+
import os from 'os';
4+
import test from 'node:test';
5+
import assert from 'assert';
6+
import { Application, MailSystem } from './main.js';
47

5-
// TODO: write your tests here
6-
// Remember to use Stub, Mock, and Spy when necessary
8+
// 輔助函式:在暫存目錄中建立 name_list.txt,並切換工作目錄
9+
async function withTempNameList(fn) {
10+
const originalCwd = process.cwd();
11+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lab2-'));
12+
fs.writeFileSync(path.join(tmpDir, 'name_list.txt'), 'Alice\nBob\nCharlie');
13+
process.chdir(tmpDir);
14+
try {
15+
await fn();
16+
} finally {
17+
process.chdir(originalCwd);
18+
fs.rmSync(tmpDir, { recursive: true, force: true });
19+
}
20+
}
21+
22+
// 建立 Stub 輔助函式
23+
const createStub = (obj, method, implementation) => {
24+
const original = obj[method];
25+
obj[method] = implementation;
26+
return { restore: () => { obj[method] = original; } };
27+
};
28+
29+
// 測試 MailSystem.write()(不需要檔案)
30+
test('MailSystem.write should generate correct mail content', () => {
31+
const mailSystem = new MailSystem();
32+
const result = mailSystem.write('Alice');
33+
assert.strictEqual(result, 'Congrats, Alice!');
34+
});
35+
36+
// 測試 MailSystem.send():透過 stub 控制隨機行為
37+
test('MailSystem.send should return success or failure', () => {
38+
const mailSystem = new MailSystem();
39+
const randomStub = createStub(Math, 'random', () => 0.9);
40+
assert.strictEqual(mailSystem.send('Alice', 'Congrats, Alice!'), true);
41+
randomStub.restore();
42+
const randomStub2 = createStub(Math, 'random', () => 0.4);
43+
assert.strictEqual(mailSystem.send('Alice', 'Congrats, Alice!'), false);
44+
randomStub2.restore();
45+
});
46+
47+
// 以下測試皆需要 name_list.txt,所以採用 withTempNameList 包裹
48+
49+
test('Application.getNames should read and parse names from file', async () => {
50+
await withTempNameList(async () => {
51+
const app = new Application();
52+
// 等待 constructor 的非同步初始化完成
53+
await new Promise(resolve => setTimeout(resolve, 10));
54+
const [people, selected] = await app.getNames();
55+
assert.deepStrictEqual(people, ['Alice', 'Bob', 'Charlie']);
56+
assert.deepStrictEqual(selected, []);
57+
});
58+
});
59+
60+
test('Application constructor should initialize people and selected', async () => {
61+
await withTempNameList(async () => {
62+
const app = new Application();
63+
await new Promise(resolve => setTimeout(resolve, 10));
64+
assert.deepStrictEqual(app.people, ['Alice', 'Bob', 'Charlie']);
65+
assert.deepStrictEqual(app.selected, []);
66+
});
67+
});
68+
69+
test('Application.getRandomPerson should return a person from people list', async () => {
70+
await withTempNameList(async () => {
71+
const app = new Application();
72+
await new Promise(resolve => setTimeout(resolve, 10));
73+
// stub Math.random 固定回傳 0.5 (floor(0.5 * 3) = 1) → 'Bob'
74+
const randomStub = createStub(Math, 'random', () => 0.5);
75+
assert.strictEqual(app.getRandomPerson(), 'Bob');
76+
randomStub.restore();
77+
});
78+
});
79+
80+
test('Application.selectNextPerson should select a unique person in order', async () => {
81+
await withTempNameList(async () => {
82+
const app = new Application();
83+
await new Promise(resolve => setTimeout(resolve, 10));
84+
// stub getRandomPerson 依序回傳:'Alice', 'Bob', 'Charlie'
85+
let sequence = ['Alice', 'Bob', 'Charlie'];
86+
let index = 0;
87+
const stub = createStub(app, 'getRandomPerson', () => sequence[index++]);
88+
89+
const selected1 = app.selectNextPerson();
90+
assert.strictEqual(selected1, 'Alice');
91+
assert.strictEqual(app.selected.length, 1);
92+
93+
const selected2 = app.selectNextPerson();
94+
assert.strictEqual(selected2, 'Bob');
95+
assert.strictEqual(app.selected.length, 2);
96+
97+
const selected3 = app.selectNextPerson();
98+
assert.strictEqual(selected3, 'Charlie');
99+
assert.strictEqual(app.selected.length, 3);
100+
101+
const selected4 = app.selectNextPerson();
102+
assert.strictEqual(selected4, null);
103+
104+
stub.restore();
105+
});
106+
});
107+
108+
test('Application.selectNextPerson should retry if duplicate selected', async () => {
109+
await withTempNameList(async () => {
110+
const app = new Application();
111+
await new Promise(resolve => setTimeout(resolve, 10));
112+
// stub 模擬第一次回傳 'Alice'(已被選取),接著回傳 'Bob'
113+
let callCount = 0;
114+
const stub = createStub(app, 'getRandomPerson', () => {
115+
if (callCount === 0) {
116+
callCount++;
117+
return 'Alice';
118+
}
119+
return 'Bob';
120+
});
121+
// 預先將 'Alice' 加入已選名單
122+
app.selected = ['Alice'];
123+
const result = app.selectNextPerson();
124+
assert.strictEqual(result, 'Bob');
125+
stub.restore();
126+
});
127+
});
128+
129+
test('Application.notifySelected should send mail to selected people', async () => {
130+
await withTempNameList(async () => {
131+
const app = new Application();
132+
await new Promise(resolve => setTimeout(resolve, 10));
133+
app.selected = ['Alice', 'Bob'];
134+
let writeCalls = [];
135+
let sendCalls = [];
136+
const writeStub = createStub(app.mailSystem, 'write', (name) => {
137+
writeCalls.push(name);
138+
return `Congrats, ${name}!`;
139+
});
140+
const sendStub = createStub(app.mailSystem, 'send', (name, content) => {
141+
sendCalls.push({ name, content });
142+
return true;
143+
});
144+
145+
app.notifySelected();
146+
147+
assert.deepStrictEqual(writeCalls, ['Alice', 'Bob']);
148+
assert.deepStrictEqual(sendCalls, [
149+
{ name: 'Alice', content: 'Congrats, Alice!' },
150+
{ name: 'Bob', content: 'Congrats, Bob!' }
151+
]);
152+
153+
writeStub.restore();
154+
sendStub.restore();
155+
});
156+
});

0 commit comments

Comments
 (0)