Files
bower/test/core/resolvers/gitRemoteResolver.js
2018-03-28 19:28:43 +02:00

692 lines
24 KiB
JavaScript

var expect = require('expect.js');
var path = require('path');
var fs = require('../../../lib/util/fs');
var Logger = require('bower-logger');
var helpers = require('../../helpers');
var Q = require('q');
var mout = require('mout');
var multiline = require('multiline').stripIndent;
var GitRemoteResolver = require('../../../lib/core/resolvers/GitRemoteResolver');
var defaultConfig = require('../../../lib/config');
describe('GitRemoteResolver', function() {
var testPackage = path.resolve(__dirname, '../../assets/package-a');
var logger;
before(function() {
logger = new Logger();
});
afterEach(function() {
logger.removeAllListeners();
});
function clearResolverRuntimeCache() {
GitRemoteResolver.clearRuntimeCache();
}
function create(decEndpoint) {
if (typeof decEndpoint === 'string') {
decEndpoint = { source: decEndpoint };
}
return new GitRemoteResolver(decEndpoint, defaultConfig(), logger);
}
describe('.constructor', function() {
it('should guess the name from the path', function() {
var resolver;
resolver = create('file://' + testPackage);
expect(resolver.getName()).to.equal('package-a');
resolver = create('git://github.com/twitter/bower.git');
expect(resolver.getName()).to.equal('bower');
resolver = create('git://github.com/twitter/bower');
expect(resolver.getName()).to.equal('bower');
resolver = create('git://github.com');
expect(resolver.getName()).to.equal('github.com');
});
});
describe('.resolve', function() {
it('should checkout correctly if resolution is a branch', function(next) {
var resolver = create({
source: 'file://' + testPackage,
target: 'some-branch'
});
resolver
.resolve()
.then(function(dir) {
expect(dir).to.be.a('string');
var files = fs.readdirSync(dir);
var fooContents;
expect(files).to.contain('foo');
expect(files).to.contain('baz');
expect(files).to.contain('baz');
fooContents = fs
.readFileSync(path.join(dir, 'foo'))
.toString();
expect(fooContents).to.equal('foo foo');
next();
})
.done();
});
it('should checkout correctly if resolution is a tag', function(next) {
var resolver = create({
source: 'file://' + testPackage,
target: '~0.0.1'
});
resolver
.resolve()
.then(function(dir) {
expect(dir).to.be.a('string');
var files = fs.readdirSync(dir);
expect(files).to.contain('foo');
expect(files).to.contain('bar');
expect(files).to.not.contain('baz');
next();
})
.done();
});
it('should checkout correctly if resolution is a commit', function(next) {
var resolver = create({
source: 'file://' + testPackage,
target: 'bdf51ece75e20cf404e49286727b7e92d33e9ad0'
});
resolver
.resolve()
.then(function(dir) {
expect(dir).to.be.a('string');
var files = fs.readdirSync(dir);
expect(files).to.not.contain('foo');
expect(files).to.not.contain('bar');
expect(files).to.not.contain('baz');
expect(files).to.contain('.master');
next();
})
.done();
});
describe('shallow cloning', function() {
var gitRemoteResolverFactory;
beforeEach(function() {
gitRemoteResolverFactory = function(handler) {
return helpers.require(
'lib/core/resolvers/GitRemoteResolver',
{
'../../util/cmd': handler
}
);
};
});
it('should add --depth=1 when shallow cloning is supported', function(next) {
var testSource = 'http://foo/bar.git';
var MyGitRemoteResolver = gitRemoteResolverFactory(function(
cmd,
args
) {
// The first git call fetches the tags for the provided source
if (
mout.array.equals(args, [
'ls-remote',
'--tags',
'--heads',
testSource
])
) {
// Return list of commits, including one tag.
// The tag will be used for the clone call.
return Q.all([
multiline(function() {
/*
e4655d250f2a3f64ef2d712f25dafa60652bb93e refs/heads/some-branch
0a7daf646d4fd743b6ef701d63bdbe20eee422de refs/tags/0.0.1
*/
})
]);
} else if (args[0] === 'clone') {
// Verify parameters of the clone call.
// In this case, the arguments need to contain "--depth 1".
expect(args).to.eql([
'clone',
'http://foo/bar.git',
'-b',
'0.0.1',
'--progress',
'.',
'--depth',
1
]);
// In this case, only the stderr content is evaluated. Everything's fine as long as it
// does not contain any error description.
return Q.all(['stdout', 'stderr']);
}
});
// Mock the call, return true for this test.
MyGitRemoteResolver.prototype._supportsShallowCloning = function() {
return Q.resolve(true);
};
var resolver = new MyGitRemoteResolver(
{ source: testSource },
defaultConfig(),
logger
);
resolver.resolve().then(function() {
next();
});
});
it('should not add --depth=1 when shallow cloning is not supported', function(next) {
var testSource = 'http://foo/bar.git';
var MyGitRemoteResolver = gitRemoteResolverFactory(function(
cmd,
args
) {
// The first git call fetches the tags for the provided source
if (
mout.array.equals(args, [
'ls-remote',
'--tags',
'--heads',
testSource
])
) {
// Return list of commits, including one tag.
// The tag will be used for the clone call.
return Q.all([
multiline(function() {
/*
e4655d250f2a3f64ef2d712f25dafa60652bb93e refs/heads/some-branch
0a7daf646d4fd743b6ef701d63bdbe20eee422de refs/tags/0.0.1
*/
})
]);
} else if (args[0] === 'clone') {
// Verify parameters of the clone call.
// In this case, the arguments should not contain "--depth 1".
expect(args).to.eql([
'clone',
'http://foo/bar.git',
'-b',
'0.0.1',
'--progress',
'.'
]);
// In this case, only the stderr content is evaluated. Everything's fine as long as it
// does not contain any error description.
return Q.all(['stdout', 'stderr']);
}
});
// Mock the call, return false for this test.
MyGitRemoteResolver.prototype._supportsShallowCloning = function() {
return Q.resolve(false);
};
var resolver = new MyGitRemoteResolver(
{ source: testSource },
defaultConfig(),
logger
);
resolver.resolve().then(function() {
next();
});
});
});
it.skip(
'should handle gracefully servers that do not support --depth=1'
);
it.skip('should report progress when it takes too long to clone');
});
describe('#refs', function() {
afterEach(clearResolverRuntimeCache);
it('should resolve to the references of the remote repository', function(next) {
GitRemoteResolver.refs('file://' + testPackage)
.then(function(refs) {
// Remove master and test only for the first 7 refs
refs = refs.slice(1, 8);
expect(refs).to.eql([
'e4655d250f2a3f64ef2d712f25dafa60652bb93e refs/heads/some-branch',
'0a7daf646d4fd743b6ef701d63bdbe20eee422de refs/tags/0.0.1',
'0791865e6f4b88f69fc35167a09a6f0626627765 refs/tags/0.0.2',
'2af02ac6ddeaac1c2f4bead8d6287ce54269c039 refs/tags/0.1.0',
'6ab264f1ba5bafa80fb0198183493e4d5b20804a refs/tags/0.1.1',
'c91ed7facbb695510e3e1ab86bac8b5ac159f4f3 refs/tags/0.2.0',
'8556e55c65722a351ca5fdce4f1ebe83ec3f2365 refs/tags/0.2.1'
]);
next();
})
.done();
});
it('should cache the results', function(next) {
var source = 'file://' + testPackage;
GitRemoteResolver.refs(source)
.then(function() {
// Manipulate the cache and check if it resolves for the cached ones
GitRemoteResolver._cache.refs.get(source).splice(0, 1);
// Check if it resolver to the same array
return GitRemoteResolver.refs('file://' + testPackage);
})
.then(function(refs) {
// Test only for the first 7 refs
refs = refs.slice(0, 7);
expect(refs).to.eql([
'e4655d250f2a3f64ef2d712f25dafa60652bb93e refs/heads/some-branch',
'0a7daf646d4fd743b6ef701d63bdbe20eee422de refs/tags/0.0.1',
'0791865e6f4b88f69fc35167a09a6f0626627765 refs/tags/0.0.2',
'2af02ac6ddeaac1c2f4bead8d6287ce54269c039 refs/tags/0.1.0',
'6ab264f1ba5bafa80fb0198183493e4d5b20804a refs/tags/0.1.1',
'c91ed7facbb695510e3e1ab86bac8b5ac159f4f3 refs/tags/0.2.0',
'8556e55c65722a351ca5fdce4f1ebe83ec3f2365 refs/tags/0.2.1'
]);
next();
})
.done();
});
});
describe('#_supportsShallowCloning', function() {
var gitRemoteResolverFactory;
beforeEach(function() {
gitRemoteResolverFactory = function(handler) {
return helpers.require('lib/core/resolvers/GitRemoteResolver', {
'../../util/cmd': handler
});
};
});
function createCmdHandlerFn(testSource, stderr) {
return function(cmd, args, options) {
expect(cmd).to.be('git');
expect(args).to.eql(['ls-remote', '--heads', testSource]);
expect(options.env.GIT_CURL_VERBOSE).to.be('2');
return Q.all(['stdout', stderr]);
};
}
it('should call ls-remote when using http protocol', function(next) {
var testSource = 'http://foo/bar.git';
var MyGitRemoteResolver = gitRemoteResolverFactory(
createCmdHandlerFn(
testSource,
multiline(function() {
/*
foo: bar
Content-Type: none
1234: 5678
*/
})
)
);
var resolver = new MyGitRemoteResolver(
{ source: testSource },
defaultConfig({ shallowCloneHosts: ['foo'] }),
logger
);
resolver._shallowClone().then(function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(false);
next();
});
});
it('should call ls-remote when using https protocol', function(next) {
var testSource = 'https://foo/bar.git';
var MyGitRemoteResolver = gitRemoteResolverFactory(
createCmdHandlerFn(
testSource,
multiline(function() {
/*
foo: bar
Content-Type: none
1234: 5678
*/
})
)
);
var resolver = new MyGitRemoteResolver(
{ source: testSource },
defaultConfig({ shallowCloneHosts: ['foo'] }),
logger
);
resolver._shallowClone().then(function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(false);
next();
});
});
it('should evaluate to false when the URL can not be parsed', function(next) {
var testSource = 'grmblfjx///:::.git';
var MyGitRemoteResolver = gitRemoteResolverFactory(
createCmdHandlerFn(
testSource,
multiline(function() {
/*
foo: bar
Content-Type: none
1234: 5678
*/
})
)
);
var resolver = new MyGitRemoteResolver(
{ source: testSource },
defaultConfig(),
logger
);
resolver._shallowClone().then(
function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(false);
next();
},
function(err) {
next(err);
}
);
});
it('should evaluate to true when the smart content type is returned', function(next) {
var testSource = 'https://foo/bar.git';
var MyGitRemoteResolver = gitRemoteResolverFactory(
createCmdHandlerFn(
testSource,
multiline(function() {
/*
foo: bar
Content-Type: application/x-git-upload-pack-advertisement
1234: 5678
*/
})
)
);
var resolver = new MyGitRemoteResolver(
{ source: testSource },
defaultConfig({ shallowCloneHosts: ['foo'] }),
logger
);
resolver._shallowClone().then(function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(true);
next();
});
});
it('should evaluate to true when source is a ssh protocol and host is defined to support shallow cloning', function(next) {
var testSource = 'git@foo:bar.git';
var MyGitRemoteResolver = gitRemoteResolverFactory(
createCmdHandlerFn(
testSource,
multiline(function() {
/*
foo: bar
Content-Type: application/x-git-upload-pack-advertisement
1234: 5678
*/
})
)
);
var resolver = new MyGitRemoteResolver(
{ source: testSource },
defaultConfig({ shallowCloneHosts: ['foo'] }),
logger
);
resolver._shallowClone().then(function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(true);
next();
});
});
it('should cache hosts that support shallow cloning', function(next) {
var testSource = 'https://foo/bar.git';
var counter = 0;
var MyGitRemoteResolver = gitRemoteResolverFactory(function(
cmd,
args,
options
) {
counter++;
if (counter === 1) {
expect(cmd).to.be('git');
expect(args).to.eql(['ls-remote', '--heads', testSource]);
expect(options.env.GIT_CURL_VERBOSE).to.be('2');
return Q.all([
'stdout',
multiline(function() {
/*
foo: bar
Content-Type: application/x-git-upload-pack-advertisement
1234: 5678
*/
})
]);
} else {
return Q.reject(new Error('More calls than expected'));
}
});
var resolver = new MyGitRemoteResolver(
{ source: testSource },
defaultConfig({ shallowCloneHosts: ['foo'] }),
logger
);
resolver._shallowClone().then(function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(true);
var resolver2 = new MyGitRemoteResolver(
{ source: testSource },
defaultConfig({ shallowCloneHosts: ['foo'] }),
logger
);
resolver2._shallowClone().then(
function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(true);
next();
},
function(err) {
next(err);
}
);
});
});
it('should cache hosts that support shallow cloning across multiple repos', function(next) {
var testSource1 = 'https://foo/bar.git';
var testSource2 = 'https://foo/barbaz.git';
var counter = 0;
var MyGitRemoteResolver = gitRemoteResolverFactory(function(
cmd,
args,
options
) {
counter++;
if (counter === 1) {
expect(cmd).to.be('git');
expect(args).to.eql(['ls-remote', '--heads', testSource1]);
expect(options.env.GIT_CURL_VERBOSE).to.be('2');
return Q.all([
'stdout',
multiline(function() {
/*
foo: bar
Content-Type: application/x-git-upload-pack-advertisement
1234: 5678
*/
})
]);
} else {
return Q.reject(new Error('More calls than expected'));
}
});
var resolver = new MyGitRemoteResolver(
{ source: testSource1 },
defaultConfig({ shallowCloneHosts: ['foo'] }),
logger
);
resolver._shallowClone().then(function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(true);
var resolver2 = new MyGitRemoteResolver(
{ source: testSource2 },
defaultConfig({ shallowCloneHosts: ['foo'] }),
logger
);
resolver2._shallowClone().then(
function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(true);
next();
},
function(err) {
next(err);
}
);
});
});
it('should run separate checks for separate hosts ', function(next) {
var testSource1 = 'https://foo/bar.git';
var testSource2 = 'https://foo.bar.baz/barbaz.git';
var counter = 0;
var MyGitRemoteResolver = gitRemoteResolverFactory(function(
cmd,
args,
options
) {
counter++;
if (counter === 1) {
expect(cmd).to.be('git');
expect(args).to.eql(['ls-remote', '--heads', testSource1]);
expect(options.env.GIT_CURL_VERBOSE).to.be('2');
return Q.all([
'stdout',
multiline(function() {
/*
foo: bar
Content-Type: application/x-git-upload-pack-advertisement
1234: 5678
*/
})
]);
} else {
expect(cmd).to.be('git');
expect(args).to.eql(['ls-remote', '--heads', testSource2]);
expect(options.env.GIT_CURL_VERBOSE).to.be('2');
return Q.all([
'stdout',
multiline(function() {
/*
foo: barbaz
Content-Type: application/x-git-upload-pack-advertisement
1234: 5678
*/
})
]);
}
});
var resolver = new MyGitRemoteResolver(
{ source: testSource1 },
defaultConfig({ shallowCloneHosts: ['foo', 'foo.bar.baz'] }),
logger
);
resolver._shallowClone().then(function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(true);
var resolver2 = new MyGitRemoteResolver(
{ source: testSource2 },
defaultConfig({
shallowCloneHosts: ['foo', 'foo.bar.baz']
}),
logger
);
resolver2._shallowClone().then(
function(shallowCloningSupported) {
expect(shallowCloningSupported).to.be(true);
next();
},
function(err) {
next(err);
}
);
});
});
});
});