uri-template-router
Version:
Match a URI to a pattern in a collection of URI Templates
362 lines (340 loc) • 8.56 kB
JavaScript
'use strict';
const assert = require('assert').strict;
const { Node, union, concat, star, optional, reverse, reduce, fromString, compare } = require('../lib/fsm.js');
// const { toViz } = require('../index.js');
function accepts(fsm, string){
var state = fsm[0];
const history = [fsm[0]];
for(var offset = 0; state && offset < string.length; offset++){
if(!state) break;
const symbol = string[offset];
const nextStateId = state.get(symbol);
if(nextStateId === undefined) return;
state = fsm[nextStateId];
history.push(state);
}
if(state.final){
return history;
}
}
describe('fsm.Node', function(){
it('matching groups', function(){
const fsm_foo = [
new Node({f:1}, {a: 0}),
new Node({o:2}, {a: 1}),
new Node({o:3}, {a: 2}),
new Node({}, {}, ['a']),
];
assert(accepts(fsm_foo, "foo"));
});
});
describe('fsm.fromString', function(){
it('fromString("")', function(){
assert.deepEqual(fromString(''), [new Node({}, {}, true)]);
});
it('fromString("foo")', function(){
assert.deepEqual(fromString('foo', v=>({offset: v})), [
new Node({f:1}, {offset: 0}),
new Node({o:2}, {offset: 1}),
new Node({o:3}, {offset: 2}),
new Node({}, {}, true),
]);
});
});
describe('fsm.union', function(){
it('expects an array', function(){
assert.throws(function(){
union();
}, err => (err.toString().indexOf('Expected `fsms` to be an array of arrays')>=0));
assert.throws(function(){
union([null]);
}, err => err.toString().indexOf('Expected `fsm` to be an Array')>=0);
assert.throws(function(){
union([true]);
}, err => err.toString().indexOf('Expected `fsm` to be an Array')>=0);
});
it('∅∪∅', function(){
const fsm_a = [
new Node({}),
];
const fsm_b = [
new Node({}),
];
const fsm_ab = [
new Node({}),
];
assert.deepEqual(union([fsm_a, fsm_b]), fsm_ab);
});
it('𝜀∪∅', function(){
const fsm_a = [
new Node({}, {}, true),
];
const fsm_b = [
new Node({}),
];
const fsm_ab = [
new Node({}, {}, true),
];
assert.deepEqual(union([fsm_a, fsm_b]), fsm_ab);
});
it('"a" union "b"', function(){
const fsm_a = fromString('a');
const fsm_b = fromString('b');
const fsm_ab = [
new Node({a:1, b:2}),
new Node({}, {}, true),
new Node({}, {}, true),
];
assert.deepEqual(union([fsm_a, fsm_b]), fsm_ab);
});
it('matching groups', function(){
const fsm_a = fromString('a');
const fsm_b = fromString('b');
});
});
describe('fsm.concat', function(){
it('expects an array', function(){
assert.throws(function(){
concat();
}, err => (err.toString().indexOf('Expected `fsms` to be an array of arrays')>=0));
assert.throws(function(){
concat([null]);
}, err => err.toString().indexOf('Expected `fsm` to be an Array')>=0);
assert.throws(function(){
concat([true]);
}, err => err.toString().indexOf('Expected `fsm` to be an Array')>=0);
});
it('∅∅', function(){
const fsm_a = [
new Node({}),
];
const fsm_b = [
new Node({}),
];
const fsm_ab = [
new Node({}),
];
assert.deepEqual(concat([fsm_a, fsm_b]), fsm_ab);
});
it('𝜀∅', function(){
const fsm_a = [
new Node({}, {}, true),
];
const fsm_b = [
new Node({}),
];
const fsm_ab = [
new Node({}),
];
assert.deepEqual(concat([fsm_a, fsm_b]), fsm_ab);
});
it('"a" concat "b"', function(){
const fsm_a = fromString('a', v=>({a:v}));
const fsm_b = fromString('b', v=>({b:v}));
const fsm_ab = [
new Node({a:1}, {a:0}),
new Node({b:2}, {b:0}),
new Node({}, {}, true),
];
assert.deepEqual(concat([fsm_a, fsm_b]), fsm_ab);
});
it('(_[_a]+){2}', function(){
const fsm_a = [
new Node({"_": 1}, {}, true),
new Node({"a": 1, "_":1}, {}, true),
];
const fsm_b = [
new Node({"_": 1}, {}, true),
new Node({"a": 1, "_":1}, {}, true),
];
const fsm_ab = [
new Node({"_": 1}, {}, true),
new Node({"a": 1, "_":1}, {}, true),
];
assert.deepEqual(concat([fsm_a, fsm_b]), fsm_ab);
});
});
describe('fsm.optional', function(){
it('optional', function(){
const fsm_a = [
new Node({a:1}),
new Node({}, {}, true),
];
const fsm_optional = optional(fsm_a);
assert(accepts(fsm_optional, ""));
assert(accepts(fsm_optional, "a"));
assert(!accepts(fsm_optional, "b"));
assert(!accepts(fsm_optional, "aaaaaaaaa"));
});
});
describe('fsm.star', function(){
it('star(a)', function(){
const fsm_a = [
new Node({a: 1}),
new Node({}, {}, true),
];
const fsm_star = star(fsm_a);
assert(accepts(fsm_star, ""));
assert(accepts(fsm_star, "a"));
assert(!accepts(fsm_star, "b"));
assert(accepts(fsm_star, "aaaaaaaaa"));
});
it('star(ab*)', function(){
const fsm_abstar = [
new Node({a:1}),
new Node({b:1}, {}, true),
];
assert(accepts(fsm_abstar, "a"));
assert(!accepts(fsm_abstar, "b"));
assert(accepts(fsm_abstar, "ab"));
assert(accepts(fsm_abstar, "abb"));
const fsm_abstarstar = star(fsm_abstar);
assert(accepts(fsm_abstarstar, "a"));
assert(!accepts(fsm_abstarstar, "b"));
assert(accepts(fsm_abstarstar, "ab"));
assert(accepts(fsm_abstarstar, "abb"));
assert(!accepts(fsm_abstarstar, "bb"));
assert(accepts(fsm_abstarstar, "aa"));
});
});
describe('fsm.reverse', function(){
it('ab', function(){
const fsm_ab = [
new Node({a:1}),
new Node({b:2}, {}),
new Node({}, {}, true),
];
const fsm_ba = [
new Node({b:1}),
new Node({a:2}, {}),
new Node({}, {}, true),
];
assert.deepEqual(reverse(fsm_ab), fsm_ba);
});
it('a?b', function(){
const fsm_ab = [
new Node({a:1, b:2}, {optional_a: 1}, false),
new Node({b:2}, {a: true}),
new Node({}, {b: true}, true),
];
const fsm_ba = [
new Node({b:1}),
new Node({a:2}, {}, true),
new Node({}, {}, true),
];
assert.deepEqual(reverse(fsm_ab), fsm_ba);
});
});
describe('fsm.reduce', function(){
it('eliminates extra states (1)', function(){
const fsm_abc = [
new Node({}),
new Node({}),
new Node({}),
];
assert.equal(fsm_abc.length, 3);
assert.equal(reduce(fsm_abc).length, 1);
});
it('eliminates extra states (2)', function(){
const fsm_abc = [
new Node({"a": 1, "b": 3}),
new Node({"a": 2, "b": 3}),
new Node({"a": 2, "b": 3}),
new Node({}, {}, true),
new Node({}),
];
assert.equal(fsm_abc.length, 5);
assert.equal(reduce(fsm_abc).length, 2);
});
it('eliminates shadowed transitions (a)', function(){
const fsm_a = [
new Node({a:1, b:1, c:1}),
new Node({}, {}, true),
];
const fsm_a0 = [
new Node({a:1, b:1, c:1}),
new Node({}, {}, true),
];
assert.deepEqual(reduce(fsm_a), fsm_a0);
});
it('eliminates shadowed transitions (b)', function(){
const fsm_a = [
new Node({'a-c':1}),
new Node({}, {}, true),
];
const fsm_a0 = [
new Node({'a-c':1}),
new Node({}, {}, true),
];
assert.deepEqual(reduce(fsm_a), fsm_a0);
});
});
describe('fsm.compare', function(){
it('disjoint', function(){
const fsm_a = [
new Node({'a':1, 'b':2}),
new Node({}, {}, true),
new Node({}, {}, false),
];
const fsm_b = [
new Node({'a':1, 'b':2}),
new Node({}, {}, false),
new Node({}, {}, true),
];
assert.deepEqual(compare([fsm_a, fsm_b]), [false, false, true]);
});
it('superset', function(){
const fsm_a = [
new Node({'a':1, 'b':2}),
new Node({}, {}, true),
new Node({}, {}, true),
];
const fsm_b = [
new Node({'a':1, 'b':2}),
new Node({}, {}, false),
new Node({}, {}, true),
];
assert.deepEqual(compare([fsm_a, fsm_b]), [true, false, false]);
});
it('subset', function(){
const fsm_a = [
new Node({'a':1, 'b':2}),
new Node({}, {}, true),
new Node({}, {}, false),
];
const fsm_b = [
new Node({'a':1, 'b':2}),
new Node({}, {}, true),
new Node({}, {}, true),
];
assert.deepEqual(compare([fsm_a, fsm_b]), [false, true, false]);
});
it('equal', function(){
const fsm_a = [
new Node({'a':1, 'b':2}),
new Node({}, {}, true),
new Node({}, {}, false),
];
const fsm_b = [
new Node({'a':1, 'b':2}),
new Node({}, {}, true),
new Node({}, {}, false),
];
assert.deepEqual(compare([fsm_a, fsm_b]), [true, true, false]);
});
it('partial overlap', function(){
const fsm_a = [
new Node({'a':1, 'b':2, 'c':3}),
new Node({}, {}, true),
new Node({}, {}, false),
new Node({}, {}, true),
];
const fsm_b = [
new Node({'a':1, 'b':2, 'c':3}),
new Node({}, {}, true),
new Node({}, {}, true),
new Node({}, {}, false),
];
assert.deepEqual(compare([fsm_a, fsm_b]), [false, false, false]);
});
});