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

Better unit tests #100

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
35 changes: 23 additions & 12 deletions lib/nuts.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,38 +235,49 @@ Nuts.prototype.onUpdateRedirect = function(req, res, next) {
Nuts.prototype.onUpdate = function(req, res, next) {
var that = this;
var fullUrl = getFullUrl(req);

var platform = req.params.platform;
var channel = req.params.channel || '*';
var channel = req.params.channel || 'stable';
var tag = req.params.version;
var filetype = req.query.filetype ? req.query.filetype : 'zip';

Q()
.then(function() {
if (!tag) throw createError(400, 'Requires "version" parameter');
if (!platform) throw createError(400, 'Requires "platform" parameter');
if (!tag) {
throw createError(400, 'Requires "version" parameter');
}
if (!platform) {
throw createError(400, 'Requires "platform" parameter');
}

platform = platforms.detect(platform);

return that.versions.filter({
tag: '>='+tag,
platform: platform,
channel: channel
channel: channel,
stripChannel: true
Copy link

@alexstrat alexstrat Apr 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get why stripChannel should be striped.

Imagine we have a version 0.1.53-beta in releases (0.1.53 is not in release yet):
With stripChannel=true: /update/channel/beta/darwin_x64/0.1.53 leads to 0.1.53-beta => will install a lower version ⚠️
With stripChannel=false: /update/channel/beta/darwin_x64/0.1.53 leads to 204

Copy link

@alexstrat alexstrat Apr 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to remove the stripChannel but I ran into other problem: I was not able to update an user on a stable version to a beta version, because of how semver.satisfies handle the prerelease tag. Such a nightmare..

});
})
.then(function(versions) {
var latest = _.first(versions);
var latest = versions[0];

// Already using latest version?
if (!latest || latest.tag == tag) {
return res.status(204).send('No updates');
}

var notesSlice = versions.slice(0, -1);
if (versions.length === 1) {
notesSlice = [versions[0]];
}
// Extract release notes from all versions in range
var notesSlice = versions.length === 1? [versions[0]] : versions.slice(0, -1);
var releaseNotes = notes.merge(notesSlice, { includeTag: false });
var gitFilePath = (channel === '*' ? '/../../../' : '/../../../../../');
res.status(200).send({
'url': urljoin(fullUrl, gitFilePath, '/download/version/'+latest.tag+'/'+platform+'?filetype='+filetype),

// URL for download should be absolute
var gitFilePath = (req.params.channel ? '/../../../../../' : '/../../../' );

res.status(200)
.send({
'url': urljoin(fullUrl, gitFilePath,
'/download/version/' + latest.tag + '/' + platform + '?filetype=' + filetype),
'name': latest.tag,
'notes': releaseNotes,
'pub_date': latest.published_at.toISOString()
Expand Down
91 changes: 72 additions & 19 deletions lib/versions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,47 @@ var createError = require('http-errors');

var platforms = require('./utils/platforms');

// Normalize tag name
/**
* Normalize a tag name to remove the prefix "v"
* @param {String} tag
* @return {String} tag
*/
function normalizeTag(tag) {
if (tag[0] == 'v') tag = tag.slice(1);
return tag;
}

// Extract channel of version
/**
* Extract channel of tag name
* @param {String} tag
* @return {String} channel
*/
function extractChannel(tag) {
var suffix = tag.split('-')[1];
if (!suffix) return 'stable';

return suffix.split('.')[0];
}

// Normalize a release to a version
/**
* Strip channel from a tag name
* @param {String} tag
* @return {String} tag
*/
function stripChannel(tag) {
return tag.split('-')[0];
}

/**
* Normalize a release to a version
* @param {Object} release
* @return {Version} version?
*/
function normalizeVersion(release) {
// Ignore draft
if (release.draft) return null;
if (release.draft) {
return null;
}

var downloadCount = 0;
var releasePlatforms = _.chain(release.assets)
Expand All @@ -43,15 +66,20 @@ function normalizeVersion(release) {
.value();

return {
tag: normalizeTag(release.tag_name).split('-')[0],
channel: extractChannel(release.tag_name),
notes: release.body || '',
tag: normalizeTag(release.tag_name),
channel: extractChannel(release.tag_name),
notes: release.body || '',
published_at: new Date(release.published_at),
platforms: releasePlatforms
platforms: releasePlatforms
};
}

// Compare two version
/**
* Compare two version
* @param {Version} v1
* @param {Version} v2
* @return {Number}
*/
function compareVersions(v1, v2) {
if (semver.gt(v1.tag, v2.tag)) {
return -1;
Expand Down Expand Up @@ -85,13 +113,22 @@ Versions.prototype.get = function(tag) {
});
};

// Filter versions with criterias
/**
* Filter versions with criterias
* @param {String} opts.tag? : tag name filter to satisfy (ex: ">=2.0.0")
* @param {String} opts.platform? : name of the platform supported by the version
* @param {String} opts.channel? : only list version of this channel ("*" for all)
* @param {Boolean} opts.stripChannel : compare tag name withotu the channel
* @return {Promise<Array<Version>>} versions
*/
Versions.prototype.filter = function(opts) {
opts = _.defaults(opts || {}, {
tag: 'latest',
platform: null,
channel: 'stable'
tag: 'latest',
platform: null,
stripChannel: false,
channel: 'stable'
});

if (opts.platform) {
opts.platform = platforms.detect(opts.platform);
}
Expand All @@ -101,19 +138,32 @@ Versions.prototype.filter = function(opts) {
return _.chain(versions)
.filter(function(version) {
// Check channel
if (opts.channel != '*' && version.channel != opts.channel) return false;
if (opts.channel !== '*' && version.channel != opts.channel) {
return false;
}

// Not available for requested paltform
if (opts.platform && !platforms.satisfies(opts.platform, _.pluck(version.platforms, 'type'))) return false;
if (opts.platform && !platforms.satisfies(opts.platform, _.pluck(version.platforms, 'type'))) {
return false;
}

// Check tag satisfies request version
return opts.tag == 'latest' || semver.satisfies(version.tag, opts.tag);
var tagName = version.tag;
if (opts.stripChannel) {
tagName = stripChannel(tagName);
}

return (opts.tag == 'latest' || semver.satisfies(tagName, opts.tag));
})
.value();
});
};

// Resolve a platform, by filtering then taking the first result
/**
* Resolve a platform, by filtering then taking the first result
* @param {Object} opts
* @return {Promise<Version>} version
*/
Versions.prototype.resolve = function(opts) {
return this.filter(opts)
.then(function(versions) {
Expand All @@ -126,8 +176,11 @@ Versions.prototype.resolve = function(opts) {
});
};

// List all channels from releases
Versions.prototype.channels = function(opts) {
/**
* List all channels from releases
* @return {Promise<Channel>}
*/
Versions.prototype.channels = function() {
return this.list()
.then(function(versions) {
var channels = {};
Expand Down
3 changes: 3 additions & 0 deletions test/all.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
require('should');

// Sync tests
require('./platforms');
require('./win-releases');

// Require a backend
require('./versions');
require('./download');
require('./update');
8 changes: 0 additions & 8 deletions test/app.js

This file was deleted.

2 changes: 1 addition & 1 deletion test/download.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var request = require('supertest');
var app = require('./app');
var app = require('./testing').app;

var MAC_USERAGENT = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us)'
+ ' AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19';
Expand Down
14 changes: 14 additions & 0 deletions test/testing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var nuts = require('../lib');

var config = {
repository: 'SamyPesse/nuts-testing',
Copy link

@alexstrat alexstrat Apr 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be more handy to implement and use a TestBackend (inherits from Backend) in which we could inject mock data? It would permit to test a large range of scenarios.

The GithubBackend looks thin and dumb enough so that test cases with TestBackend will cover a similar amount of logic. Moreover, some test cases (functional) can use the GithubBackend with the nuts-testing repo.

token: process.env.GITHUB_TOKEN
};

var instance = nuts.Nuts(config);
var app = nuts.createApp(config);

module.exports = {
app: app,
nuts: instance
};
82 changes: 67 additions & 15 deletions test/update.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,82 @@
var request = require('supertest');
var expect = require('expect');
var app = require('./app');

var app = require('./testing').app;

describe('Update', function() {
var agent = request.agent(app);

describe('Squirrel.Mac (OS X)', function() {

it('should return a 204 if using latest version', function(done) {
agent
.get('/update/osx/1.0.0')
.expect(204, done);
describe('/update/osx/', function() {
it('should return a 204 if using latest version', function(done) {
agent
.get('/update/osx/1.0.0')
.expect(204, done);
});

it('should return a 200 with json if using old stable version', function(done) {
agent
.get('/update/osx/0.9.0')
.expect('Content-Type', /json/)
.expect(function(res) {
expect(res.body.name).toBe('1.0.0');
expect(res.body.url).toExist();
expect(res.body.pub_date).toExist();
})
.expect(200, done);
});

it('should return a 200 with json if using old beta version (1)', function(done) {
agent
.get('/update/osx/0.9.1-beta')
.expect('Content-Type', /json/)
.expect(function(res) {
expect(res.body.name).toBe('1.0.0');
})
.expect(200, done);
});

it('should return a 200 with json if using old beta version (2)', function(done) {
agent
.get('/update/osx/0.9.1-beta.1')
.expect('Content-Type', /json/)
.expect(function(res) {
expect(res.body.name).toBe('1.0.0');
})
.expect(200, done);
});
});

it('should return a 200 with json if using old version', function(done) {
agent
.get('/update/osx/0.9.0')
.expect('Content-Type', /json/)
.expect(function(res) {
expect(res.body.name).toBe('1.0.0');
expect(res.body.url).toExist();
expect(res.body.pub_date).toExist();
})
.expect(200, done);
describe('/update/channel/beta/osx', function() {
it('should update from 0.9.1-beta to 1.0.1-beta.0', function(done) {
agent
.get('/update/channel/beta/osx/0.9.1-beta')
.expect('Content-Type', /json/)
.expect(function(res) {
expect(res.body.name).toBe('1.0.1-beta.0');
})
.expect(200, done);
});

it('should not update from 1.0.1-beta.0', function(done) {
agent
.get('/update/channel/beta/osx/1.0.1-beta.0')
.expect(204, done);
});
});

describe('/update/channel/alpha/osx', function() {
it('should update from 0.9.1-beta to 1.1.0-alpha.0', function(done) {
agent
.get('/update/channel/alpha/osx/0.9.1-beta')
.expect('Content-Type', /json/)
.expect(function(res) {
expect(res.body.name).toBe('1.1.0-alpha.0');
})
.expect(200, done);
});
});
});

});
39 changes: 39 additions & 0 deletions test/versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
var expect = require('expect');
var versions = require('./testing').nuts.versions;

describe('Versions', function() {

describe('.list', function() {
it('should list all versions', function() {
return versions.list()
.then(function(out) {
expect(out.length).toEqual(6);
});
});
});

describe('.filter', function() {

it('should filter correctly by tag name', function() {
return versions.filter({ tag: '>=0.9.0' })
.then(function(out) {
expect(out.length).toEqual(2);
expect(out[0].tag).toEqual('1.0.0');
expect(out[1].tag).toEqual('0.9.0');
});
});

it('should filter correctly by tag name (stripChannel)', function() {
return versions.filter({
tag: '>=0.9.0',
channel: '*',
stripChannel: true
})
.then(function(out) {
expect(out.length).toEqual(6);
});
});

});

});