ng-tql
Version:
A domain specific language that extends SQL and allows visual metaprogramming
167 lines (138 loc) • 6.03 kB
JavaScript
const expect = require('chai').expect;
const tql = require('../dist/tql');
describe('Parser specification', () => {
describe('Inferred type declarations', () => {
it('Shouldn\'t support free-type variables', () => {
expect(() => tql.parse('Declare Age')).to.throw(TypeError, /Cannot infer type of free variable/);
});
it('Should infer integers', () => {
const ast = tql.parse('Declare Age := 18');
expect(ast[0].type[0]).to.be.equal('int');
});
it('Should infer doubles', () => {
const ast = tql.parse('Declare Price := 10.5');
expect(ast[0].type[0]).to.be.equal('double');
});
it('Should infer symbols', () => {
const ast = tql.parse('Declare Table := STJT30');
expect(ast[0].type[0]).to.be.equal('symbol');
});
it('Should infer any char(*) to varchar', () => {
const ast = tql.parse('Declare Username := \'Admin\'');
expect(ast[0].type[0]).to.be.equal('varchar');
});
it('Should infer booleans', () => {
const ast = tql.parse('Declare ShouldUpdate := .F.');
expect(ast[0].type[0]).to.be.equal('bool');
});
});
describe('Type declarations' , () => {
it('Should allow every type to have defined variables without value', () => {
const source = [
'Declare Val: Int',
'Declare Name: VarChar',
'Declare When: Date',
'Declare Age: Nat',
'Declare Trigger: Bool',
'Declare Table: Symbol',
'Declare Id: Char(32)',
'Declare Limit: Range(10, 100)',
'Declare Price: Double'
];
tql.parse(source.join('\n'));
});
it('Should restrict negative numbers on Nat', () => {
expect(() => {
tql.parse('Declare Age: Nat := -10');
}).to.throw(TypeError, /Value of type `int\(-10\)' is not assignable to field of type `nat'/);
});
it('Should restrict max length of string in Char(*)', () => {
expect(() => {
tql.parse('Declare Val: Char(10) := \'Trixie Mattel\'');
}).to.throw(TypeError, /Value of type `string\(13\)' is not assignable to field of type `char\(10\)'/);
});
it('Should restrict max number in Range(*, *)', () => {
expect(() => {
tql.parse('Declare Val: Range(1, 10) := 11');
}).to.throw(TypeError, /Value of type `int\(11\)' is not assignable to field of type `range\(1, 10\)'/);
});
it('Should restrict min number in Range(*, *)', () => {
expect(() => {
tql.parse('Declare Val: Range(1, 10) := -20');
}).to.throw(TypeError, /Value of type `int\(-20\)' is not assignable to field of type `range\(1, 10\)'/);
});
});
describe('Duplication', () => {
it('Doesn\'t allow duplicated fields', () => {
const source = [
'Declare age := 18',
'Declare Age := .T.'
];
expect(() => {
tql.parse(source.join(''));
}).to.throw(Error, /Field `AGE' declared twice/);
});
});
describe('Picture', () => {
it('Allows pictures after type declaration', () => {
tql.parse('Declare Price: Nat Picture \'99.99\' := 10');
});
it('Allows pictures after name declaration', () => {
tql.parse('Declare Price Picture \'99.99\' := 10');
});
});
describe('Description', () => {
it('Allows descriptions to be placed after all', () => {
tql.parse('Declare Table: Symbol := STJT30 { Table to select }');
});
it('Allows descriptions with line break', () => {
tql.parse('Declare Table: Symbol := STJT30 { Table\n to select }');
});
});
describe('Dates', () => {
it('Should allow a date to be assigned to Date', () => {
tql.parse('Declare BirthDay: Date := 4 Dec 1996');
});
it('Should reject negative day', () => {
expect(() => {
tql.parse('Declare BirthDay := -4 Dec 1996');
}).to.throw(Error, /Day of date must be positive/);
});
it('Should reject negative year', () => {
expect(() => {
tql.parse('Declare BirthDay := 4 Dec -1996');
}).to.throw(Error, /Year of date must be positive/);
});
it('Should allow seconds on datetime', () => {
tql.parse('Declare ExactMoment := 7 Nov 2016 13:43:32');
});
it ('Should allow a datetime to be assigned to Date', () => {
tql.parse('Declare Event: Date := 7 Nov 2016 13:30');
});
it('Should validate hours', () => {
expect(() => {
tql.parse('Declare Event: Date := 7 Nov 2016 -13:00');
}).to.throw(Error, /Hour of time must be between 0 and 23/);
});
it('Should validate minutes', () => {
expect(() => {
tql.parse('Declare Event: Date := 7 Nov 2016 13:78');
}).to.throw(Error, /Minute of time must be between 0 and 59/);
});
it('Should validate seconds', () => {
expect(() => {
tql.parse('Declare Event: Date := 7 Nov 2016 13:10:61');
}).to.throw(Error, /Seconds of time must be between 0 and 59/);
});
});
describe('Syntax', () => {
it('Should be case insensitive for keywords', () => {
const lower = tql.parse('declare X: int := 1');
const upper = tql.parse('Declare X: Int := 1');
expect(lower).to.be.deep.equal(upper);
});
it('Shouldn\'t care about whitespace', () => {
tql.parse(' declare Foo:Date:=18 Dec 1887 ');
});
});
});