Skip to content

Commit dfe6b1f

Browse files
committed
feat: lab3
1 parent fc4b5a3 commit dfe6b1f

5 files changed

Lines changed: 109 additions & 119 deletions

File tree

lab2/main_test.js

Lines changed: 3 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,6 @@
11
const test = require('node:test');
22
const assert = require('assert');
3-
const fs = require('fs');
4-
const util = require('util');
3+
const { Application, MailSystem } = require('./main');
54

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-
});
5+
// TODO: write your tests here
6+
// Remember to use Stub, Mock, and Spy when necessary

lab3/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Lab3
2+
3+
## Introduction
4+
5+
In this lab, you will write unit tests for functions implemented in `main.js`. You can learn how to use classes and functions in it by uncommenting the code in it. (But remember don't commit them on GitHub)
6+
7+
## Preparation (Important!!!)
8+
9+
1. Sync fork on GitHub
10+
2. `git checkout -b lab3` (**NOT** your student ID !!!)
11+
12+
## Requirement
13+
14+
1. (40%) Write test cases in `main_test.js` and achieve 100% code coverage.
15+
2. (30%) For each function, parameterize their testcases to test the error-results.
16+
3. (30%) For each function, use at least 3 parameterized testcases to test the non-error-results.
17+
18+
You can run `validate.sh` in your local to test if you satisfy the requirements.
19+
20+
Please note that you must not alter files other than `main_test.js`. You will get 0 points if
21+
22+
1. you modify other files to achieve requirements.
23+
2. you can't pass all CI on your PR.
24+
25+
## Submission
26+
27+
You need to open a pull request to your branch (e.g. 312XXXXXX, your student number) and contain the code that satisfies the abovementioned requirements.
28+
29+
Moreover, please submit the URL of your PR to E3. Your submission will only be accepted when you present at both places.

lab3/main.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class Calculator {
2+
exp(x) {
3+
if (!Number.isFinite(x)) {
4+
throw Error('unsupported operand type');
5+
}
6+
const result = Math.exp(x);
7+
if (result === Infinity) {
8+
throw Error('overflow');
9+
}
10+
return result;
11+
}
12+
13+
log(x) {
14+
if (!Number.isFinite(x)) {
15+
throw Error('unsupported operand type');
16+
}
17+
const result = Math.log(x);
18+
if (result === -Infinity) {
19+
throw Error('math domain error (1)');
20+
}
21+
if (Number.isNaN(result)) {
22+
throw Error('math domain error (2)');
23+
}
24+
return result;
25+
}
26+
}
27+
28+
// const calculator = new Calculator();
29+
// console.log(calculator.exp(87));
30+
// console.log(calculator.log(48763));
31+
32+
module.exports = {
33+
Calculator
34+
};

lab3/main_test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const {describe, it} = require('node:test');
2+
const assert = require('assert');
3+
const { Calculator } = require('./main');
4+
5+
// TODO: write your tests here

lab3/validate.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
3+
# Check for unwanted files
4+
for file in *; do
5+
if [[ $file != "main.js" && $file != "main_test.js" && $file != "README.md" && $file != "validate.sh" ]]; then
6+
echo "[!] Unwanted file detected: $file."
7+
exit 1
8+
fi
9+
done
10+
11+
node=$(which node)
12+
test_path="${BASH_SOURCE[0]}"
13+
solution_path="$(realpath .)"
14+
tmp_dir=$(mktemp -d -t lab3-XXXXXXXXXX)
15+
16+
cd $tmp_dir
17+
18+
rm -rf *
19+
cp $solution_path/*.js .
20+
result=$($"node" --test --experimental-test-coverage) ; ret=$?
21+
if [ $ret -ne 0 ] ; then
22+
echo "[!] testing fails"
23+
exit 1
24+
else
25+
coverage=$(echo "$result" | grep 'all files' | awk -F '|' '{print $2}' | sed 's/ //g')
26+
if (( $(echo "$coverage < 100" | bc -l) )); then
27+
echo "[!] Coverage is only $coverage%"
28+
exit 1
29+
else
30+
echo "[V] Coverage is 100%"
31+
fi
32+
fi
33+
34+
rm -rf $tmp_dir
35+
36+
exit 0
37+
38+
# vim: set fenc=utf8 ff=unix et sw=2 ts=2 sts=2:

0 commit comments

Comments
 (0)