Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LAB2] m091545 #146

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions .github/workflows/PR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,31 @@ jobs:
const { owner, repo, number: issue_number } = context.issue;
const pr = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
const title = pr.data.title;
const labRegex = /\[LAB(\d+)\]/;
const titleRegex = /^\[LAB\d+\] [\da-zA-Z]+$/;

if (!titleRegex.test(title)) {
core.setFailed('PR title does not match the required format. Please use the format [LAB#] student#.');
const titleRegex = /^\[LAB(\d+)\] [a-zA-Z]?\d+$/;
const match = title.match(titleRegex);

let labNumberStr = undefined;
if (match) {
labNumberStr = match[1];
} else {
core.setFailed('PR title does not match the required format. Please use the format: [LAB#] <studentId>.');
}

if (pr.data.head.ref !== pr.data.base.ref) {
core.setFailed('The source branch and target branch must be the same.');
const labelToAdd = `lab${labNumberStr}`;
if (labNumberStr) {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [labelToAdd] });
}

if (pr.data.base.ref === 'main') {
core.setFailed('The target branch cannot be main.');
}

const match = title.match(labRegex);
if (match) {
const labelToAdd = 'lab' + match[1];
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [labelToAdd] });
} else {
core.setFailed('No match found in PR title. Please add a label in the format [LAB#] to the PR title.');
if (labNumberStr < 3 && pr.data.head.ref !== pr.data.base.ref) {
core.setFailed('The source branch and target branch must be the same.');
}
if (labNumberStr >= 3 && pr.data.head.ref !== labelToAdd) {
core.setFailed(`The source branch must be '${labelToAdd}'`);
}
checklist-check:
runs-on: ubuntu-latest
Expand All @@ -49,12 +53,12 @@ jobs:
const pr = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
const body = pr.data.body;

const checkboxes = body.match(/\- \[[x ]\]/g);
const checkboxes = body.match(/^ ?(-|\*) \[[x ]\]/gmi);
if (!checkboxes || checkboxes.length !== 5) {
core.setFailed('The PR description must contain exactly 5 checkboxes.');
}

const unchecked = body.match(/\- \[ \]/g);
const unchecked = body.match(/^ ?(-|\*) \[ \]/gm);
if (unchecked && unchecked.length > 0) {
core.setFailed(`There are ${unchecked.length} unchecked items in the PR description.`);
core.setFailed(`There are ${unchecked.length} unchecked item(s) in the PR description.`);
}
56 changes: 56 additions & 0 deletions .github/workflows/lab-autograding.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Autograding

on:
pull_request_target:
types: [labeled, synchronize, opened, reopened, ready_for_review]

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04]
fail-fast: false
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- uses: actions/setup-node@v4
with:
node-version: latest
- name: Extract lab number and Check no changes other than specific files
uses: actions/github-script@v5
id: lab
with:
result-encoding: string
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo, number: issue_number } = context.issue;
const pr = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
const labels = pr.data.labels;
const lab = labels.find((label) => label.name.startsWith('lab'));
if (!lab) {
core.setFailed('No lab label found on the PR.');
return { number: 0 };
}
const labNumberMatch = lab.name.match(/lab(\d+)/);
if (!labNumberMatch) {
core.setFailed('Invalid lab label found on the PR.');
return { number: 0 };
}
const labNumber = labNumberMatch[1];
console.log(`Lab number: ${labNumber}`)

const files = await github.rest.pulls.listFiles({ owner, repo, pull_number: issue_number });
const changedFiles = files.data.map((file) => file.filename);
const allowedFiles = [
`lab${labNumber}/main_test.js`,
];
if (!changedFiles.every((file) => allowedFiles.includes(file))) {
core.setFailed('The PR contains changes to files other than the allowed files.');
}
return labNumber;
- name: Grading
run: |
cd lab${{ steps.lab.outputs.result }}
./validate.sh
26 changes: 0 additions & 26 deletions .github/workflows/lab1.yml

This file was deleted.

26 changes: 0 additions & 26 deletions .github/workflows/lab2.yml

This file was deleted.

123 changes: 121 additions & 2 deletions lab2/main_test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,125 @@
const test = require('node:test');
const {test, mock} = require('node:test');
const fs = require('fs');
const util = require('util');
const assert = require('assert');
const { Application, MailSystem } = require('./main');

