blob: f91bcc9b99b91f51ca185d0f673fc2e9f02e22c2 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var test = require('prova');
var serve = require('./serve');
var Promise = require('../../src/lib/promise');
var vanadium = require('../../');
var naming = vanadium.naming;
var verror = vanadium.verror;
var ALBUMS = [
'public',
'public/2014',
'public/2014/california',
'public/2014/california/los-angeles',
'public/2014/california/san-francisco',
'public/2014/california/san-francisco/golden-gate',
'public/2014/newyork',
'public/2014/newyork/nyc',
'public/2014/newyork/nyc/empire-state',
'public/2015',
'private',
'private/2013',
'private/2013/california',
'private/2013/california/wedding',
'private/2013/california/wedding/reception'
];
function Directory() {
this.children = {};
}
Directory.prototype._addChild = function(name, node) {
this.children[name] = node;
};
Directory.prototype.__globChildren = function(ctx, serverCall, $stream) {
Object.keys(this.children).forEach(function(child) {
$stream.write(child);
});
};
function createNodes(files) {
var root = new Directory();
for (var i = 0; i < files.length; i++) {
var path = files[i].split('/');
var currentNode = root;
for (var j = 0; j < path.length; j++) {
var component = path[j];
var node = currentNode.children[component];
// Add this element if it doesn't exist.
if (!node) {
node = new Directory();
currentNode._addChild(component, node);
}
currentNode = node;
}
}
return root;
}
function createAuthorizer(disallowed) {
return function(ctx, call) {
for (var i = 0; i < disallowed.length; i++) {
if (call.suffix === disallowed[i]) {
return new Error('disallowed');
}
}
return;
};
}
function createDispatcher(root, disallowed) {
return function(suffix) {
var auth;
if (disallowed) {
auth = createAuthorizer(disallowed);
}
if (suffix === '') {
return {
service: root,
authorizer: auth
};
}
var path = suffix.split('/');
var current = root;
for (var i = 0; i < path.length; i++) {
current = current.children[path[i]];
if (!current) {
return Promise.reject(new Error('not found'));
}
}
return Promise.resolve({
service: current,
authorizer: auth
});
};
}
function runChildrenGlobTest(pattern, expectedResults, disallowed, assert) {
runGlobTest(pattern, expectedResults,
createDispatcher(createNodes(ALBUMS), disallowed),
disallowed,
assert);
}
function runGlobTest(pattern, expectedResults, dispatcher, expectedErrors,
assert) {
serve({
name: 'testGlob',
autoBind: false,
dispatcher: dispatcher
}, function(err, res) {
if (err) {
return assert.end(err);
}
var namespace = res.runtime.getNamespace();
var ctx = res.runtime.getContext();
var globRPC = namespace.glob(ctx, pattern);
var stream = globRPC.stream;
var globResults = [];
var globErrors = [];
var hadErrors = false;
stream.on('data', function(mountPoint) {
globResults.push(mountPoint.name);
});
stream.on('error', function(errItem) {
hadErrors = true;
assert.ok(errItem.error instanceof Error);
assert.ok(typeof errItem.name === 'string');
// The tests are setup to error based on name, which is this glob error.
assert.ok(errItem.error instanceof verror.NoServersError);
globErrors.push(errItem.name);
});
stream.on('end', function() {
globResults.sort();
expectedResults.sort();
assert.deepEqual(globResults, expectedResults);
if (expectedErrors) {
globErrors.sort();
expectedErrors.sort();
assert.deepEqual(globErrors, expectedErrors);
assert.ok(hadErrors, 'expected to have errors on the stream');
} else {
assert.notOk(hadErrors, 'expected not to have errors on the stream');
}
res.end(assert);
});
});
}
test('Test globbing all descendants of root - GlobChildren - ' +
'glob(testGlob/...)', function(assert) {
var expectedResults = ALBUMS.map(function(s) { return 'testGlob/' + s; });
// We need to push testGlob twice because we get one entry from the
// mountable and the next entry from the glob method. This is expected
// behavior.
expectedResults.push('testGlob');
expectedResults.push('testGlob');
expectedResults.sort();
runChildrenGlobTest('testGlob/...', expectedResults, null, assert);
});
test('Test globbing all descendants of a child - GlobChildren - ' +
' glob(testGlob/private/...)',
function(assert) {
var expectedResults = [
'testGlob/private',
'testGlob/private/2013',
'testGlob/private/2013/california',
'testGlob/private/2013/california/wedding',
'testGlob/private/2013/california/wedding/reception',
];
runChildrenGlobTest('testGlob/private/...', expectedResults, null, assert);
});
test('Test globbing children of root - GlobChildren - glob(testGlob/*)',
function(assert) {
var expectedResults = [
'testGlob/private',
'testGlob/public',
];
runChildrenGlobTest('testGlob/*', expectedResults, null, assert);
});
test('Test globbing pattern testGlob/*/*/california - GlobChildren',
function(assert) {
var expectedResults = [
'testGlob/private/2013/california',
'testGlob/public/2014/california',
];
runChildrenGlobTest('testGlob/*/*/california', expectedResults, null, assert);
});
test('Test globbing pattern testGlob/*/20*/california/... - GlobChildren',
function(assert) {
var expectedResults = [
'testGlob/private/2013/california',
'testGlob/private/2013/california/wedding',
'testGlob/private/2013/california/wedding/reception',
'testGlob/public/2014/california',
'testGlob/public/2014/california/los-angeles',
'testGlob/public/2014/california/san-francisco',
'testGlob/public/2014/california/san-francisco/golden-gate',
];
runChildrenGlobTest('testGlob/*/20*/california/...', expectedResults, null,
assert);
});
test('Test globbing a partially restricted namespace - GlobChildren -' +
' testGlob/private is restricted', function(assert) {
var expectedResults = [
'testGlob',
'testGlob',
'testGlob/public',
'testGlob/public/2014',
'testGlob/public/2014/california',
'testGlob/public/2014/california/los-angeles',
'testGlob/public/2014/california/san-francisco',
'testGlob/public/2014/california/san-francisco/golden-gate',
'testGlob/public/2014/newyork',
'testGlob/public/2014/newyork/nyc',
'testGlob/public/2014/newyork/nyc/empire-state',
'testGlob/public/2015'
];
var restrictedNames = ['private'];
runChildrenGlobTest('testGlob/...', expectedResults, restrictedNames, assert);
});
test('Test globbing a fully restricted namespace - GlobChildren -' +
' all children of root are restricted', function(assert) {
var expectedResults = [
'testGlob',
'testGlob'
];
var restrictedNames = ['private', 'public'];
runChildrenGlobTest('testGlob/...', expectedResults, restrictedNames, assert);
});
function FullGlobber() {
}
FullGlobber.prototype.__glob = function(ctx, serverCall, glob, $stream) {
var mountEntry = new naming.MountEntry({
name: naming.join(serverCall.securityCall.suffix, glob),
});
$stream.write(new naming.GlobReply({
entry: mountEntry
}));
};
test('Test globbing all descendants of root - FullGlobber - glob(testGlob/...)',
function(assert) {
var expectedResults = [
'testGlob',
'testGlob/...',
];
function dispatcher(suffix) {
return {
service: new FullGlobber(),
};
}
runGlobTest('testGlob/...', expectedResults, dispatcher, null, assert);
});
test('Test globbing all descendants of a child - FullGlobber - ' +
'glob(testGlob/bar/...)', function(assert) {
var expectedResults = [
'testGlob/bar/...',
];
function dispatcher(suffix) {
return {
service: new FullGlobber(),
};
}
runGlobTest('testGlob/bar/...', expectedResults, dispatcher, null, assert);
});
function ChildGlobber(children) {
this.children = children;
}
ChildGlobber.prototype.__globChildren = function(ctx, $stream) {
for (var i = 0; i < this.children.length; i++) {
$stream.write(this.children[i]);
}
};
test('Test mixing GlobChildren and FullGlobber', function(assert) {
var expectedResults = [
'testGlob/foo/bar/baz',
];
function dispatcher(suffix) {
var service;
if (suffix === '') {
service = new ChildGlobber(['foo', 'bar']);
} else if (suffix === 'foo') {
service = new FullGlobber();
} else {
service = new ChildGlobber([]);
}
return {
service: service,
};
}
runGlobTest('testGlob/foo/bar/baz', expectedResults, dispatcher, null,
assert);
});