Files
fn_registry/cpp/tests/test_lua_engine.cpp
egutierrez 212875ed0d chore: auto-commit (286 archivos)
- .claude/agents/fn-orquestador/SKILL.md
- .claude/commands/fn_claude.md
- .claude/rules/INDEX.md
- .claude/rules/cpp_apps.md
- .claude/rules/ids_naming.md
- CHANGELOG.md
- apps/dag_engine/README.md
- apps/dag_engine/api.go
- apps/dag_engine/dags_migrated/example.yaml
- apps/dag_engine/dags_migrated/example_lineage_tracking.yaml
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:33:22 +02:00

108 lines
3.5 KiB
C++

// Tests para lua_engine_cpp_core.
// Catch2 amalgamated. No requiere ImGui context.
#include <catch_amalgamated.hpp>
#include "core/lua_engine.h"
// Helper: compila + eval con RowCtx vacia (sin columnas).
static std::string quick_eval(const std::string& formula, std::string* err = nullptr) {
lua_engine::Engine* e = lua_engine::get();
std::string compile_err;
int id = lua_engine::compile(e, formula, &compile_err);
if (id < 0) {
if (err) *err = compile_err;
return "";
}
lua_engine::RowCtx ctx;
std::string eval_err;
std::string result = lua_engine::eval(e, id, ctx, &eval_err);
lua_engine::release(e, id);
if (err) *err = eval_err;
return result;
}
TEST_CASE("lua_engine: eval expr simple", "[lua_engine]") {
std::string err;
std::string result = quick_eval("2 + 3", &err);
REQUIRE(err.empty());
REQUIRE(result == "5");
}
TEST_CASE("lua_engine: eval con vars via RowCtx", "[lua_engine]") {
// Prepara tabla: 1 fila, 2 columnas (price=10, qty=3).
const char* price_str = "10";
const char* qty_str = "3";
const char* cells[] = { price_str, qty_str };
std::vector<std::string> headers = { "price", "qty" };
std::unordered_map<std::string,int> name_to_col = { {"price", 0}, {"qty", 1} };
data_table::ColumnType types[] = {
data_table::ColumnType::Float,
data_table::ColumnType::Float
};
lua_engine::RowCtx ctx;
ctx.cells = cells;
ctx.orig_cols = 2;
ctx.row = 0;
ctx.header_names = &headers;
ctx.name_to_col = &name_to_col;
ctx.types_orig = types;
ctx.n_types_orig = 2;
lua_engine::Engine* e = lua_engine::get();
std::string err;
int id = lua_engine::compile(e, "[price] * [qty]", &err);
REQUIRE(err.empty());
REQUIRE(id >= 0);
std::string result = lua_engine::eval(e, id, ctx, &err);
lua_engine::release(e, id);
REQUIRE(err.empty());
// 10 * 3 = 30 (integer)
REQUIRE(result == "30");
}
TEST_CASE("lua_engine: error en expr invalida", "[lua_engine]") {
std::string err;
int id = lua_engine::compile(lua_engine::get(), "this is not lua !!!", &err);
REQUIRE(id < 0);
REQUIRE(!err.empty());
}
TEST_CASE("lua_engine: sandbox bloquea io.open", "[lua_engine]") {
std::string err;
// io fue eliminado del sandbox; acceder a io.open debe producir error en runtime.
std::string result = quick_eval("io.open('/etc/passwd', 'r')", &err);
// Debe fallar (io es nil -> intento de indexar nil -> runtime error).
REQUIRE(!err.empty());
REQUIRE(result.empty());
}
TEST_CASE("lua_engine: preprocess convierte [col] a row[\"col\"]", "[lua_engine]") {
std::string out = lua_engine::preprocess("[price] * [qty]");
// Debe contener row["price"] y row["qty"], y auto-return al ser expresion.
REQUIRE(out.find("row[\"price\"]") != std::string::npos);
REQUIRE(out.find("row[\"qty\"]") != std::string::npos);
REQUIRE(out.rfind("return", 0) == 0);
}
TEST_CASE("lua_engine: fn.* builtins disponibles", "[lua_engine]") {
std::string err;
std::string result = quick_eval("fn.upper('hello')", &err);
REQUIRE(err.empty());
REQUIRE(result == "HELLO");
}
TEST_CASE("lua_engine: shutdown y reinicio", "[lua_engine]") {
lua_engine::shutdown();
// Tras shutdown, get() debe crear un nuevo estado limpio.
lua_engine::Engine* e = lua_engine::get();
REQUIRE(e != nullptr);
std::string err;
std::string result = quick_eval("1 + 1", &err);
REQUIRE(err.empty());
REQUIRE(result == "2");
lua_engine::shutdown();
}