// TODO: write your tests here
// Remember to use Stub, Mock, and Spy when necessary
// Remember to use Stub, Mock, and Spy when necessary

const unlink = util.promisify(fs.unlink);
const writeFile = util.promisify(fs.writeFile);
// const content = 'Quan\nHenry\nBilly';

// test.mock.method(fs, "readFile", (path, encoding, callback) => {
// callback(null, content)
// });


test('should write a mail for a given name', () => {
const mailSystem = new MailSystem();
const name = 'John';
const res = mailSystem.write(name);
assert.strictEqual(res, 'Congrats, John!');
})

test('send email to a given name', (context) => {
const mailSystem = new MailSystem();
const name = 'John';
const res = 'Congrats, John!';

mock.method(Math, 'random', () => 0.6);
const success = mailSystem.send(name, context);
assert.strictEqual(success, true);

mock.method(Math, 'random', () => 0.4);
const failure = mailSystem.send(name, context);
assert.strictEqual(failure, false);
})


test('create Application instance', async () => {

const content = 'Quan\nHenry\nBilly';
const fileName = 'name_list.txt';
fs.writeFileSync(fileName, content, 'utf8');
// test.mock.method(fs, "readFile", (path, encoding, callback) => {
// callback(null, content)
// });

const app = new Application();

const people = await app.getNames();
assert.deepStrictEqual(people, [['Quan', 'Henry', 'Billy'], []]);
assert.deepStrictEqual(app.people.length, 3);
assert.deepStrictEqual(app.selected.length, 0);
fs.unlinkSync(fileName);
})

test('get random person', async () => {
const content = 'Quan\nHenry\nBilly';
const fileName = 'name_list.txt'
await writeFile(fileName, content, 'utf8');

const app = new Application();
const [people, selected] = await app.getNames()

mock.method(Math, 'random', () => 0.5);
const index = Math.floor(0.5 * people.length)
const person = app.getRandomPerson();
// console.log(person);

assert.strictEqual(person, people[index])

// fs.unlinkSync(fileName);

await unlink(fileName);
})



test('notify selected', async () => {
const content = 'Quan\nHenry\nBilly';
const fileName = 'name_list.txt';
await writeFile(fileName, content, 'utf8');

const app = new Application();

const write = mock.method(app.mailSystem, 'write');
const send = mock.method(app.mailSystem, 'send');

app.selected = ['Quan', 'Henry', 'Billy'];
app.notifySelected();
assert.deepStrictEqual(write.mock.calls.length, 3);
assert.deepStrictEqual(send.mock.calls.length, 3);
unlink(fileName);
})

test('select next person', async () => {
const content = 'Quan\nHenry\nBilly';
const fileName = 'name_list.txt';
await writeFile(fileName, content, 'utf8');

const app = new Application();
const [people, selected] = await app.getNames()

const fn = mock.method(app, 'getRandomPerson');


// all people are selected
app.selected = people;
const res = app.selectNextPerson();
assert.strictEqual(res, null);

// select a person
// already selected Quan
// Make the first call return Quan
// Make the second call return Henry
app.selected = ['Quan'];
fn.mock.mockImplementation(() => 'Henry');
fn.mock.mockImplementationOnce(() => 'Quan');
const person = app.selectNextPerson();
assert.strictEqual(person, 'Henry');
assert.deepStrictEqual(app.selected.length, 2);
unlink(fileName);
})
29 changes: 29 additions & 0 deletions scripts/merge-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

if [ $# -ne 1 ]; then
echo "./merge-all.sh <commit-message>"
exit 1
fi

git fetch origin

for branch in $(git branch -r | grep -v HEAD); do
# Remove the "origin/" prefix
branch=${branch#origin/}

if [[ "$branch" != "main" ]]; then
git checkout "$branch"
if [[ $? -ne 0 ]]; then
echo "Checkout failed for branch $branch"
exit 1
fi
git merge --squash main
if [[ $? -ne 0 ]]; then
echo "Merge failed for branch $branch"
exit 1
fi
git commit -m "$1"
fi
done

git checkout main
Loading