blob: cbd3a5c3f620bb432a6d8ecd2a7499be4ee44d57 [file]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import { Session } from "../../src/client/Session";
describe("SessionDataSet E2E Tests", () => {
let session: Session;
const IOTDB_HOST = process.env.IOTDB_HOST || "localhost";
const IOTDB_PORT = parseInt(process.env.IOTDB_PORT || "6667");
const IOTDB_USER = process.env.IOTDB_USER || "root";
const IOTDB_PASSWORD = process.env.IOTDB_PASSWORD || "root";
beforeAll(async () => {
session = new Session({
host: IOTDB_HOST,
port: IOTDB_PORT,
username: IOTDB_USER,
password: IOTDB_PASSWORD,
});
try {
await session.open();
} catch (error) {
console.warn("Could not connect to IoTDB. Tests will be skipped.");
try {
await session.close();
} catch {
// Ignore cleanup errors
}
}
}, 60000);
afterAll(async () => {
if (session && session.isOpen()) {
await session.close();
}
});
test("Should iterate through query results using SessionDataSet", async () => {
if (!session.isOpen()) {
console.log("Skipping test - no IoTDB connection");
return;
}
// Setup test data
try {
await session.executeNonQueryStatement("DELETE DATABASE root.test");
} catch (e) {
// Ignore if doesn't exist
}
await session.executeNonQueryStatement("CREATE DATABASE root.test");
await session.executeNonQueryStatement(
"CREATE TIMESERIES root.test.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN",
);
await session.executeNonQueryStatement(
"CREATE TIMESERIES root.test.d1.s2 WITH DATATYPE=TEXT, ENCODING=PLAIN",
);
// Insert test data
const now = Date.now();
for (let i = 0; i < 10; i++) {
await session.executeNonQueryStatement(
`INSERT INTO root.test.d1(timestamp, s1, s2) VALUES(${now + i}, ${i}, 'value${i}')`,
);
}
// Query using SessionDataSet
const dataSet = await session.executeQueryStatement(
"SELECT s1, s2 FROM root.test.d1",
);
expect(dataSet).toBeDefined();
// IoTDB returns fully qualified column names
expect(dataSet.getColumnNames()).toEqual([
"root.test.d1.s1",
"root.test.d1.s2",
]);
let rowCount = 0;
const rows: any[] = [];
while (await dataSet.hasNext()) {
const row = dataSet.next();
rowCount++;
rows.push({
timestamp: row.getTimestamp(),
s1: row.getInt("root.test.d1.s1"),
s2: row.getString("root.test.d1.s2"),
});
}
expect(rowCount).toBe(10);
expect(rows[0].s1).toBe(0);
expect(rows[0].s2).toBe("value0");
expect(rows[9].s1).toBe(9);
expect(rows[9].s2).toBe("value9");
await dataSet.close();
}, 60000);
test("Should handle large result sets with lazy loading", async () => {
if (!session.isOpen()) {
console.log("Skipping test - no IoTDB connection");
return;
}
await session.executeNonQueryStatement(
"CREATE TIMESERIES root.test.large.d1.value WITH DATATYPE=INT32, ENCODING=PLAIN",
);
// Insert 100 rows
const now = Date.now();
for (let i = 0; i < 100; i++) {
await session.executeNonQueryStatement(
`INSERT INTO root.test.large.d1(timestamp, value) VALUES(${now + i}, ${i})`,
);
}
// Query with small fetch size to test lazy loading
const smallSession = new Session({
host: IOTDB_HOST,
port: IOTDB_PORT,
username: IOTDB_USER,
password: IOTDB_PASSWORD,
fetchSize: 10, // Small fetch size to trigger multiple fetches
});
await smallSession.open();
const dataSet = await smallSession.executeQueryStatement(
"SELECT value FROM root.test.large.d1",
);
let rowCount = 0;
while (await dataSet.hasNext()) {
const row = dataSet.next();
expect(row.getInt("root.test.large.d1.value")).toBe(rowCount);
rowCount++;
}
expect(rowCount).toBe(100);
await dataSet.close();
await smallSession.close();
}, 90000);
test("Should support column access by name and index", async () => {
if (!session.isOpen()) {
console.log("Skipping test - no IoTDB connection");
return;
}
await session.executeNonQueryStatement(
"CREATE TIMESERIES root.test.column.d1.temperature WITH DATATYPE=FLOAT, ENCODING=PLAIN",
);
await session.executeNonQueryStatement(
"CREATE TIMESERIES root.test.column.d1.humidity WITH DATATYPE=DOUBLE, ENCODING=PLAIN",
);
const now = Date.now();
await session.executeNonQueryStatement(
`INSERT INTO root.test.column.d1(timestamp, temperature, humidity) VALUES(${now}, 23.5, 65.2)`,
);
const dataSet = await session.executeQueryStatement(
"SELECT temperature, humidity FROM root.test.column.d1",
);
expect(await dataSet.hasNext()).toBe(true);
const row = dataSet.next();
// Access by column name (fully qualified)
expect(row.getFloat("root.test.column.d1.temperature")).toBeCloseTo(
23.5,
1,
);
expect(row.getDouble("root.test.column.d1.humidity")).toBeCloseTo(65.2, 1);
// Access by index
expect(row.getFloatByIndex(0)).toBeCloseTo(23.5, 1);
expect(row.getDoubleByIndex(1)).toBeCloseTo(65.2, 1);
// Find column index (using fully qualified names)
expect(dataSet.findColumn("root.test.column.d1.temperature")).toBe(0);
expect(dataSet.findColumn("root.test.column.d1.humidity")).toBe(1);
await dataSet.close();
}, 60000);
test("Should handle null values correctly", async () => {
if (!session.isOpen()) {
console.log("Skipping test - no IoTDB connection");
return;
}
await session.executeNonQueryStatement(
"CREATE TIMESERIES root.test.null_test.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN",
);
await session.executeNonQueryStatement(
"CREATE TIMESERIES root.test.null_test.d1.s2 WITH DATATYPE=INT32, ENCODING=PLAIN",
);
const now = Date.now();
// Insert row with one null value
await session.executeNonQueryStatement(
`INSERT INTO root.test.null_test.d1(timestamp, s1) VALUES(${now}, 100)`,
);
const dataSet = await session.executeQueryStatement(
"SELECT s1, s2 FROM root.test.null_test.d1",
);
expect(await dataSet.hasNext()).toBe(true);
const row = dataSet.next();
expect(row.isNull("root.test.null_test.d1.s1")).toBe(false);
expect(row.isNull("root.test.null_test.d1.s2")).toBe(true);
expect(row.getInt("root.test.null_test.d1.s1")).toBe(100);
await dataSet.close();
}, 60000);
test("Should support toArray() for backward compatibility", async () => {
if (!session.isOpen()) {
console.log("Skipping test - no IoTDB connection");
return;
}
// Setup test data
try {
await session.executeNonQueryStatement("DELETE DATABASE root.test");
} catch (e) {
// Ignore
}
await session.executeNonQueryStatement("CREATE DATABASE root.test");
await session.executeNonQueryStatement(
"CREATE TIMESERIES root.test.d1.value WITH DATATYPE=INT32, ENCODING=PLAIN",
);
const now = Date.now();
for (let i = 0; i < 5; i++) {
await session.executeNonQueryStatement(
`INSERT INTO root.test.d1(timestamp, value) VALUES(${now + i}, ${i})`,
);
}
const dataSet = await session.executeQueryStatement(
"SELECT value FROM root.test.d1",
);
const allRows = await dataSet.toArray();
expect(allRows.length).toBe(5);
expect(allRows[0][1]).toBe(0); // timestamp, value
expect(allRows[4][1]).toBe(4);
// Dataset should be closed after toArray()
expect(await dataSet.hasNext()).toBe(false);
}, 60000);
test("Should properly cleanup resources on close", async () => {
if (!session.isOpen()) {
console.log("Skipping test - no IoTDB connection");
return;
}
const dataSet = await session.executeQueryStatement("SHOW DATABASES");
// Close without iterating through all results
await dataSet.close();
// Should not be able to iterate after close
expect(await dataSet.hasNext()).toBe(false);
expect(dataSet.isClosed_()).toBe(true);
}, 60000);
});