Lab 4 Testing Sails, and Advanced git operations
Lab 4 Testing Sails, and Advanced git operations
Lab 4.1 Testing in Sails
Mocha: A testing framework for Node.js application
Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases. Hosted on GitHub.
Configuration
In the project directory, we run npm install mocha --save-dev
to add mocha library in development dependencies.
cd your_project_location
npm install mocha --save-dev
If you don’t have project in your local machine, please clone your Sails project from Github, or create a new Sails project using sails new
command.
sails new testingApp
# or
git clone https://your_project_git
test/
directory structure
We would like to put all testing related stuffs in test
folder in our project. I have created the folder skeleton for you. Please download test.zip
from BUMoodle and extract in your sails project . After that, your sails project file structure would look similar to the followings.
./myApp
├── api/
├── assets/
├── ...
├── test/
│ ├── integration/
│ │ ├── controllers/
│ │ │ └── ...
│ │ ├── models/
│ │ └── ...
│ ├── fixtures/
| │ └── ...
│ ├── lifecycle.test.js
│ └── mocha.opts
├── ...
└── views/
Example: Unit test for User Models
Let’s develop a unit test for User
model in our Sails app.
Optional: Generating User Model and configure blueprints
If you don’t have api/models/User.js
in your project, please run sails generate api
to generate it.
sails generate api User
Please modify config/blueprints.js
as follows.
actions: true,
rest: false,
shortcuts: false,
Adding attributes for User
model
For simplicity, we only define necessary attribute for User
model. Please add the following attributes in api/models/User.js
.
username: {
type: "string"
},
password: {
type: "string"
},
Adding admin account
In config/bootstrap.js
, we hard-coded an administrator account for our system. Please put the following lines right before return done();
.
await User.createEach([
{ "username": "admin", "password": "123456" },
]);
Writing the unit test
describe('User (model)', function() {
describe('Admin account', function() {
it('should find an User with username "admin"', function (done) {
User.find({username:'admin'})
.then(function(users) {
if (users.length == 0) {
return done(new Error(
'Should return 1 user -- the admin '+
'But instead, got: no user found'
));
}
return done();
})
.catch(done);
});
});
});
Run tests
To run all tests, please issue the following command in terminal.
node ./node_modules/mocha/bin/mocha test/lifecycle.test.js test/integration/**/*.test.js
If you would like to simplify the previous command, we need to edit package.json
for running the tests with npm test
. Please comment out this line.
"test": "npm run lint && npm run custom-tests && echo 'Done.'",
Then add the following line.
"test": "node ./node_modules/mocha/bin/mocha test/lifecycle.test.js test/integration/**/*.test.js",
//...
"scripts": {
//"test": "npm run lint && npm run custom-tests && echo 'Done.'",
"test": "node ./node_modules/mocha/bin/mocha test/lifecycle.test.js test/integration/**/*.test.js",
},
//...
After that, we could simply run the tests by npm test
in terminal.
npm test
Supertest: for testing HTTP request and response
Supertest is a Javascript module to provide a high-level abstraction for testing HTTP. We could easy to make a HTTP request to our Sails app and check the result for testing. It works great with Mocha.
There are several examples on its official Github, please have a look.
Configuration
cd your_project_location
npm install supertest --save-dev
Example 1: Testing controller actions
Adding login and logout actions in UserController
Let’s add login
and logout
action to api/controllers/UserController.js
for testing.
login: async function (req, res) {
if (req.method == "GET") return res.view('user/login');
if (!req.body.username) return res.badRequest();
if (!req.body.password) return res.badRequest();
var user = await User.findOne({ username: req.body.username });
if (!user) {
res.status(401);
return res.send("User not found");
}
if (user.password != req.body.password) {
res.status(401);
return res.send("Wrong Password");
}
req.session.regenerate(function (err) {
if (err) return res.serverError(err);
req.session.username = req.body.username;
sails.log("Session: " + JSON.stringify(req.session) );
return res.ok("Login successfully");
});
},
logout: async function (req, res) {
req.session.destroy(function (err) {
if (err) return res.serverError(err);
return res.ok("Log out successfully");
});
},
Writing Unit test for login and logout action
Please create a new Javascript file attest/integration/controllers/UserController.test.js
with the following codes.
var supertest = require('supertest');
describe('UserController', function() {
describe('#login() with admin account', function() {
it('should return status 200 with "Login successfully" in body', function (done) {
supertest(sails.hooks.http.app)
.post('/user/login')
.send({ username: 'admin', password: '123456' })
.expect(200, 'Login successfully', done);
});
});
describe('#login() with non-exists account', function() {
it('should return status 401 with "User not found" in body', function (done) {
supertest(sails.hooks.http.app)
.post('/user/login')
.send({ username: 'user', password: '123456' })
.expect(401, 'User not found', done);
});
});
describe('#logout()', function() {
it('should return status 200 with "Log out successfully" in body', function (done) {
supertest(sails.hooks.http.app)
.get('/user/logout')
.expect(200, 'Log out successfully', done);
});
});
});
Then run tests by npm test
.
Example 2: Testing Dialogflow fulfillment Webhook
In case you don’t have a Sails project for Dialogflow, you may fork this sample project and clone it in your machine. The upcoming steps are already done in this sample project. You may have a look.
Getting a fulfillment request from Dialogflow
Let’s direct to the Dialogflow Console at https://console.dialogflow.com.
Please input a query in Try it now
input box. When there is response, click on Diagnostic info. You should able to see Fulfillment Request
and Fulfillment Response
in JSON format.
Writing Unit test
Please create a Javascript file for the unit test at test/integration/controllers/PersonController.test.js
.
var supertest = require('supertest');
describe('PersonController.fulfillment', function() {
it('should response status 200 with JSON', function (done) {
supertest(sails.hooks.http.app)
.post('/person/fulfillment')
.send(/* paste your fulfullment request json here */)
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, /* paste your expected fulfullment response json here */, done);
});
});
Then simply run tests by npm test
.
Lab 4.2 Advanced Git operations
In this section, we talk about more git operations. You should familiar with git clone
, git add
, git commit
and git push
. You may also take a look to Pro Git book, it is a complete reference book.
Clone and checkout remote branch
Checkout directly while clone
We could checkout a particular branch while clone by adding -b branch_name
option.
git clone https://your_repo_url -b branch_name
It just behave the same as following commands basically.
git clone https://your_repo_url
git checkout -b branch_name -t origin/branch_name
The -b branch_name
option in git checkout
means create a branch with branch_name
locally. For -t origin/branch_name
means set the newly created branch to track remote branch origin/branch_name
.
Checkout manually after cloning
git clone https://your_repo_url
cd your_project_location
git checkout -b kenny -t origin/kenny
Pull remote changes to local: git pull vs git fetch
git fetch
git fetch
download objects/commits/refs/tags from remote for all branches. If would update remote-tracked branches to latest commit without touching your modification.
cd your_project_location
git fetch
git pull
git pull
is basically same as git fetch && git merge origin/current_branch_name
.
Assume the following history exists and the current branch is “master”:
A---B---C master on origin
/
D---E---F---G master
^
Then “git pull” will fetch and replay the changes from the remote master branch since it diverged from the local master (i.e., E) until its current commit (C) on top of master and record the result in a new commit along with the names of the two parent commits and a log message from the user describing the changes.
A---B---C origin/master
/ \
D---E---F---G---H master
git pull
would fail, if there are any conflicts in merging.
Merge branch
git stash: Save uncommitted changes
git stash
is a powerful command that help us to save uncommitted changes. It always save us when git fetch
fails. Here is a list of git stash
sub-commands.
List stash
git stash list
It would provide output like this.
stash@{0}: On branch_name: xxxxx
View stash
git stash show
Example output would like this, showing which file changed with the number of changed lines.
drivers/media/platform/msm/camera_v2/sensor/Makefile | 2 ++
drivers/misc/tspdrv/ImmVibeSPI.c | 21 +++++++++++
drivers/nfc/Kconfig | 2 ++
drivers/nfc/Makefile | 1 +
drivers/power/qpnp-charger.c | 84 +++++++++++++++++++++++++++++++++++++++++++
firmware/synaptics/g3/PLG352-V1.05-PR1648545-DS5.5.1.0-30055185.img | Bin 0 -> 95744 bytes
firmware/synaptics/g3/PLG352-V1.08-PR1670515-DS5.5.1.0-30055188.img | Bin 0 -> 95744 bytes
firmware/synaptics/g3/PLG391-V1.05-PR1653201-DS5.5.1.0.1066_10055185_S3528A1.img | Bin 0 -> 95744 bytes
firmware/synaptics/g3/g3_kddi_jp_limit.txt | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 382 insertions(+)
Output stashed changes as patch
We could use git stash show
with option -p
to format stash as unified patch.
git stash show -p > filename.patch
Merge code on Github
Create pull request
To create a pull request, please click on New pull request button.
Then you would have a screen to select forks and branches. You could click on compare across forks to select branches in other forks.
The merging direction is from right to left, meaning that you are merging code on the right side branch to left side branch. If you are the owner of left side repository, you could directly merge the commits to your branch. If you are not the owner, a pull request would created in the left side repository waiting for repository owner to approval. The repository owner would receive an notification for reviewing your pull request.
Merge code with patch locally
Getting a single commit as patch on Github
Let’s merge a security fix into our Sails project. Here is the link of commit.
https://github.com/hkbu-kennycheng/gahk/commit/c9609f501c166bbdb5739f86a49bb4621e669264
We could get it as email patch format by appending .patch
in the URL.
https://github.com/hkbu-kennycheng/gahk/commit/c9609f501c166bbdb5739f86a49bb4621e669264.patch
The file is actually an email with a unified patch file, which is easy to read and debug.
From c9609f501c166bbdb5739f86a49bb4621e669264 Mon Sep 17 00:00:00 2001
From: Kenny Cheng <31645711+hkbu-kennycheng@users.noreply.github.com>
Date: Tue, 29 Jan 2019 10:41:18 +0800
Subject: [PATCH] Update package.json fixs CVE-2018-3750
---
package.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 00f78b4..0b2ef1c 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,8 @@
"@sailshq/connect-redis": "^3.2.1",
"@sailshq/socket.io-redis": "^5.2.0",
"@sailshq/lodash": "^3.10.3",
- "async": "2.0.1"
+ "async": "2.0.1",
+ "deep-extend": ">=0.5.1"
},
"devDependencies": {
"@sailshq/eslint": "^4.19.3"
Apply patch with git am
To apply email patch file directly from URL, we could use the following command.
cd your_project_location
curl -k https://github.com/hkbu-kennycheng/gahk/commit/c9609f501c166bbdb5739f86a49bb4621e669264.patch | git am
curl
is a command-line HTTP client, which download the patch for us and print it on standard output. We then pipe the standard output to git am
, apply it in our local repository directly.
It will create a new commit in the repository. You could see the commit history by git log
command.
git log