// Copyright 2022 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // License, and you may not use this file except in compliance with the Business Source License. // // As of the Change Date specified in that file, in accordance with // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. #include #include #include #include #include "query/procedure/mg_procedure_impl.hpp" #include "test_utils.hpp" static void DummyCallback(mgp_list *, mgp_graph *, mgp_result *, mgp_memory *) {} TEST(Module, InvalidProcedureRegistration) { mgp_module module(memgraph::utils::NewDeleteResource()); mgp_proc *proc{nullptr}; EXPECT_EQ(mgp_module_add_read_procedure(&module, "dashes-not-supported", DummyCallback, &proc), mgp_error::MGP_ERROR_INVALID_ARGUMENT); // as u8string this is u8"unicode\u22c6not\u2014supported" EXPECT_EQ(mgp_module_add_read_procedure(&module, "unicode\xE2\x8B\x86not\xE2\x80\x94supported", DummyCallback, &proc), mgp_error::MGP_ERROR_INVALID_ARGUMENT); // as u8string this is u8"`backticks⋆\u22c6won't-save\u2014you`" EXPECT_EQ( mgp_module_add_read_procedure(&module, "`backticks⋆\xE2\x8B\x86won't-save\xE2\x80\x94you`", DummyCallback, &proc), mgp_error::MGP_ERROR_INVALID_ARGUMENT); EXPECT_EQ(mgp_module_add_read_procedure(&module, "42_name_must_not_start_with_number", DummyCallback, &proc), mgp_error::MGP_ERROR_INVALID_ARGUMENT); EXPECT_EQ(mgp_module_add_read_procedure(&module, "div/", DummyCallback, &proc), mgp_error::MGP_ERROR_INVALID_ARGUMENT); EXPECT_EQ(mgp_module_add_read_procedure(&module, "mul*", DummyCallback, &proc), mgp_error::MGP_ERROR_INVALID_ARGUMENT); EXPECT_EQ(mgp_module_add_read_procedure(&module, "question_mark_is_not_valid?", DummyCallback, &proc), mgp_error::MGP_ERROR_INVALID_ARGUMENT); } TEST(Module, RegisteringTheSameProcedureMultipleTimes) { mgp_module module(memgraph::utils::NewDeleteResource()); mgp_proc *proc{nullptr}; EXPECT_EQ(module.procedures.find("same_name"), module.procedures.end()); EXPECT_EQ(mgp_module_add_read_procedure(&module, "same_name", DummyCallback, &proc), mgp_error::MGP_ERROR_NO_ERROR); EXPECT_NE(module.procedures.find("same_name"), module.procedures.end()); EXPECT_EQ(mgp_module_add_read_procedure(&module, "same_name", DummyCallback, &proc), mgp_error::MGP_ERROR_LOGIC_ERROR); EXPECT_EQ(mgp_module_add_read_procedure(&module, "same_name", DummyCallback, &proc), mgp_error::MGP_ERROR_LOGIC_ERROR); EXPECT_NE(module.procedures.find("same_name"), module.procedures.end()); } TEST(Module, CaseSensitiveProcedureNames) { mgp_module module(memgraph::utils::NewDeleteResource()); EXPECT_TRUE(module.procedures.empty()); mgp_proc *proc{nullptr}; EXPECT_EQ(mgp_module_add_read_procedure(&module, "not_same", DummyCallback, &proc), mgp_error::MGP_ERROR_NO_ERROR); EXPECT_EQ(mgp_module_add_read_procedure(&module, "NoT_saME", DummyCallback, &proc), mgp_error::MGP_ERROR_NO_ERROR); EXPECT_EQ(mgp_module_add_read_procedure(&module, "NOT_SAME", DummyCallback, &proc), mgp_error::MGP_ERROR_NO_ERROR); EXPECT_EQ(module.procedures.size(), 3U); } static void CheckSignature(const mgp_proc *proc, const std::string &expected) { std::stringstream ss; memgraph::query::procedure::PrintProcSignature(*proc, &ss); EXPECT_EQ(ss.str(), expected); } TEST(Module, ProcedureSignature) { mgp_memory memory{memgraph::utils::NewDeleteResource()}; mgp_module module(memgraph::utils::NewDeleteResource()); auto *proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_read_procedure, &module, "proc", &DummyCallback); CheckSignature(proc, "proc() :: ()"); EXPECT_EQ(mgp_proc_add_arg(proc, "arg1", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number)), mgp_error::MGP_ERROR_NO_ERROR); CheckSignature(proc, "proc(arg1 :: NUMBER) :: ()"); EXPECT_EQ(mgp_proc_add_opt_arg( proc, "opt1", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)), test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)).get()), mgp_error::MGP_ERROR_NO_ERROR); CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: ()"); EXPECT_EQ( mgp_proc_add_result( proc, "res1", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int))), mgp_error::MGP_ERROR_NO_ERROR); CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: (res1 :: LIST OF INTEGER)"); EXPECT_EQ(mgp_proc_add_arg(proc, "arg2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number)), mgp_error::MGP_ERROR_LOGIC_ERROR); CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: (res1 :: LIST OF INTEGER)"); EXPECT_EQ(mgp_proc_add_arg(proc, "arg2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map)), mgp_error::MGP_ERROR_LOGIC_ERROR); CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: (res1 :: LIST OF INTEGER)"); EXPECT_EQ(mgp_proc_add_deprecated_result(proc, "res2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string)), mgp_error::MGP_ERROR_NO_ERROR); CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: " "(res1 :: LIST OF INTEGER, DEPRECATED res2 :: STRING)"); EXPECT_EQ(mgp_proc_add_result(proc, "res2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)), mgp_error::MGP_ERROR_LOGIC_ERROR); EXPECT_EQ(mgp_proc_add_deprecated_result(proc, "res1", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)), mgp_error::MGP_ERROR_LOGIC_ERROR); EXPECT_EQ( mgp_proc_add_opt_arg(proc, "opt2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string), test_utils::CreateValueOwningPtr( EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_string, "string=\"value\"", &memory)) .get()), mgp_error::MGP_ERROR_NO_ERROR); CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?, " "opt2 = \"string=\\\"value\\\"\" :: STRING) :: " "(res1 :: LIST OF INTEGER, DEPRECATED res2 :: STRING)"); } TEST(Module, ProcedureSignatureOnlyOptArg) { mgp_memory memory{memgraph::utils::NewDeleteResource()}; mgp_module module(memgraph::utils::NewDeleteResource()); auto *proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_read_procedure, &module, "proc", &DummyCallback); EXPECT_EQ(mgp_proc_add_opt_arg( proc, "opt1", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)), test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)).get()), mgp_error::MGP_ERROR_NO_ERROR); CheckSignature(proc, "proc(opt1 = Null :: ANY?) :: ()"); } TEST(Module, ReadWriteProcedures) { mgp_module module(memgraph::utils::NewDeleteResource()); auto *read_proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_read_procedure, &module, "read", &DummyCallback); EXPECT_FALSE(read_proc->info.is_write); auto *write_proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_write_procedure, &module, "write", &DummyCallback); EXPECT_TRUE(write_proc->info.is_write); mgp_proc read_proc_with_function{"dummy_name", std::function{ [](mgp_list *, mgp_graph *, mgp_result *, mgp_memory *) {}}, memgraph::utils::NewDeleteResource()}; EXPECT_FALSE(read_proc_with_function.info.is_write); }