Skip to content

Commit da51dc5

Browse files
aheevadsharma
authored andcommitted
fix tests
1 parent 469652a commit da51dc5

6 files changed

Lines changed: 86 additions & 16 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ jobs:
109109
- name: Build Node.js native module (Windows)
110110
if: ${{ matrix.platform == 'win32' }}
111111
shell: cmd
112+
env:
113+
EXTRA_CMAKE_FLAGS: -DLBUG_NODEJS_ENABLE_TEST_EXPORTS=ON
112114
run: |
113115
powershell.exe -Command "Add-MpPreference -ExclusionPath '${{ github.workspace }}'"
114116
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
@@ -124,6 +126,7 @@ jobs:
124126
CMAKE_OSX_ARCHITECTURES: ${{ matrix.mac_env && matrix.arch || '' }}
125127
CMAKE_C_COMPILER_LAUNCHER: ccache
126128
CMAKE_CXX_COMPILER_LAUNCHER: ccache
129+
EXTRA_CMAKE_FLAGS: -DLBUG_NODEJS_ENABLE_TEST_EXPORTS=ON
127130

128131
- name: Run Node.js tests
129132
working-directory: tools/nodejs_api

CMakeLists.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,16 @@ get_filename_component(NODE_ADDON_API_INCLUDE_PATH ./node_modules/node-addon-api
104104
include_directories(${CMAKE_JS_INC})
105105
include_directories(${NODE_ADDON_API_INCLUDE_PATH})
106106

107-
file(GLOB CPP_SOURCE_FILES ./src_cpp/*)
107+
option(LBUG_NODEJS_ENABLE_TEST_EXPORTS
108+
"Export test-only native helpers (e.g. createArrowCSRTestData). Enable for dev/test builds only; never ship in production."
109+
OFF)
110+
111+
file(GLOB CPP_SOURCE_FILES ./src_cpp/*.cpp)
112+
113+
if(NOT LBUG_NODEJS_ENABLE_TEST_EXPORTS)
114+
list(REMOVE_ITEM CPP_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src_cpp/node_arrow_test_utils.cpp")
115+
endif()
116+
108117
file(GLOB JS_SOURCE_FILES
109118
./src_js/*.js
110119
./src_js/*.mjs
@@ -178,3 +187,7 @@ target_link_libraries(lbugjs PRIVATE lbug ${NODEJS_LBUG_LINK_DEPS} ${CMAKE_JS_LI
178187
if(LBUG_NODEJS_EXTRA_LINK_LIBS)
179188
target_link_libraries(lbugjs PRIVATE ${LBUG_NODEJS_EXTRA_LINK_LIBS})
180189
endif()
190+
if(LBUG_NODEJS_ENABLE_TEST_EXPORTS)
191+
target_compile_definitions(lbugjs PRIVATE LBUG_ENABLE_TEST_EXPORTS)
192+
message(STATUS "lbugjs: test-only exports enabled (LBUG_NODEJS_ENABLE_TEST_EXPORTS=ON)")
193+
endif()

src_cpp/main.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1-
#include "include/node_arrow_test_utils.h"
21
#include "include/node_connection.h"
32
#include "include/node_database.h"
43
#include "include/node_query_result.h"
54
#include <napi.h>
65

6+
#ifdef LBUG_ENABLE_TEST_EXPORTS
7+
#include "include/node_arrow_test_utils.h"
8+
#endif
9+
710
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
811
NodeConnection::Init(env, exports);
912
NodeDatabase::Init(env, exports);
1013
NodePreparedStatement::Init(env, exports);
1114
NodeQueryResult::Init(env, exports);
15+
#ifdef LBUG_ENABLE_TEST_EXPORTS
1216
exports.Set("createArrowCSRTestData", Napi::Function::New(env, CreateArrowCSRTestData));
17+
#endif
1318
return exports;
1419
}
1520

src_cpp/node_connection.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,25 @@
1010

1111
namespace {
1212

13+
// Maximum number of Arrow arrays accepted in a single batch call.
14+
// Guards against integer-wrap DoS when a raw pointer + count is used.
15+
static constexpr int64_t kMaxArrowBatchCount = 65536;
16+
17+
// Reads a batch-count argument, throwing if it is not a number, not a positive
18+
// integer, or exceeds kMaxArrowBatchCount.
19+
static uint64_t ValidateArrayCount(const Napi::Value& value, const char* name) {
20+
if (!value.IsNumber()) {
21+
throw std::runtime_error(std::string(name) + " must be a number.");
22+
}
23+
auto count = value.As<Napi::Number>().Int64Value();
24+
if (count <= 0 || count > kMaxArrowBatchCount) {
25+
throw std::runtime_error(std::string(name) +
26+
" must be a positive integer no greater than " +
27+
std::to_string(kMaxArrowBatchCount) + ".");
28+
}
29+
return static_cast<uint64_t>(count);
30+
}
31+
1332
template<typename T>
1433
T* GetPointerArgument(const Napi::Value& value, const char* name) {
1534
if (value.IsBigInt()) {
@@ -264,7 +283,7 @@ Napi::Value NodeConnection::CreateArrowTableSync(const Napi::CallbackInfo& info)
264283
Napi::HandleScope scope(env);
265284
auto tableName = info[0].As<Napi::String>().Utf8Value();
266285
auto* schema = GetPointerArgument<ArrowSchema>(info[1], "schema");
267-
auto numArrays = info[3].As<Napi::Number>().Int64Value();
286+
auto numArrays = ValidateArrayCount(info[3], "numArrays");
268287
auto nodeQueryResult = Napi::ObjectWrap<NodeQueryResult>::Unwrap(info[4].As<Napi::Object>());
269288
try {
270289
auto result = lbug::ArrowTableSupport::createViewFromArrowTable(*connection, tableName,
@@ -290,9 +309,9 @@ Napi::Value NodeConnection::CreateArrowRelTableSync(const Napi::CallbackInfo& in
290309
// info[6]=indptrSchemaPtr, info[7]=indptrArraysPtr, info[8]=numIndptrArrays,
291310
// info[9]=dstColName, info[10]=nodeQueryResult
292311
auto* indicesSchema = GetPointerArgument<ArrowSchema>(info[3], "indicesSchema");
293-
auto numIndicesArrays = info[5].As<Napi::Number>().Uint32Value();
312+
auto numIndicesArrays = ValidateArrayCount(info[5], "numIndicesArrays");
294313
auto* indptrSchema = GetPointerArgument<ArrowSchema>(info[6], "indptrSchema");
295-
auto numIndptrArrays = info[8].As<Napi::Number>().Uint32Value();
314+
auto numIndptrArrays = ValidateArrayCount(info[8], "numIndptrArrays");
296315
auto dstColName = info[9].As<Napi::String>().Utf8Value();
297316
auto nodeQueryResult =
298317
Napi::ObjectWrap<NodeQueryResult>::Unwrap(info[10].As<Napi::Object>());
@@ -306,7 +325,7 @@ Napi::Value NodeConnection::CreateArrowRelTableSync(const Napi::CallbackInfo& in
306325
// Flat mode: info[3]=schemaPtr, info[4]=arraysPtr, info[5]=numArrays,
307326
// info[6]=nodeQueryResult
308327
auto* schema = GetPointerArgument<ArrowSchema>(info[3], "schema");
309-
auto numArrays = info[5].As<Napi::Number>().Uint32Value();
328+
auto numArrays = ValidateArrayCount(info[5], "numArrays");
310329
auto nodeQueryResult =
311330
Napi::ObjectWrap<NodeQueryResult>::Unwrap(info[6].As<Napi::Object>());
312331
auto result = lbug::ArrowTableSupport::createRelTableFromArrowTable(*connection,

src_js/connection.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,12 @@ class Connection {
426426
const connection = this._getConnectionSync();
427427
const nodeQueryResult = new LbugNative.NodeQueryResult();
428428
if (indptrSchemaPtr != null) {
429+
if (indptrArraysPtr == null) {
430+
throw new Error("indptrArraysPtr must be provided in CSR mode.");
431+
}
432+
if (!Array.isArray(indptrArraysPtr) && (!Number.isSafeInteger(numIndptrArrays) || numIndptrArrays <= 0)) {
433+
throw new Error("numIndptrArrays must be a positive integer.");
434+
}
429435
if (typeof dstColName !== "string") {
430436
throw new Error("dstColName must be a string.");
431437
}

test/test_arrow_memory_backed_table.js

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ const fs = require("fs");
66
// Access the native module directly for test helpers
77
const LbugNative = require("../build/lbug_native.js");
88

9+
// createArrowCSRTestData is only compiled in when the addon is built with
10+
// LBUG_NODEJS_ENABLE_TEST_EXPORTS=ON. Skip the whole suite otherwise.
11+
const hasTestHelpers = typeof LbugNative.createArrowCSRTestData === "function";
12+
913
let arrowDb, arrowConn;
1014

1115
before(function () {
@@ -15,12 +19,23 @@ before(function () {
1519
arrowConn = new lbug.Connection(arrowDb, 2);
1620
});
1721

22+
after(function () {
23+
if (arrowConn) arrowConn.closeSync();
24+
if (arrowDb) arrowDb.closeSync();
25+
});
26+
1827
describe("createArrowRelTableSync CSR", function () {
28+
before(function () {
29+
if (!hasTestHelpers) {
30+
this.skip();
31+
}
32+
});
1933
it("should throw when dstColName is not a string in CSR mode", function () {
2034
const { nodeSchemaPtr, nodeArrayPtr, indicesSchemaPtr, indicesArrayPtr,
2135
indptrSchemaPtr, indptrArrayPtr } = LbugNative.createArrowCSRTestData("to");
2236

23-
arrowConn.createArrowTableSync("person_err", nodeSchemaPtr, nodeArrayPtr, 1);
37+
const createPersonResult = arrowConn.createArrowTableSync("person_err", nodeSchemaPtr, nodeArrayPtr, 1);
38+
createPersonResult.close();
2439

2540
assert.throws(() => {
2641
arrowConn.createArrowRelTableSync(
@@ -36,20 +51,24 @@ describe("createArrowRelTableSync CSR", function () {
3651
const { nodeSchemaPtr, nodeArrayPtr, indicesSchemaPtr, indicesArrayPtr,
3752
indptrSchemaPtr, indptrArrayPtr } = LbugNative.createArrowCSRTestData("to");
3853

39-
arrowConn.createArrowTableSync("person_default", nodeSchemaPtr, nodeArrayPtr, 1);
54+
const createPersonResult = arrowConn.createArrowTableSync("person_default", nodeSchemaPtr, nodeArrayPtr, 1);
55+
createPersonResult.close();
4056

4157
// Use default dstColName "to" (omit last optional param)
42-
arrowConn.createArrowRelTableSync(
58+
const createRelResult = arrowConn.createArrowRelTableSync(
4359
"knows_default", "person_default", "person_default",
4460
indicesSchemaPtr, indicesArrayPtr, 1,
4561
indptrSchemaPtr, indptrArrayPtr, 1
4662
);
63+
createRelResult.close();
4764

4865
// Verify edges: person0→person1 (w=10), person0→person2 (w=20), person1→person2 (w=30)
49-
const rows = arrowConn.querySync(
66+
const qr = arrowConn.querySync(
5067
"MATCH (a:person_default)-[r:knows_default]->(b:person_default) " +
5168
"RETURN a.id, r.weight, b.id ORDER BY a.id, b.id"
52-
).getAllSync();
69+
);
70+
const rows = qr.getAllSync();
71+
qr.close();
5372

5473
assert.deepEqual(
5574
rows.map(r => [Number(r["a.id"]), Number(r["r.weight"]), Number(r["b.id"])]),
@@ -62,19 +81,23 @@ describe("createArrowRelTableSync CSR", function () {
6281
const { nodeSchemaPtr, nodeArrayPtr, indicesSchemaPtr, indicesArrayPtr,
6382
indptrSchemaPtr, indptrArrayPtr } = LbugNative.createArrowCSRTestData(dstColName);
6483

65-
arrowConn.createArrowTableSync("person_custom", nodeSchemaPtr, nodeArrayPtr, 1);
84+
const createPersonResult = arrowConn.createArrowTableSync("person_custom", nodeSchemaPtr, nodeArrayPtr, 1);
85+
createPersonResult.close();
6686

67-
arrowConn.createArrowRelTableSync(
87+
const createRelResult = arrowConn.createArrowRelTableSync(
6888
"knows_custom", "person_custom", "person_custom",
6989
indicesSchemaPtr, indicesArrayPtr, 1,
7090
indptrSchemaPtr, indptrArrayPtr, 1,
7191
dstColName
7292
);
93+
createRelResult.close();
7394

74-
const rows = arrowConn.querySync(
95+
const qr = arrowConn.querySync(
7596
"MATCH (a:person_custom)-[r:knows_custom]->(b:person_custom) " +
7697
"RETURN a.id, r.weight, b.id ORDER BY a.id, b.id"
77-
).getAllSync();
98+
);
99+
const rows = qr.getAllSync();
100+
qr.close();
78101

79102
assert.deepEqual(
80103
rows.map(r => [Number(r["a.id"]), Number(r["r.weight"]), Number(r["b.id"])]),
@@ -86,7 +109,8 @@ describe("createArrowRelTableSync CSR", function () {
86109
// Fresh data since each call to createArrowTableSync transfers ownership
87110
const d = LbugNative.createArrowCSRTestData("to");
88111

89-
arrowConn.createArrowTableSync("person_flat_test", d.nodeSchemaPtr, d.nodeArrayPtr, 1);
112+
const createPersonResult = arrowConn.createArrowTableSync("person_flat_test", d.nodeSchemaPtr, d.nodeArrayPtr, 1);
113+
createPersonResult.close();
90114

91115
// Flat path requires "from"/"to" columns in the Arrow batch.
92116
// Passing a batch with only "id" should trigger a Ladybug error (confirming flat path is used).

0 commit comments

Comments
 (0)