/*jshint globalstrict:false, strict:false, maxlen: 200, unused: false*/ /*global assertEqual, assertTrue, assertFalse, fail */ /* unused for functions with 'what' parameter.*/ //////////////////////////////////////////////////////////////////////////////// /// @brief test the AQL user functions management /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2012 triagens GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Jan Steemann /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// var jsunity = require("jsunity"); var arangodb = require("@arangodb"); var ERRORS = arangodb.errors; var db = arangodb.db; var aqlfunctions = require("@arangodb/aql/functions"); //////////////////////////////////////////////////////////////////////////////// /// @brief test suite //////////////////////////////////////////////////////////////////////////////// function AqlFunctionsSuite () { 'use strict'; var unregister = function (name) { try { aqlfunctions.unregister(name); } catch (err) { } }; return { //////////////////////////////////////////////////////////////////////////////// /// @brief set up //////////////////////////////////////////////////////////////////////////////// setUp : function () { db._useDatabase("_system"); aqlfunctions.unregisterGroup("UnitTests::"); }, //////////////////////////////////////////////////////////////////////////////// /// @brief tear down //////////////////////////////////////////////////////////////////////////////// tearDown : function () { db._useDatabase("_system"); aqlfunctions.unregisterGroup("UnitTests::"); }, //////////////////////////////////////////////////////////////////////////////// /// @brief multiple databases //////////////////////////////////////////////////////////////////////////////// testDatabases1 : function () { aqlfunctions.register("UnitTests::tryme::foo", function (what) { return what * 2; }, true); try { db._dropDatabase("UnitTestsAhuacatl"); } catch (err) { } try { db._createDatabase("UnitTestsAhuacatl"); db._useDatabase("UnitTestsAhuacatl"); } catch (err) { } var actual; var result = aqlfunctions.register("UnitTests::tryme::foo", function (what) { return what * 4; }, true); assertEqual("function (what) { return what * 4; }", aqlfunctions.toArray("UnitTests")[0].code); actual = db._query({ query: "RETURN UnitTests::tryme::foo(4)" }).toArray(); assertEqual([ 16 ], actual); db._useDatabase("_system"); assertEqual("function (what) { return what * 2; }", aqlfunctions.toArray("UnitTests")[0].code); actual = db._query({ query: "RETURN UnitTests::tryme::foo(4)" }).toArray(); assertEqual([ 8 ], actual); try { db._useDatabase("_system"); db._dropDatabase("UnitTestsAhuacatl"); } catch (err) { } }, //////////////////////////////////////////////////////////////////////////////// /// @brief multiple databases //////////////////////////////////////////////////////////////////////////////// testDatabases2 : function () { try { unregister("UnitTests::tryme"); } catch (err) { } try { db._dropDatabase("UnitTestsAhuacatl"); } catch (err) { } try { db._createDatabase("UnitTestsAhuacatl"); db._useDatabase("UnitTestsAhuacatl"); } catch (err) { } var actual; aqlfunctions.register("UnitTests::tryme", function (what) { return what * 3; }, true); assertEqual("function (what) { return what * 3; }", aqlfunctions.toArray("UnitTests")[0].code); actual = db._query({ query: "RETURN UnitTests::tryme(3)" }).toArray(); assertEqual([ 9 ], actual); aqlfunctions.register("UnitTests::tryme", function (what) { return what * 4; }, true); assertEqual("function (what) { return what * 4; }", aqlfunctions.toArray("UnitTests")[0].code); actual = db._query({ query: "RETURN UnitTests::tryme(3)" }).toArray(); assertEqual([ 12 ], actual); db._useDatabase("_system"); assertEqual(0, aqlfunctions.toArray("UnitTests").length); try { db._query({ query: "RETURN UnitTests::tryme(4)" }).toArray(); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_NOT_FOUND.code, err.errorNum); } try { db._useDatabase("_system"); db._dropDatabase("UnitTestsAhuacatl"); } catch (err) { } }, //////////////////////////////////////////////////////////////////////////////// /// @brief toArray //////////////////////////////////////////////////////////////////////////////// testToArray1 : function () { aqlfunctions.register("UnitTests::tryme::foo", function (what) { return what * 2; }, true); aqlfunctions.register("UnitTests::tryme::bar", function (what) { return what * 2; }, true); assertEqual([ "UnitTests::tryme::bar", "UnitTests::tryme::foo" ], aqlfunctions.toArray("UnitTests").map(function (f) { return f.name; }).sort()); assertEqual([ "UnitTests::tryme::bar", "UnitTests::tryme::foo" ], aqlfunctions.toArray("UnitTests::").map(function (f) { return f.name; }).sort()); assertEqual([ "UnitTests::tryme::bar", "UnitTests::tryme::foo" ], aqlfunctions.toArray("UnitTests::tryme").map(function (f) { return f.name; }).sort()); assertEqual([ "UnitTests::tryme::bar", "UnitTests::tryme::foo" ], aqlfunctions.toArray("UnitTests::tryme::").map(function (f) { return f.name; }).sort()); }, //////////////////////////////////////////////////////////////////////////////// /// @brief toArray //////////////////////////////////////////////////////////////////////////////// testToArray2 : function () { aqlfunctions.register("UnitTests::tryme::foo", function (what) { return what * 2; }, true); aqlfunctions.register("UnitTests::tryme::bar", function (what) { return what * 2; }, true); aqlfunctions.register("UnitTests58::tryme::bar", function (what) { return what * 2; }, true); aqlfunctions.register("UnitTests58::whyme::bar", function (what) { return what * 2; }, true); assertEqual([ "UnitTests::tryme::bar", "UnitTests::tryme::foo" ], aqlfunctions.toArray("UnitTests").map(function (f) { return f.name; }).sort()); assertEqual([ "UnitTests::tryme::bar", "UnitTests::tryme::foo" ], aqlfunctions.toArray("UnitTests::").map(function (f) { return f.name; }).sort()); assertEqual([ "UnitTests::tryme::bar", "UnitTests::tryme::foo" ], aqlfunctions.toArray("UnitTests::tryme").map(function (f) { return f.name; }).sort()); assertEqual([ "UnitTests58::tryme::bar", "UnitTests58::whyme::bar" ], aqlfunctions.toArray("UnitTests58").map(function (f) { return f.name; }).sort()); assertEqual([ "UnitTests58::tryme::bar", "UnitTests58::whyme::bar" ], aqlfunctions.toArray("UnitTests58::").map(function (f) { return f.name; }).sort()); assertEqual([ "UnitTests58::tryme::bar" ], aqlfunctions.toArray("UnitTests58::tryme").map(function (f) { return f.name; }).sort()); assertEqual([ "UnitTests58::tryme::bar" ], aqlfunctions.toArray("UnitTests58::tryme::").map(function (f) { return f.name; }).sort()); aqlfunctions.unregister("UnitTests58::tryme::bar"); aqlfunctions.unregister("UnitTests58::whyme::bar"); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test registered code //////////////////////////////////////////////////////////////////////////////// testRegisterCode1 : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return what * 2; }, true); assertEqual("function (what) { return what * 2; }", aqlfunctions.toArray("UnitTests")[0].code); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test registered code //////////////////////////////////////////////////////////////////////////////// testRegisterCode2 : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", "function (what) { return what * 2; }", true); assertEqual("function (what) { return what * 2; }", aqlfunctions.toArray("UnitTests")[0].code); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function //////////////////////////////////////////////////////////////////////////////// testRegisterFunc1 : function () { unregister("UnitTests::tryme::foo"); aqlfunctions.register("UnitTests::tryme::foo", function (what) { return what * 2; }, true); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function //////////////////////////////////////////////////////////////////////////////// testRegisterFunc2 : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return what * 2; }, true); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function //////////////////////////////////////////////////////////////////////////////// testRegisterString1 : function () { unregister("UnitTests::tryme"); assertFalse(aqlfunctions.register("UnitTests::tryme", "function (what) { return what * 2; }", true)); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function //////////////////////////////////////////////////////////////////////////////// testRegisterString2 : function () { unregister("UnitTests::tryme::foo"); assertFalse(aqlfunctions.register("UnitTests::tryme::foo", "function (what) { return what * 2; }", true)); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function //////////////////////////////////////////////////////////////////////////////// testRegisterString3 : function () { unregister("UnitTests::tryme::foo"); assertFalse(aqlfunctions.register("UnitTests::tryme::foo", "/* this is a function! */ \n function (what) { return what * 2; }", true)); }, //////////////////////////////////////////////////////////////////////////////// /// @brief re-register a function //////////////////////////////////////////////////////////////////////////////// testReRegister : function () { unregister("UnitTests::tryme"); assertFalse(aqlfunctions.register("UnitTests::tryme", function (what) { return what * 2; }, true)); assertTrue(aqlfunctions.register("UnitTests::tryme", function (what) { return what * 2; }, true)); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function with an invalid name //////////////////////////////////////////////////////////////////////////////// testRegisterInvalidName1 : function () { try { aqlfunctions.register("foo", function (what) { return what * 2; }, true); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_NAME.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function with an invalid name //////////////////////////////////////////////////////////////////////////////// testRegisterInvalidName2 : function () { try { aqlfunctions.register("_test", function (what) { return what * 2; }, true); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_NAME.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function with an invalid name //////////////////////////////////////////////////////////////////////////////// testRegisterInvalidName3 : function () { try { aqlfunctions.register("_test::foo", function (what) { return what * 2; }, true); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_NAME.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function with an invalid name //////////////////////////////////////////////////////////////////////////////// testRegisterInvalidName4 : function () { try { aqlfunctions.register("test:", function (what) { return what * 2; }, true); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_NAME.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function with an invalid name //////////////////////////////////////////////////////////////////////////////// testRegisterInvalidName5 : function () { try { aqlfunctions.register("test::", function (what) { return what * 2; }, true); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_NAME.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function without surrounding function() //////////////////////////////////////////////////////////////////////////////// testRegisterNonFunction1 : function () { unregister("UnitTests::tryme"); try { aqlfunctions.register("UnitTests::tryme", "1 + 1", true); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_CODE.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function without surrounding function() //////////////////////////////////////////////////////////////////////////////// testRegisterNonFunction2 : function () { unregister("UnitTests::tryme"); try { aqlfunctions.register("UnitTests::tryme", "name = 'foo'", true); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_CODE.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function with an invalid body //////////////////////////////////////////////////////////////////////////////// testRegisterInvalidCode1 : function () { unregister("UnitTests::tryme"); try { aqlfunctions.register("UnitTests::tryme", "function (what) { ", true); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_CODE.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function with an invalid body //////////////////////////////////////////////////////////////////////////////// testRegisterInvalidCode2 : function () { unregister("UnitTests::tryme"); try { aqlfunctions.register("UnitTests::tryme", 1234, true); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_CODE.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and unregister it //////////////////////////////////////////////////////////////////////////////// testUnregister : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return what * 4; }, true); aqlfunctions.unregister("UnitTests::tryme"); }, //////////////////////////////////////////////////////////////////////////////// /// @brief unregister an unknown function //////////////////////////////////////////////////////////////////////////////// testUnregisterUnknown : function () { unregister("UnitTests::tryme"); try { aqlfunctions.unregister("UnitTests::tryme"); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_NOT_FOUND.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQuery1 : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return what * 2; }, true); var actual = db._query({ query: "RETURN UnitTests::tryme(4)" }).toArray(); assertEqual([ 8 ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQuery2 : function () { unregister("UnitTests::tryme"); unregister("UnitTests::foo"); aqlfunctions.register("UnitTests::tryme", function (what) { return what * 2; }, true); aqlfunctions.register("UnitTests::foo", function (what) { return what * 4; }, true); var actual = db._query({ query: "RETURN UnitTests::tryme(4) + UnitTests::foo(9)" }).toArray(); assertEqual([ 4 * 2 + 9 * 4 ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testMultiple : function () { unregister("UnitTests::tryme"); unregister("UnitTests::foo"); aqlfunctions.register("UnitTests::tryme", function (what) { return "foobar" + what; }, true); aqlfunctions.register("UnitTests::foo", function (what) { return (what * 4) + "barbaz"; }, true); var actual = db._query({ query: "RETURN CONCAT(UnitTests::tryme('abcdef'), UnitTests::foo(9))" }).toArray(); assertEqual([ "foobar" + "abcdef" + (9 * 4) + "barbaz" ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testManyInvocations : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return what * 2; }, true); var actual = db._query({ query: "FOR i IN 1..10000 RETURN UnitTests::tryme(i)" }).toArray(); var expected = [ ]; for (var i = 1; i <= 10000; ++i) { expected.push(i * 2); } assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQueryThrow : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { throw "peng"; }, true); try { db._query({ query: "RETURN UnitTests::tryme(4)" }).toArray(); fail(); } catch (err) { assertEqual(ERRORS.ERROR_QUERY_FUNCTION_RUNTIME_ERROR.code, err.errorNum); } }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQueryReturnUndefined : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { }, true); var actual = db._query({ query: "RETURN UnitTests::tryme(4)" }).toArray(); assertEqual([ null ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQueryReturnNan : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return 1 / 0; }, true); var actual = db._query({ query: "RETURN UnitTests::tryme(4)" }).toArray(); assertEqual([ null ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQueryReturnNull : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return null; }, true); var actual = db._query({ query: "RETURN UnitTests::tryme(4)" }).toArray(); assertEqual([ null ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQueryReturnTrue : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return true; }, true); var actual = db._query({ query: "RETURN UnitTests::tryme(4)" }).toArray(); assertEqual([ true ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQueryReturnFalse : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return false; }, true); var actual = db._query({ query: "RETURN UnitTests::tryme(4)" }).toArray(); assertEqual([ false ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQueryReturnComplex : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return [ true, false, null, 1, 2, -4, [ 5.5, { a: 1, "b": "def" } ] ]; }, true); var actual = db._query({ query: "RETURN UnitTests::tryme()" }).toArray(); assertEqual([ [ true, false, null, 1, 2, -4, [ 5.5, { a: 1, "b": "def" } ] ] ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQueryStringWithComments : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { return true; }, true); var actual = db._query({ query: "// foo\n /* bar\nbaz\n*/ RETURN UnitTests::tryme(4) // some comment" }).toArray(); assertEqual([ true ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief register a function and run a query //////////////////////////////////////////////////////////////////////////////// testQueryFuncWithComments : function () { unregister("UnitTests::tryme"); aqlfunctions.register("UnitTests::tryme", function (what) { // foo /* bar baz */ return [ true, false, null, 1, 2, -4, [ 5.5, { a: 1, "b": "def" } ] ]; // some comment }, true); var actual = db._query({ query: "RETURN UnitTests::tryme()" }).toArray(); assertEqual([ [ true, false, null, 1, 2, -4, [ 5.5, { a: 1, "b": "def" } ] ] ], actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test for specific problem with docs returned by user functions //////////////////////////////////////////////////////////////////////////////// testQueryReturnShapedJson : function () { db._drop("UnitTestsFunc"); db._create("UnitTestsFunc"); for (var i = 0; i < 5 ; ++i) { db.UnitTestsFunc.save({ value: i }); } unregister("UnitTests::testFunc"); var testFunc = function() { var db = require("internal").db; var query = "FOR path IN UnitTestsFunc SORT path.value RETURN path.value"; var bind = {}; return db._query(query, bind).toArray()[0]; }; aqlfunctions.register("UnitTests::testFunc", testFunc); var actual = db._query({ query: "RETURN UnitTests::testFunc()" }).toArray(); assertEqual([ 0 ], actual); db._drop("UnitTestsFunc"); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test for specific problem with docs returned by user functions //////////////////////////////////////////////////////////////////////////////// testQueryReturnShapedJsonFull : function () { db._drop("UnitTestsFunc"); db._create("UnitTestsFunc"); db.UnitTestsFunc.save({ value1: "abc", value2: 123, value3: null, value4: false }); unregister("UnitTests::testFunc"); var testFunc = function() { var db = require("internal").db; var query = "FOR path IN UnitTestsFunc RETURN path"; var bind = {}; return db._query(query, bind).toArray()[0]; }; aqlfunctions.register("UnitTests::testFunc", testFunc); var actual = db._query({ query: "RETURN UnitTests::testFunc()" }).toArray(); assertEqual("abc", actual[0].value1); assertEqual(123, actual[0].value2); assertEqual(null, actual[0].value3); assertEqual(false, actual[0].value4); db._drop("UnitTestsFunc"); } }; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes the test suite //////////////////////////////////////////////////////////////////////////////// jsunity.run(AqlFunctionsSuite); return jsunity.done();