dtl-js
Version:
Data Transformation Language - JSON templates and data transformation
639 lines (610 loc) • 37.7 kB
JavaScript
/* =================================================
* Copyright (c) 2015-2020 Jay Kuri
*
* This file is part of DTL.
*
* DTL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* DTL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with DTL; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* =================================================
*/
var assert = require('assert');
var util = require('util');
var DTL = require('../lib/DTL.js');
var container = {
"$foo": "william",
ctx: { "foo": 72, 'request': { 'origin': { 'detail': { port: 25 }}}},
meta: { "bob":10, "john":22, "will":"no", "deep": { "things": 22 },
"null": null,
"undef": undefined,
"boolean": true,
"foo,bar": "foobar",
"$foo": "foobar",
"reg": "foo",
"number": 4, // guaranteed to be random: chosen by fair dice roll
"@unusual:": 'is ok',
"obj_list" : {
'fff' : {
'name': 'fff',
},
'abc' : {
'name': 'abc'
},
'zz' : {
'name': 'zz',
},
'ccf' : {
'name': 'ccf',
'bar': null
}
},
"sortable" : [
{ 'name' : 'fff'},
{ 'name' : 'ccc'},
{ 'name' : 'ddd'},
{ 'name' : 'aaa'},
{ 'name' : 'cmy'},
null
],
"sortable_numeric" : [
{ 'val' : 10},
{ 'val' : 120},
{ 'val' : 300},
{ 'val' : 700},
{ 'val' : 111},
{ 'val' : 11},
{ 'val' : 22},
{ 'val' : null}
],
"filter_me" : {
name : 'a name',
foo : 'a foo',
url : 'http://foo.bar.com/?x=1&m=500'
},
"json_test": {
"name": "testy",
"value": undefined
},
"numbers": ['1','2','3','4','5'],
"letters": [ '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ],
"sub_numbers": [1, 2, 3, [4, 5]],
"deep_numbers": [1, 2, 3, [4, 5, [6, 7]], [9, 12, [22, [33, [45,63,[88,12]]]]]],
"url": "http://foo.bar.com/fuzzy/about.php",
"long_url" : 'http://foo.bar.com:900/path/to/do?a=1',
"encode_me" : "hello world",
"long_path" : 'the/long/path/to/do/the/thing',
"thing": "port",
"this.thing": 173,
"number_thing": "192381.110000001",
"list": ["bob", "john", "will", "shallow"],
"Step": 3,
"content-type": 'text/plain',
"pair": ["the_key", "the_value"],
"pairs": [["key2", "value2"],["key3", "value3"]],
"key": { is: 'meta.deep.things' },
"phone" : "303-554-9000",
"not_true" : false,
"wacky_key": '[foo].baz',
"wacky": {
"foo+bar": "one",
"foo<bar": "two",
"foo-bar": "three",
"foo..bar": "four",
"12..18": "five",
"foo=>bar": "six",
"foo]bar": "seven",
"foo].bar": "eight",
"[foo].baz": "nine"
}
}
};
var tests = [
{ to_parse: '99', result: 99},
{ to_parse: '98.6', result: 98.6},
{ to_parse: '+98.6', result: 98.6},
{ to_parse: '-98.6', result: -98.6},
{ to_parse: '0x84', result: 132 },
{ to_parse: '0xFF', result: 255 },
{ to_parse: '0xff', result: 255 },
{ to_parse: '0xF0', result: 240 },
{ to_parse: '0xf0', result: 240 },
{ to_parse: '-0xf0', result: -240 },
{ to_parse: '0x8abcdef', result: 145477103 },
{ to_parse: '0x0abDF0', result: 703984 },
{ to_parse: '-0x3fedcba', result: -67034298 },
{ to_parse: '0o2', result: 2 },
{ to_parse: '0o23', result: 19 },
{ to_parse: '-0o23', result: -19 },
{ to_parse: '0o755', result: 493 },
{ to_parse: '0o1323', result: 723 },
{ to_parse: '0b10', result: 2 },
{ to_parse: '0b101', result: 5 },
{ to_parse: '0b101010', result: 42 },
{ to_parse: '"double quoted"', result: "double quoted" },
{ to_parse: "'single quoted'", result: "single quoted" },
{ to_parse: '`back quoted`', result: "back quoted" },
{ to_parse: '27 * 1', result: 27 },
{ to_parse: '26 + 1', result: 27 },
{ to_parse: '26 - 1', result: 25 },
{ to_parse: '26 / 2', result: 13 },
{ to_parse: '-12', result: -12},
{ to_parse: '2 - (12)', result: -10},
{ to_parse: '2 - 11', result: -9},
{ to_parse: '(-12)', result: -12},
{ to_parse: '2 - (12)', result: -10},
{ to_parse: '2 - (-12)', result: 14},
{ to_parse: '2 - -12', result: 14},
{ to_parse: '2--12', result: 14},
{ to_parse: '2 + +12', result: 14},
{ to_parse: '2-+98.6', result: -96.6},
{ to_parse: '2+-98.6', result: -96.6},
{ to_parse: '2 * -12', result: -24},
{ to_parse: '(27 * 1) == (26 + 1)', result: true },
{ to_parse: '(13 * 2 * 2) == 52', result: true },
{ to_parse: '(13 * 2 + 7)', result: 33 },
{ to_parse: '(7 + 2 * 13)', result: 33 },
{ to_parse: '13 * (2 + 7)', result: 117 },
{ to_parse: '7 + (2 * 13)', result: 33 },
{ to_parse: '(2 + 7) * 13', result: 117 },
{ to_parse: '(2 * 13) + 7', result: 33 },
{ to_parse: '((2 * 13) + 7)', result: 33 },
{ to_parse: '6 < 7', result: true},
{ to_parse: '6 <= 6', result: true},
{ to_parse: '17.5 >= 17', result: true},
{ to_parse: '6 <= 2 * 3', result: true},
{ to_parse: '7 > (2+3) == true', result: true},
{ to_parse: '2 + 7 + 9 * 3 + 2', result: 38 },
{ to_parse: '2 * 7 + 9 * 3 + 2', result: 43 },
{ to_parse: '2 * 7 + 9 / 3 - 2', result: 15 },
{ to_parse: '2 ^ 3', result: 8 },
{ to_parse: '2 ^ 3 * 2', result: 16 },
{ to_parse: '2 ^ 3 * 2', result: 16 },
{ to_parse: '17 % 3', result: 2 },
{ to_parse: '2 + 7 + (9 * 3) + 2', result: 38 },
{ to_parse: '(2 * 7) + (9 * 3) + 2', result: 43 },
{ to_parse: '2 + (7 + 9) * 3 + 2', result: 52 },
{ to_parse: '2 * (7 + 9) * (3 + 2)', result: 160 },
{ to_parse: '(2 * (7 + 9) * (3 + 2)) == 160', result: true },
{ to_parse: '2 * (7 + 9) * (3 + 2) == 160', result: true },
{ to_parse: '2 + 2 == 4', result: true },
{ to_parse: '2 * 3 == 6', result: true },
{ to_parse: '2 <=> 3', result: -1 },
{ to_parse: '3 <=> 2', result: 1 },
{ to_parse: '3 <=> 3', result: 0},
{ to_parse: '"apple" <=> "apple"', result: 0},
{ to_parse: '"bob" <=> "alice"', result: 1},
{ to_parse: '"alligator" <=> "zebra"', result: -1},
{ to_parse: '"a" <=> 9', result: 1},
{ to_parse: '9 <=> "a"', result: -1},
{ to_parse: '9.012 <=> "9"', result: 1}, // can't fool me.
{ to_parse: '$meta.bob + 7 == 17', result: true },
{ to_parse: '$meta.bob + 2 * 2 == 14', result: true },
{ to_parse: '"bob" !== "boob"', result: true},
{ to_parse: '$meta.bob == 10', result: true },
{ to_parse: '$meta.bob === 10', result: true },
{ to_parse: '120 / $meta.bob ', result: 12 },
{ to_parse: '"bob" & "boob"', result: "bobboob"},
{ to_parse: '$meta.deep.things == 22', result: true },
{ to_parse: '$meta.deep.things != 26', result: true },
{ to_parse: '$meta.nonexistant.things == undefined', result: true },
{ to_parse: '$meta.nonexistant.things == undef', result: true },
{ to_parse: 'exists($meta.nonexistant.things)', result: false },
{ to_parse: 'exists($meta.deep.things)', result: true },
{ to_parse: 'exists($meta.deep.fiddle)', result: false },
{ to_parse: '((27 * 1) == (26 + 1)) == true', result: true },
{ to_parse: '$meta.bob', result: 10 },
{ to_parse: '$ctx.foo', result: 72 },
{ to_parse: '( $meta.bob + 17 ) == 27', result: true},
{ to_parse: '( $meta.bob *2 ) == 20', result: true},
{ to_parse: '!(!$meta.not_true)', result: false},
{ to_parse: '!(!$meta.Step)', result: true},
{ to_parse: '( 10 + 17 ) == 27', result: true},
{ to_parse: '( $meta.will ) == "no"', result: true},
{ to_parse: '(( $meta.will )) == "no"', result: true},
{ to_parse: '(( $meta.will ) != "no")', result: false},
{ to_parse: 'undef == undefined', result: true },
{ to_parse: 'true != false', result: true },
{ to_parse: 'true == true && 17 == 17', result: true },
{ to_parse: 'true == true && 17 == 12', result: false },
{ to_parse: '17 + 2 == 19 && 12 * 2 == 24', result: true},
{ to_parse: '17 + 2 == 11 || 12 * 2 == 24', result: true},
{ to_parse: '17 + 2 == 11 || 12 * 2 == 26 || $meta.will == "no"', result: true},
{ to_parse: '17 + 2 == 19 && $ctx.foo * 2 == 144', result: true},
{ to_parse: '17 + 2 == 19 && 12 * 2 == 24', result: true},
{ to_parse: '(17 + 2 == 19 && 12 * 2 == 26) || $meta.will == "no"', result: true},
{ to_parse: '!true', result: false},
{ to_parse: '!(11 == 17)', result: true},
{ to_parse: '!(17 + 2 == 19 && 12 * 2 == 26) || $meta.will != "no"', result: true},
{ to_parse: "undefined || '' || $meta.will", result: "no"},
{ to_parse: "'ctx.config'", result: "ctx.config" },
{ to_parse: "'%foo'", result: "%foo" },
{ to_parse: "'.FileSpec'", result: ".FileSpec"},
{ to_parse: "uc('.FileSpec')", result: ".FILESPEC"},
{ to_parse: "lc('.FileSpec')", result: ".filespec"},
{ to_parse: "'>foo'", result: ">foo"},
{ to_parse: "'<foo'", result: "<foo"},
{ to_parse: "'=foo'", result: "=foo"},
{ to_parse: "'!foo'", result: "!foo"},
{ to_parse: "'^foo'", result: "^foo"},
{ to_parse: "'foo>'", result: "foo>"},
{ to_parse: '"<img "', result: "<img "},
{ to_parse: '"26 / 2"', result: "26 / 2" },
{ to_parse: '"foo"', result: "foo"},
{ to_parse: '"foo>s"', result: "foo>s"},
{ to_parse: '$meta.will', result: "no"},
{ to_parse: 'x${meta.will}', result: "xno"},
{ to_parse: '"no"', result: "no"},
{ to_parse: '"I\\\'ll see"', result: "I'll see"},
{ to_parse: "'I\\'ll see'", result: "I'll see"},
{ to_parse: "'I\\\"ll see'", result: 'I"ll see'},
{ to_parse: '"I\\"ll see"', result: 'I"ll see'},
{ to_parse: "($meta.port || $ctx.request.origin.detail.port || 80)", result: 25},
{ to_parse: "($meta.host || $ctx.request.origin.detail.host || 'www.cnn.com')", result: 'www.cnn.com'},
{ to_parse: "length(' ') && length(' ') && length('')", result: 0},
{ to_parse: "length($meta.undef)", result: 0},
{ to_parse: "length($meta.null)", result: 0},
{ to_parse: "length($meta.number)", result: 1},
{ to_parse: "length($meta.boolean)", result: 1},
{ to_parse: "length($meta.obj_list)", result: 4},
{ to_parse: "( '\\(hi\\)' == '(hi)')", result: true},
{ to_parse: "'tHing' =~ /hin/i", result: true},
{ to_parse: "'tHing' =~ /h.*g/i", result: true},
{ to_parse: "'tHing' =~ /h.*l/i", result: false},
{ to_parse: "('tHing' =~ /h.*g/i) && (18 > 9)", result: true},
{ to_parse: "('tHing' =~ /h.*l/i) || (18 + 9)", result: 27},
{ to_parse: "'/mydata.html' =~ m/(receptor_list|user_config|logview|mydetails|mydata)/", result: true},
{ to_parse: "'/mydata.html' =~ /(receptor_list)|(user_config)|(logview)|(mydetails)|(mydata)/", result: true},
{ to_parse: "'/mydata.html' =~ regex('(receptor_list)|(user_config)|(logview)|(mydetails)|(mydata)')", result: true},
{ to_parse: "'/mydata.html' =~ regex(join(['receptor_list' 'user_config' 'logview' 'mydetails' 'mydata'] '|'))", result: true},
{ to_parse: "'application/json' =~ /application\\/json/", result: true },
{ to_parse: "'{ foo: 17 }' =~ m/{.*}/", result: true},
{ to_parse: "'foobar' =~ m/{.*}/", result: false},
{ to_parse: "'\\$meta.will'", result: "$meta.will"},
{ to_parse: "${meta.will}", result: "no"},
{ to_parse: "$199.22", result: undefined},
{ to_parse: "'99.22%'", result: "99.22%"},
{ to_parse: "typeof(99.22)", result: "number"},
{ to_parse: "typeof($ctx.request)", result: "object"},
{ to_parse: "'$b0b'", result: "$b0b"},
{ to_parse: "$b0b", result: undefined},
{ to_parse: "$$foo", result: "william"},
{ to_parse: "$['$foo']", result: "william"},
{ to_parse: "$.$foo", result: "william"},
{ to_parse: "$meta.$foo", result: "foobar"},
{ to_parse: "$meta['$foo']", result: "foobar"},
{ to_parse: 'length($meta.will)', result: 2},
{ to_parse: 'length(random_string("aAa!.####")) ', result: 9},
// check comments
{ to_parse: '/* inline comment */', result: undefined },
{ to_parse: '"hi" /* inline comment */', result: "hi" },
{ to_parse: '$meta.will /* inline comment */', result: "no" },
{ to_parse: '$meta.boolean /* inline comment */', result: true },
{ to_parse: '$meta.boolean /* false */', result: true },
{ to_parse: '$meta.boolean /* && @(false) */', result: true },
{ to_parse: '/* false && */ $meta.boolean', result: true },
{ to_parse: '/* inline comment */ $meta.will', result: "no" },
{ to_parse: '// eol comment', result: undefined },
{ to_parse: '"hi"// eol comment', result: "hi" },
{ to_parse: '"hi" // eol comment', result: "hi" },
{ to_parse: '$meta.will // eol comment', result: "no" },
{ to_parse: "['a' 'b' 'foo'] // eol comment", result: [ 'a', 'b', 'foo']},
{ to_parse: "['a' 'b' 'foo']// eol comment", result: [ 'a', 'b', 'foo']},
{ to_parse: "['a' /* inline comment */ 'b' 'foo']", result: [ 'a', 'b', 'foo']},
{ to_parse: "( 'foo' /* inline comment #8 */)", result: "foo"},
{ to_parse: "&( 'a' 'b' 'foo' /* inline comment #9 */)", result: "abfoo"},
{ to_parse: "&( 'a' 'b' 'foo') // eol comment", result: "abfoo"},
// todo - add more random_string checks! including providing charmap
{ to_parse: "( 'foo' )", result: "foo"},
{ to_parse: "' foo '", result: ' foo '},
{ to_parse: '$meta.will & "bob"', result: "nobob"},
{ to_parse: '$meta.nonexistant & "bob"', result: "bob"},
{ to_parse: '&($meta.will "bob")', result: "nobob"},
{ to_parse: '&($meta.nonexistant "bob")', result: "bob"},
{ to_parse: '$meta.url', result: "http://foo.bar.com/fuzzy/about.php"},
{ to_parse: '${meta.url}', result: "http://foo.bar.com/fuzzy/about.php"},
{ to_parse: '$meta.url == "http://foo.bar.com/fuzzy/about.php"', result: true},
{ to_parse: '`$meta.url`', result: "$meta.url"},
{ to_parse: '"[foo]"', result: "[foo]"},
{ to_parse: '"request"', result: "request"},
{ to_parse: '"reques[]"', result: "reques[]"},
{ to_parse: '"bob" == `bob`', result: true},
{ to_parse: '`foo` & `bob & "182" \\` / 19 == 22`', result: 'foobob & "182" ` / 19 == 22'},
{ to_parse: "`foo` & 'bob & 182 \\' / 19 == 22'", result: "foobob & 182 ' / 19 == 22"},
{ to_parse: ' "$meta.will" & `bob`', result: "$meta.willbob"},
{ to_parse: " '$meta.will' & `bob`", result: "$meta.willbob"},
// { to_parse: "`foo` _ '$meta.will' _ `bob`", result: "foo$meta.willbob"},
{ to_parse: "'foo' & '$meta.will' & `bob`", result: "foo$meta.willbob"},
{ to_parse: "&('foo' '$meta.will' `bob`)", result: "foo$meta.willbob"},
{ to_parse: " `$meta.will` & `bob`", result: "$meta.willbob"},
{ to_parse: ' "will + bob"', result: "will + bob"},
{ to_parse: "$ctx.request.origin.detail[($meta.thing)]", result: 25},
{ to_parse: "$ctx.request.origin.detail[$meta.thing]", result: 25},
{ to_parse: "$ctx['request'].origin.detail[$meta.thing]", result: 25},
{ to_parse: "$ctx['request']['origin']['detail'][$meta.thing]", result: 25},
{ to_parse: "$ctx[request].origin.detail[$meta.thing]", result: 25},
{ to_parse: "$meta.list[2]", result: "will"},
{ to_parse: "$meta.list[$meta['Step']]", result: "shallow"},
{ to_parse: "$meta.list[$meta[Step]]", result: "shallow"},
{ to_parse: "$meta.list[$meta.Step]", result: "shallow"},
{ to_parse: "$meta.list[$meta.step]", result: undefined},
{ to_parse: "$meta.list[${meta.Step}]", result: "shallow"},
{ to_parse: "${meta.list[${meta.Step}]}", result: "shallow"},
{ to_parse: "$meta.wacky.'foo+bar'", expect_exception: true},
{ to_parse: "$meta.wacky['foo+bar']", result: "one"},
{ to_parse: "$meta.wacky['foo<bar']", result: "two"},
{ to_parse: "$meta.wacky['foo-bar']", result: "three"},
{ to_parse: "$meta.wacky['foo..bar']", result: "four"},
{ to_parse: "$meta.wacky[['foo..bar']]", result: "four"},
{ to_parse: "$meta.wacky['12..18']", result: "five"},
{ to_parse: "$meta.wacky['foo=>bar']", result: "six"},
{ to_parse: "$meta.wacky['foo]bar']", result: "seven"},
{ to_parse: "$meta.wacky['foo].bar']", result: "eight"},
{ to_parse: "$meta.wacky['[foo]\.baz']", result: "nine"},
{ to_parse: "$meta.wacky[$meta.wacky_key]", result: "nine"},
{ to_parse: "url_encode($meta.long_url)", result: "http%3A%2F%2Ffoo.bar.com%3A900%2Fpath%2Fto%2Fdo%3Fa%3D1"},
{ to_parse: "url_decode('pack-%3Ewindowmemory%3D200m')", result: "pack->windowmemory=200m"},
{ to_parse: "#('25')", result: 25},
{ to_parse: "num('25')", result: 25},
{ to_parse: "#('25.0')", result: 25},
{ to_parse: "num('25.0')", result: 25},
{ to_parse: "#('25.623')", result: 25.623},
{ to_parse: "num('25.623')", result: 25.623},
{ to_parse: "#('25.623')", result: 25.623},
{ to_parse: "num('24.7a')", result: 24.7},
{ to_parse: "#('24.7a')", result: 24.7},
{ to_parse: "num($meta.numbers[0])", result: 1},
{ to_parse: "#($meta.numbers[0])", result: 1},
{ to_parse: "num('val')", result: undefined},
{ to_parse: "#('b0b')", result: undefined},
{ to_parse: "num($meta.number_thing)", result: 192381.110000001},
{ to_parse: "#($meta.number_thing)", result: 192381.110000001},
{ to_parse: "num($meta.number_thing & 1)", result: 192381.1100000011},
{ to_parse: "#($meta.number_thing & 1)", result: 192381.1100000011},
{ to_parse: "num($meta.nonexistant)", result: undefined},
{ to_parse: "#($meta.nonexistant)", result: undefined},
{ to_parse: "num($meta.numbers)", result: undefined},
{ to_parse: "#($meta.numbers)", result: undefined},
{ to_parse: "tofixed(9.238123 3)", result: 9.238},
{ to_parse: "tofixed(19.200001 2)", result: 19.2},
{ to_parse: "tofixed(9.238123 3)", result: 9.238},
{ to_parse: "tofixed(0.194 2)", result: 0.19},
{ to_parse: "tofixed(0.199 2)", result: 0.2},
{ to_parse: "tofixed(0.199 3)", result: 0.199},
{ to_parse: "tofixed(9.238123 3)", result: 9.238},
{ to_parse: "tofixed(9.1 3)", result: 9.1},
{ to_parse: "$ctx[request].origin.detail[$meta.thing]", result: 25},
{ to_parse: "$ctx[request].origin.detail[$meta.thing]", result: 25},
{ to_parse: "$ctx[request].origin.detail[$meta.thing]", result: 25},
{ to_parse: "$ctx[request].origin.detail[$meta.thing]", result: 25},
// { to_parse: "${meta.list[${meta.Step}]}", result: "shallow"},
// { to_parse: "${meta.list[${meta.Step}]}", result: "shallow"},
{ to_parse: "&($meta.list 'Robert')", result: ["bob", "john", "will", "shallow", "Robert"]},
{ to_parse: "join($meta.list ', ')", result: "bob, john, will, shallow"},
{ to_parse: "join(head($meta.sub_numbers 3) ':')", result: "1:2:3"},
// appending to nonexistant equals an array with just the appended stuff there.
{ to_parse: "&($meta.nonexist [] 'bob' )", result: ['bob']},
{ to_parse: "&($meta.nonexist ['bob' 'baz' 'bat'] )", result: ['bob', 'baz', 'bat']},
{ to_parse: "&($meta.list ['bill' 'jim'])", result: ['bob', "john", "will", "shallow", 'bill', 'jim']},
{ to_parse: "&($meta.filter_me $meta.key)", result: { name: 'a name', foo: 'a foo', url: 'http://foo.bar.com/?x=1&m=500', is: 'meta.deep.things' }},
{ to_parse: "&({} $meta.filter_me $meta.key)", result: { name: 'a name', foo: 'a foo', url: 'http://foo.bar.com/?x=1&m=500', is: 'meta.deep.things' }},
{ to_parse: "&({} $meta.null $meta.key [ 'bob' ])", result: { '0': 'bob', is: 'meta.deep.things' }},
{ to_parse: "&({} $meta.key)", result: { is: 'meta.deep.things' }},
{ to_parse: "&({} $meta.list 'bob' 'frank' 'howard')", result: { '0': 'bob', '1': 'john', '2': 'will', '3': 'shallow', bob: 'bob', frank: 'frank', howard: 'howard' }},
{ to_parse: "&($meta.nonexist $meta.key)", result: { is: 'meta.deep.things' }},
{ to_parse: "&([] $meta.nonexist $meta.key)", result: [ { is: 'meta.deep.things' } ]},
{ to_parse: "['groups' &([] $meta.numbers)]", result: [ 'groups', [1, 2, 3, 4, 5] ] },
{ to_parse: "['groups' (&([] $meta.numbers))]", result: [ 'groups', [1, 2, 3, 4, 5] ] },
//{ to_parse: "&($meta.list ['bill' 'jim'])[2]", result: ['bob', "john", "will", "shallow", 'bill', 'jim']},
{ to_parse: "$meta.asdallf", result: undefined},
{ to_parse: "empty($meta.foo)", result: true},
{ to_parse: "empty($meta.list)", result: false},
{ to_parse: "empty(\"\")", result: true},
{ to_parse: "!empty(\"\")", result: false},
{ to_parse: "$meta['content-type']", result: 'text/plain'},
{ to_parse: "$meta['content-type'] =~ /text\\/plain/", result: true},
{ to_parse: "$meta.mime", result: undefined},
{ to_parse: '$meta.nonexistant.things', result: undefined },
{ to_parse: "$meta['this.thing']", result: 173 },
{ to_parse: "$meta['foo,bar']", result: 'foobar'},
{ to_parse: '${meta}', result: container.meta},
{ to_parse: "$[split($meta.key.is '.')]", result: 22},
{ to_parse: '$[[$meta.key.is]]', result: 22},
// { to_parse: '${$meta.key.is}', result: '22'},
{ to_parse: "uuid()", regex: "^[0-9a-z]{8,8}\-[0-9a-z]{4,4}\-[0-9a-z]{4,4}\-[0-9a-z]{4,4}\-[0-9a-z]{12,12}$"},
//{ to_parse: 'A bunch of things', result: "A bunch of things" },
{ to_parse: '( "/index.html" =~ m/^\\/static/ )', result: false },
// { to_parse: "empy($meta.foo)", result: true},
{ to_parse: ' "will" & `bob`', result: "willbob"},
{ to_parse: "to_base64( &($meta.will ':' $meta.will) )", result: 'bm86bm8=' },
// { to_parse: "sha1($meta.encode_me)", result: "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"},
// { to_parse: "md5($meta.encode_me)", result: "5eb63bbbe01eeed093cb22bb8f5acdc3"},
{ to_parse: "hash('sha1' $meta.encode_me)", result: "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"},
{ to_parse: "hash('md5' $meta.encode_me)", result: "5eb63bbbe01eeed093cb22bb8f5acdc3"},
{ to_parse: "hash('foobar' $meta.encode_me)", result: undefined},
// { to_parse: "clone($ctx.request)", result: container.ctx.request},
{ to_parse: "to_json($ctx.request)", result: '{"origin":{"detail":{"port":25}}}'},
{ to_parse: "to_json({ ['foo' undef ]['bar' 1]} 2)", result: "{\n \"bar\": 1\n}" },
{ to_parse: "to_json({ ['foo' undef ]['bar' 1]} ' ')", result: "{\n \"bar\": 1\n}" },
{ to_parse: "to_json($meta.json_test)", result: '{"name":"testy"}'},
{ to_parse: "to_json($meta.json_test undef true)", result: '{"name":"testy","value":null}'},
{ to_parse: "from_json('{\"origin\": { \"detail\": { \"port\": 25 }}}')", result: {"origin": { "detail": { port: 25 }}}},
{ to_parse: "substr($meta.nonexistant 0 3)", result: undefined},
{ to_parse: "substr($meta.long_path 0 3)", result: 'the'},
{ to_parse: "substr($meta.long_path 4 8)", result: 'long'},
{ to_parse: "?( ($meta.deep.things == 22) 'it is true' 'or not')", result: 'it is true'},
{ to_parse: "?( ($meta.deep.things != 22) 'it is true' 'or not')", result: 'or not'},
{ to_parse: "?( ($meta.deep.things == 22) 'it is true' fooble('wizywit'))", result: 'it is true'},
{ to_parse: "fne('' 'bob')", result: 'bob'},
{ to_parse: "substr($meta.long_path 4)", result: 'long/path/to/do/the/thing'},
{ to_parse: "substr($meta.long_path, 4)", result: 'long/path/to/do/the/thing'},
{ to_parse: "replace('foobaOr' '/o/ig' '00')", result: "f0000ba00r"},
{ to_parse: "replace('foobaOr' /o/ig '00')", result: "f0000ba00r"},
{ to_parse: "replace('foobar' '/o/' '00')", result: "f00obar"},
{ to_parse: "replace('foobar' /o/ '00')", result: "f00obar"},
{ to_parse: "replace('fOobar' '/o/g' '00')", result: "fO00bar"},
{ to_parse: "replace('fOobar' /o/g '00')", result: "fO00bar"},
{ to_parse: "match('foobar' '/c,d/')", result: []},
{ to_parse: "match('foobar' '#c,d#')", result: []},
{ to_parse: "match('foobar' regex($meta.reg))", result: ['foo']},
{ to_parse: "empty(match('foobar' '/c,d/'))", result: true},
{ to_parse: "'foobar' =~ m/c,d/", result: false},
{ to_parse: "match('FOobar' '/foo/i')", result: ['FOo'] },
{ to_parse: "match('FOobar' ['foo' 'i'])", result: ['FOo'] },
{ to_parse: "match('FOobar' regex($meta.reg))", result: [] },
{ to_parse: "match('FOobar' regex($meta.reg 'i'))", result: ['FOo'] },
{ to_parse: "match('FOobar' regex(&('Oo' '.')))", result: ['Oob'] },
{ to_parse: "match('FOobar' regex(&('^Oo' '.')))", result: [] },
{ to_parse: "match('FOobar' regex(&('^F.' 'o')))", result: ['FOo'] },
// making a regex from a non-string is unmatchable
{ to_parse: "match('FOobar' regex($meta 'i'))", result: [] },
{ to_parse: "match('FOobar' ['foo' 'u'])", result: [] },
{ to_parse: "'FOobar' =~ m/foo/i", result: true},
{ to_parse: "'FOobar' =~ regex($meta.reg 'i')", result: true},
{ to_parse: "'FOobar' =~ regex($meta.reg)", result: false},
{ to_parse: "!empty(match('FOobar' '/foo/i'))", result: true},
{ to_parse: "!empty(match('LOobar' '/foo/i'))", result: false},
{ to_parse: "match('foobar' '/([f,b])(.)/')", result: [ 'fo', 'f', 'o' ]},
{ to_parse: "match('foobarbaz' '/(f(..)bar)../')", result: [ 'foobarba', 'foobar','oo' ]},
{ to_parse: "replace('foobar' '/([f,b])(.)/g' '$2$1')", result: "ofoabr"},
{ to_parse: "replace('foobar' /([f,b])(.)/g '$2$1')", result: "ofoabr"},
{ to_parse: "! $meta.not_true", result: true },
{ to_parse: "$meta.not_true == false", result: true },
{ to_parse: "$meta.not_true == 'false'", result: false },
{ to_parse: "!empty($meta.phone) && $meta.not_true == false", result: true },
{ to_parse: "!empty($meta.phone) && $meta.not_true", result: false },
{ to_parse: "!empty($meta.zeebraeoadfs) && $meta.not_true != false ", result: false },
{ to_parse: "!empty($meta.zeebraeoadfs) && $meta.not_true == false", result: false },
{ to_parse: "$meta.numbers[$meta.deep.things - 24]", result: '4' },
{ to_parse: "$.", result: container },
{ to_parse: "$meta['will']", result: 'no'},
{ to_parse: "capitalize('bubbaBoBobBrain')", result: 'BubbaBoBobBrain'},
// Arrays & objects constructors
{ to_parse: "['foo' 'bar']", result: ['foo', 'bar'] },
{ to_parse: "('foo':'bar')", result: ['foo', 'bar'] },
{ to_parse: "('foo' 'bar')", result: ['foo', 'bar'] },
{ to_parse: "{['foo' 'bar']}", result: {'foo': 'bar'} },
{ to_parse: "['foo', 'bar']", result: ['foo', 'bar'] },
{ to_parse: "[ 12 25 ]", result: [12, 25] },
{ to_parse: "[ 12, -25 ]", result: [12, -25] },
{ to_parse: "[ 12 -25 ]", result: [-13] },
{ to_parse: "0..5", result: [0,1,2,3,4,5]},
{ to_parse: "5..0", result: [5,4,3,2,1,0]},
{ to_parse: "0..20:2", result: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ]},
{ to_parse: "20..0:2", result: [20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0]},
{ to_parse: "$meta.bob..20:3", result: [ 10, 13, 16, 19 ]},
{ to_parse: "0..$meta.bob:2", result: [ 0, 2, 4, 6, 8, 10 ]},
{ to_parse: "$meta.Step+1..$meta.bob:2", result: [ 4, 6, 8, 10 ]},
{ to_parse: "$meta.bob..($meta.Step+3)", result: [10, 9, 8, 7, 6] },
{ to_parse: "$meta['@unusual:']", result: 'is ok'},
{ to_parse: "{ $meta.pair }", result: { 'the_key': "the_value"}},
{ to_parse: "{ $meta.pairs }", result: { 'key2': "value2", 'key3': "value3"}},
{ to_parse: "pairs({ $meta.pairs })", result: [['key2', 'value2'], ['key3', 'value3']] },
{ to_parse: "{['foo' 'bar'] ['baz' 'bat']}", result: {'foo': 'bar', 'baz': 'bat'} },
{ to_parse: "{('foo': 'bar') ('baz': 'bat')}", result: {'foo': 'bar', 'baz': 'bat'} },
{ to_parse: "{['foo' 'bar'] [$meta.will 'way']}", result: {'foo': 'bar', 'no': 'way'} },
{ to_parse: "{('foo': 'bar') ($meta.will: 'way')}", result: {'foo': 'bar', 'no': 'way'} },
{ to_parse: "{('foo': {($meta.will: 'way')})}", result: {'foo': {'no': 'way'}} },
{ to_parse: "('foo' 'bar' 'baz' 'bat')", result: ['foo', 'bar', 'baz', 'bat'] },
{ to_parse: "reverse($meta.numbers)", result: [5,4,3,2,1]},
{ to_parse: "reverse('a cat without a grin')", result: 'nirg a tuohtiw tac a'},
{ to_parse: "flatten([[1 2] [3 [4 5]]])", result: [1,2,3,4,5] },
{ to_parse: "flatten({ [ 'a' { [ 'bw' 1 ] } ] })", result: { "a.bw": 1 } },
{ to_parse: "flatten({ [ 'a' { [ 'b' 1 ] } ] [ 'c' 2 ] } '-')", result: { "a-b": 1, "c": 2 } },
{ to_parse: "flatten($meta.sub_numbers)", result: [1,2,3,4,5]},
{ to_parse: "flatten($meta.deep_numbers)", result: [ 1, 2, 3, 4, 5, 6, 7, 9, 12, 22, 33, 45, 63, 88, 12 ]},
{ to_parse: "flatten($meta.deep_numbers 2)", result: [1,2,3,4,5,6,7,9,12,22,[33,[45,63,[88,12]]]]},
{ to_parse: "flatten($ctx)", result: { "foo": 72, "request.origin.detail.port": 25 }},
{ to_parse: "unflatten(flatten($meta '%') '%')", result: container.meta},
{ to_parse: "transform($meta.obj_list.abc { ['out' '(: $.name :)']} )", result: 'abc'},
{ to_parse: "transform($meta.obj_list.abc '(: $.name :)' )", result: 'abc'},
{ to_parse: "^($meta.obj_list.abc '(: $.name :)' )", result: 'abc'},
{ to_parse: "^($meta.obj_list.abc ( &('(:' '$.name' ':)') ))", result: 'abc'},
{ to_parse: "{ ('key':'value') ('key2':'value2') ('key3':'value3')}", result: { "key": "value", "key2": "value2", "key3": "value3"}},
{ to_parse: "{ ('key' 'value') ('key2' 'value2') ('key3' 'value3')}", result: { "key": "value", "key2": "value2", "key3": "value3"}},
{ to_parse: "&(['foo' 'bar'] ['baz' 'bat'])", result: ['foo', 'bar', 'baz', 'bat'] },
{ to_parse: "['foo' 'bar']", result: ['foo', 'bar'] },
{ to_parse: "sprintf('%05.2f %08d %12s' 2.33 7 'bob')", result: '02.33 00000007 bob'},
{ to_parse: "sprintf('%05.2f %08d %12s' [2.33 7 'bob'])", result: '02.33 00000007 bob'},
{ to_parse: "sprintf('key: %12s value: %17s' $meta.pair)", result: 'key: the_key value: the_value'},
{ to_parse: 'sprintf("%08X" 0xABCD)', result: "0000ABCD"},
{ to_parse: 'sprintf("%lu" 43981)', result :'43981'},
{ to_parse: 'sprintf("%10s" "string")', result: ' string'},
{ to_parse: 'sprintf("%*s" 10 "string")', result:' string'},
{ to_parse: 'sprintf("%-10s" "string")', result: 'string '},
{ to_parse: 'sprintf("%-10.10s" "truncateiftoolong")', result :'truncateif'},
{ to_parse: "(['foo' 'bar'])[0]", result: 'foo' },
{ to_parse: "({ ('key' 'value') ('key2' 'value2') ('key3' 'value3')})['key2']", result: "value2"},
{ to_parse: "({ ('key' 'value') ('key2' 'value2') ('key3' 'value3')}).key2", result: "value2"},
{ to_parse: "({ ('key' 'value') ('key2' 'value2') ('key3' 'value3')})", result: { "key": "value", "key2": "value2", "key3": "value3"}},
{ to_parse: "(flatten($meta.deep_numbers))[10]", result: 33},
{ to_parse: "(flatten($meta.deep_numbers)).10", result: 33},
{ to_parse: "(flatten($meta.deep_numbers))[$meta.sortable_numeric[0].val]", result: 33},
{ to_parse: "(flatten($meta.deep_numbers 2)).10.1", result: [45,63,[88,12]]},
{ to_parse: "(flatten($meta.deep_numbers 2))[10][1][0]", result: 45},
{ to_parse: "&( [] fne($meta.obj_list []) transform($meta.deep '(: $. :)') )", result: [ { fff: { name: 'fff' }, abc: { name: 'abc' }, zz: { name: 'zz' }, ccf: { name: 'ccf', bar: null } }, { things: 22 } ]},
{ to_parse: "&( [] fne($meta.obj_list []) @('deep' transform($meta.deep '(: $. :)')) )", result: [ { fff: { name: 'fff' }, abc: { name: 'abc' }, zz: { name: 'zz' }, ccf: { name: 'ccf', bar: null } }, { things: 22 } ]},
{ to_parse: "&( [] fne($meta.obj_list []) @(transform($meta.deep '(: $. :)')) )", result: [ { fff: { name: 'fff' }, abc: { name: 'abc' }, zz: { name: 'zz' }, ccf: { name: 'ccf', bar: null } }, { things: 22 } ]},
{ to_parse: "extract($meta.letters 8 5 12 12 15 )", result: ['h','e','l','l','o'] },
{ to_parse: "extract($meta.letters, 8, 5, 12, 12, 15 )", result: ['h','e','l','l','o'] },
{ to_parse: "extract($meta.letters 15 13 7 24..26 )", result: ['o','m','g','x','y','z']},
{ to_parse: "extract($meta.letters 4..5 9..13 19..23)", result: ['d','e','i','j','k','l','m','s','t','u','v','w' ]},
{ to_parse: "extract($meta.letters 4 [ 5 9 8 2 ])", result: ['d','e','i','h','b'] },
{ to_parse: "extract($meta.obj_list 'abc' 'zz')", result: { abc: { name: 'abc' }, zz: { name: 'zz' }} },
{ to_parse: "extract($meta.obj_list 'abc' ['fff' 'zz'])", result: { abc: { name: 'abc'}, fff: { name: 'fff' }, zz: { name: 'zz' }} },
{ to_parse: "extract($meta.obj_list ['abc' 'zz'] 'fff')", result: { abc: { name: 'abc'}, fff: { name: 'fff' }, zz: { name: 'zz' }} },
{ to_parse: "extract($meta.obj_list ['abc' 'zz'] 'fff')", result: { abc: { name: 'abc'}, fff: { name: 'fff' }, zz: { name: 'zz' }} },
{ to_parse: "'1.22' =~ /\\d\\.\\d/g", result: true },
{ to_parse: "'1.22' =~ /\\\\d\\\\.\\\\d/g", result: false },
{ to_parse: "/foo/ == /foo/", result: true },
{ to_parse: "/foo/g", result: new RegExp('foo', 'g') },
{ to_parse: "template($. 'Hi There {$meta.thing}!')", result: "Hi There port!" },
{ to_parse: "template($. 'length is {length($meta.thing)}!')", result: "length is 4!" },
{ to_parse: "template($. '{$meta.thing} is {$ctx.request.origin.detail.port}')", result: "port is 25" },
{ to_parse: "template($. 'Letters is {join($meta.letters ``)}')", result: "Letters is @abcdefghijklmnopqrstuvwxyz" },
{ to_parse: "t($. 'Hi There {$meta.thing}!')", result: "Hi There port!" },
{ to_parse: "t($. 'user_{join(2..8 ``)}')", result: "user_2345678" },
{ to_parse: "t($. 'length is {length($meta.thing)}!')", result: "length is 4!" },
{ to_parse: "t($. '{$ctx.request.origin.detail.port} is the {$meta.thing}')", result: "25 is the port" },
];
describe('DTL Expression Parsing', function(done) {
describe('Basic', function() {
tests.forEach(function(test, i) {
it("Parsing '" + test.to_parse +"'", function() {
var result;
var res;
// result = DTLExpressions.parse(test.to_parse, container);
try {
result = DTL.apply(container, { out: "(: " + test.to_parse + " :)" });
if (test.regex ) {
var r = new RegExp(test.regex);
assert.ok(r.test(result), 'RegExp passes');
} else {
if (typeof test.result == 'object') {
assert.deepEqual(result, test.result, "Produces expected results: " + util.inspect(result, { depth: null}) + " = " + util.inspect(test.result, {depth: null}) );
} else {
assert.strictEqual(result, test.result, "Produces expected results: " + result + " = " + test.result );
}
}
} catch(e) {
if (!test.expect_exception) {
throw e;
}
}
if (i >= tests.length) {
done();
}
});
});
});
});