{
} else if (process.platform === "darwin") {
result = await tryExecuteCommand("/bin", killPidCommand(pid), log);
}
- ext.outputChannel.appendLine(`Destroying q process result: ${result}`);
+ kdbOutputLog(`Destroying q process result: ${result}`, "INFO");
}
function killPidCommand(pid: number): string {
diff --git a/src/validators/kdbValidator.ts b/src/validators/kdbValidator.ts
index 9b3f803d..bdbe6655 100644
--- a/src/validators/kdbValidator.ts
+++ b/src/validators/kdbValidator.ts
@@ -104,5 +104,7 @@ export function validateTls(input: string | undefined): string | undefined {
}
export function isAliasInUse(alias: string): boolean {
- return ext.kdbConnectionAliasList.includes(alias);
+ return !!ext.kdbConnectionAliasList.find(
+ (item) => item.toLowerCase() === alias.toLowerCase(),
+ );
}
diff --git a/src/webview/components/kdbNewConnectionView.ts b/src/webview/components/kdbNewConnectionView.ts
index a106e398..63455338 100644
--- a/src/webview/components/kdbNewConnectionView.ts
+++ b/src/webview/components/kdbNewConnectionView.ts
@@ -52,6 +52,7 @@ export class KdbNewConnectionView extends LitElement {
alias: "",
server: "",
auth: true,
+ realm: "",
};
this.bundledServer = {
serverName: "127.0.0.1",
@@ -220,6 +221,29 @@ export class KdbNewConnectionView extends LitElement {
`;
}
+ renderRealm() {
+ return html`
+
+ {
+ /* istanbul ignore next */
+ const value = (event.target as HTMLSelectElement).value;
+ /* istanbul ignore next */
+ this.insightsServer.realm = value;
+ }}"
+ >Define Realm (optional)
+
+
+ Specify the Keycloak realm for authentication. Use this field to connect
+ to a specific realm as configured on your server.
+
+ `;
+ }
+
tabClickAction(tabNumber: number) {
const config =
this.tabConfig[tabNumber as keyof typeof this.tabConfig] ||
@@ -381,6 +405,14 @@ export class KdbNewConnectionView extends LitElement {
${this.renderConnAddress(ServerType.INSIGHTS)}
+
+
+
+ Advanced
+ ${this.renderRealm()}
+
+
+
diff --git a/test/runTest.ts b/test/runTest.ts
index 774e2184..0d8d9a71 100644
--- a/test/runTest.ts
+++ b/test/runTest.ts
@@ -27,7 +27,7 @@ async function main() {
// load the instrumented files
extensionTestsPath = path.join(
__dirname,
- "../../out-cov/test/suite/index",
+ "../../out-cov/test/suite/index"
);
// signal that the coverage data should be gathered
@@ -37,7 +37,6 @@ async function main() {
await runTests({
extensionDevelopmentPath,
extensionTestsPath,
- version: "1.89.1",
});
} catch (err) {
console.log(err);
diff --git a/test/suite/commands.test.ts b/test/suite/commands.test.ts
index 92a692cd..fbd7ed44 100644
--- a/test/suite/commands.test.ts
+++ b/test/suite/commands.test.ts
@@ -15,7 +15,6 @@ import assert from "assert";
import mock from "mock-fs";
import * as sinon from "sinon";
import * as vscode from "vscode";
-import { TreeItemCollapsibleState, window } from "vscode";
import * as dataSourceCommand from "../../src/commands/dataSourceCommand";
import * as installTools from "../../src/commands/installTools";
import * as serverCommand from "../../src/commands/serverCommand";
@@ -34,6 +33,7 @@ import {
InsightsNode,
KdbNode,
KdbTreeProvider,
+ MetaObjectPayloadNode,
} from "../../src/services/kdbTreeProvider";
import { KdbResultsViewProvider } from "../../src/services/resultsPanelProvider";
import * as coreUtils from "../../src/utils/core";
@@ -50,6 +50,10 @@ import { InsightsConnection } from "../../src/classes/insightsConnection";
import * as workspaceCommand from "../../src/commands/workspaceCommand";
import { MetaObject } from "../../src/models/meta";
import { WorkspaceTreeProvider } from "../../src/services/workspaceTreeProvider";
+import { GetDataError } from "../../src/models/data";
+import * as clientCommand from "../../src/commands/clientCommands";
+import { LanguageClient } from "vscode-languageclient/node";
+import { MetaContentProvider } from "../../src/services/metaContentProvider";
describe("dataSourceCommand", () => {
afterEach(() => {
@@ -57,7 +61,7 @@ describe("dataSourceCommand", () => {
mock.restore();
});
- it("should add a data source", async () => {
+ it.skip("should add a data source", async () => {
mock({
"/temp": {
".kdb-datasources": {
@@ -88,7 +92,7 @@ describe("dataSourceCommand2", () => {
alias: "insightsserveralias",
auth: true,
},
- TreeItemCollapsibleState.None,
+ vscode.TreeItemCollapsibleState.None,
);
const insightsConn = new InsightsConnection(insightsNode.label, insightsNode);
const uriTest: vscode.Uri = vscode.Uri.parse("test");
@@ -629,6 +633,7 @@ describe("dataSourceCommand2", () => {
afterEach(() => {
sinon.restore();
+ ext.isResultsTabVisible = false;
});
it("should show an error message if not connected to an Insights server", async () => {
@@ -665,7 +670,7 @@ describe("dataSourceCommand2", () => {
insightsConn.meta = dummyMeta;
getMetaStub.resolves(dummyMeta);
getDataInsightsStub.resolves({ arrayBuffer: ab, error: "" });
- isVisibleStub.returns(true);
+ ext.isResultsTabVisible = true;
await dataSourceCommand.runDataSource(
dummyFileContent as DataSourceFiles,
insightsConn.connLabel,
@@ -684,7 +689,7 @@ describe("dataSourceCommand2", () => {
dummyFileContent.dataSource.selectedType = DataSourceTypes.API;
getMetaStub.resolves(dummyMeta);
getDataInsightsStub.resolves({ arrayBuffer: ab, error: "" });
- isVisibleStub.returns(false);
+ ext.isResultsTabVisible = false;
await dataSourceCommand.runDataSource(
dummyFileContent as DataSourceFiles,
insightsConn.connLabel,
@@ -703,7 +708,7 @@ describe("dataSourceCommand2", () => {
dummyFileContent.dataSource.selectedType = DataSourceTypes.SQL;
getMetaStub.resolves(dummyMeta);
getDataInsightsStub.resolves({ arrayBuffer: ab, error: "" });
- isVisibleStub.returns(false);
+ ext.isResultsTabVisible = false;
await dataSourceCommand.runDataSource(
dummyFileContent as DataSourceFiles,
insightsConn.connLabel,
@@ -798,6 +803,16 @@ describe("dataSourceCommand2", () => {
sinon.assert.neverCalledWith(writeQueryResultsToViewStub);
sinon.assert.neverCalledWith(writeQueryResultsToConsoleStub);
});
+
+ it("should handle errors correctly", async () => {
+ retrieveConnStub.throws(new Error("Test error"));
+ await dataSourceCommand.runDataSource(
+ dummyFileContent as DataSourceFiles,
+ insightsConn.connLabel,
+ "test-file.kdb.json",
+ );
+ windowMock.expects("showErrorMessage").once().withArgs("Test error");
+ });
});
describe("populateScratchpad", async () => {
@@ -848,6 +863,29 @@ describe("dataSourceCommand2", () => {
.withArgs("Please connect to an Insights server");
});
});
+
+ describe("parseError", () => {
+ let kdbOutputLogStub: sinon.SinonStub;
+
+ beforeEach(() => {
+ kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog");
+ });
+ afterEach(() => {
+ sinon.restore();
+ });
+
+ it("should call kdbOutputLog and return error if error does not have buffer", () => {
+ const error: GetDataError = "test error";
+
+ const result = dataSourceCommand.parseError(error);
+
+ assert(kdbOutputLogStub.calledOnce);
+ assert(
+ kdbOutputLogStub.calledWith(`[DATASOURCE] Error: ${error}`, "ERROR"),
+ );
+ assert.deepEqual(result, { error });
+ });
+ });
});
describe("installTools", () => {
@@ -874,14 +912,14 @@ describe("serverCommand", () => {
alias: "insightsserveralias",
auth: true,
},
- TreeItemCollapsibleState.None,
+ vscode.TreeItemCollapsibleState.None,
);
const kdbNode = new KdbNode(
["child1"],
"testElement",
servers["testServer"],
- TreeItemCollapsibleState.None,
+ vscode.TreeItemCollapsibleState.None,
);
const insights = {
testInsight: {
@@ -896,10 +934,10 @@ describe("serverCommand", () => {
ext.serverProvider = undefined;
});
- it("should call the New Connection Panel Renderer", () => {
+ it("should call the New Connection Panel Renderer", async () => {
const newConnectionPanelStub = sinon.stub(NewConnectionPannel, "render");
-
- serverCommand.addNewConnection();
+ ext.context = {};
+ await serverCommand.addNewConnection();
sinon.assert.calledOnce(newConnectionPanelStub);
sinon.restore();
});
@@ -1669,6 +1707,7 @@ describe("serverCommand", () => {
disconnectStub,
getServersStub,
getHashStub,
+ getKeyStub,
getInsightsStub,
removeLocalConnectionContextStub,
updateServersStub,
@@ -1680,6 +1719,7 @@ describe("serverCommand", () => {
getServersStub = sinon.stub(coreUtils, "getServers");
getInsightsStub = sinon.stub(coreUtils, "getInsights");
getHashStub = sinon.stub(coreUtils, "getHash");
+ getKeyStub = sinon.stub(coreUtils, "getKeyForServerName");
removeLocalConnectionContextStub = sinon.stub(
coreUtils,
"removeLocalConnectionContext",
@@ -1698,7 +1738,7 @@ describe("serverCommand", () => {
it("should remove connection and refresh server provider", async () => {
indexOfStub.returns(1);
getServersStub.returns({ testKey: {} });
- getHashStub.returns("testKey");
+ getKeyStub.returns("testKey");
await serverCommand.removeConnection(kdbNode);
@@ -1715,7 +1755,7 @@ describe("serverCommand", () => {
ext.connectedContextStrings.push(kdbNode.label);
indexOfStub.returns(1);
getServersStub.returns({ testKey: {} });
- getHashStub.returns("testKey");
+ getKeyStub.returns("testKey");
await serverCommand.removeConnection(kdbNode);
assert.ok(updateServersStub.calledOnce);
@@ -1752,6 +1792,90 @@ describe("serverCommand", () => {
windowErrorStub.calledOnce;
});
});
+
+ describe("refreshGetMeta", () => {
+ let refreshGetMetaStub, refreshAllGetMetasStub: sinon.SinonStub;
+ beforeEach(() => {
+ refreshGetMetaStub = sinon.stub(
+ ConnectionManagementService.prototype,
+ "refreshGetMeta",
+ );
+ refreshAllGetMetasStub = sinon.stub(
+ ConnectionManagementService.prototype,
+ "refreshAllGetMetas",
+ );
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ });
+
+ it("should call refreshGetMeta if connLabel is provided", async () => {
+ await serverCommand.refreshGetMeta("test");
+
+ sinon.assert.calledOnce(refreshGetMetaStub);
+ sinon.assert.calledWith(refreshGetMetaStub, "test");
+ sinon.assert.notCalled(refreshAllGetMetasStub);
+ });
+
+ it("should call refreshAllGetMetas if connLabel is not provided", async () => {
+ await serverCommand.refreshGetMeta();
+
+ sinon.assert.notCalled(refreshGetMetaStub);
+ sinon.assert.calledOnce(refreshAllGetMetasStub);
+ });
+ });
+
+ describe("openMeta", () => {
+ let sandbox: sinon.SinonSandbox;
+ const node = new MetaObjectPayloadNode(
+ [],
+ "meta",
+ "",
+ vscode.TreeItemCollapsibleState.None,
+ "meta",
+ "connLabel",
+ );
+ const connService = new ConnectionManagementService();
+
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+ sandbox.spy(vscode.workspace, "registerTextDocumentContentProvider");
+ sandbox.spy(vscode.workspace, "openTextDocument");
+ sandbox.spy(vscode.window, "showTextDocument");
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ sinon.restore();
+ });
+
+ it("should call functions once for valid meta content", async () => {
+ sinon
+ .stub(ConnectionManagementService.prototype, "retrieveMetaContent")
+ .returns('{"test": []}');
+ await serverCommand.openMeta(node);
+ sinon.assert.calledOnce(
+ vscode.workspace.registerTextDocumentContentProvider as sinon.SinonSpy,
+ );
+ sinon.assert.calledOnce(
+ vscode.workspace.openTextDocument as sinon.SinonSpy,
+ );
+ sinon.assert.calledOnce(vscode.window.showTextDocument as sinon.SinonSpy);
+ });
+
+ it("should not call some functions for invalid meta content", async () => {
+ sinon.stub(connService, "retrieveMetaContent").returns("");
+ await serverCommand.openMeta(node);
+ sinon.assert.calledOnce(
+ vscode.workspace.registerTextDocumentContentProvider as sinon.SinonSpy,
+ );
+ sinon.assert.notCalled(
+ vscode.workspace.openTextDocument as sinon.SinonSpy,
+ );
+ sinon.assert.notCalled(vscode.window.showTextDocument as sinon.SinonSpy);
+ });
+ });
});
describe("walkthroughCommand", () => {
@@ -1832,14 +1956,14 @@ describe("workspaceCommand", () => {
});
describe("pickConnection", () => {
it("should pick from available servers", async () => {
- sinon.stub(window, "showQuickPick").value(async () => "test");
+ sinon.stub(vscode.window, "showQuickPick").value(async () => "test");
const result = await workspaceCommand.pickConnection(
vscode.Uri.file("test.kdb.q"),
);
assert.strictEqual(result, "test");
});
it("should return undefined from (none)", async () => {
- sinon.stub(window, "showQuickPick").value(async () => "(none)");
+ sinon.stub(vscode.window, "showQuickPick").value(async () => "(none)");
const result = await workspaceCommand.pickConnection(
vscode.Uri.file("test.kdb.q"),
);
@@ -1878,17 +2002,26 @@ describe("workspaceCommand", () => {
let windowErrorStub,
windowWithProgressStub,
windowShowInfo,
- workspaceFolderStub: sinon.SinonStub;
+ workspaceFolderStub,
+ tokenOnCancellationRequestedStub,
+ kdbOutputLogStub: sinon.SinonStub;
beforeEach(() => {
windowErrorStub = sinon.stub(vscode.window, "showErrorMessage");
windowWithProgressStub = sinon.stub(vscode.window, "withProgress");
windowShowInfo = sinon.stub(vscode.window, "showInformationMessage");
workspaceFolderStub = sinon.stub(vscode.workspace, "workspaceFolders");
+ tokenOnCancellationRequestedStub = sinon.stub();
+ windowWithProgressStub.callsFake((options, task) => {
+ const token = {
+ onCancellationRequested: tokenOnCancellationRequestedStub,
+ };
+ task({}, token);
+ });
+
+ kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog");
});
afterEach(() => {
- windowErrorStub.restore();
- windowWithProgressStub.restore();
- windowShowInfo.restore();
+ sinon.restore();
});
it("should show info message if old files do not exist", async () => {
ext.oldDSformatExists = false;
@@ -1913,5 +2046,90 @@ describe("workspaceCommand", () => {
sinon.assert.notCalled(windowErrorStub);
sinon.assert.notCalled(windowShowInfo);
});
+
+ it("should log cancellation if user cancels the request", async () => {
+ ext.oldDSformatExists = true;
+ workspaceFolderStub.get(() => [
+ {
+ uri: { fsPath: "path/to/workspace" },
+ name: "workspace",
+ index: 0,
+ },
+ ]);
+
+ tokenOnCancellationRequestedStub.callsFake((callback) => callback());
+
+ await workspaceCommand.importOldDSFiles();
+
+ sinon.assert.calledOnce(kdbOutputLogStub);
+ sinon.assert.calledWith(
+ kdbOutputLogStub,
+ "[DATASOURCE] User cancelled the old DS files import.",
+ "INFO",
+ );
+ });
+ });
+});
+
+describe("clientCommands", () => {
+ const client = sinon.createStubInstance(LanguageClient);
+ let executeBlock;
+ let toggleParameterCache;
+
+ beforeEach(() => {
+ const context = { subscriptions: [] };
+ sinon.stub(vscode.commands, "registerCommand").value((a, b) => b);
+ clientCommand.connectClientCommands(context, client);
+ executeBlock = context.subscriptions[0];
+ toggleParameterCache = context.subscriptions[1];
+ ext.activeTextEditor = {
+ options: { insertSpaces: true, indentSize: 4 },
+ selection: { active: new vscode.Position(0, 0) },
+ document: {
+ uri: vscode.Uri.file("/tmp/some.q"),
+ getText: () => "",
+ },
+ };
+ });
+ afterEach(() => {
+ sinon.restore();
+ ext.activeTextEditor = undefined;
+ });
+ describe("executeBlock", () => {
+ it("should execute current block", async () => {
+ sinon
+ .stub(client, "sendRequest")
+ .value(async () => new vscode.Range(0, 0, 1, 1));
+ sinon.stub(workspaceCommand, "runActiveEditor").value(() => {});
+ await executeBlock(client);
+ assert.deepEqual(
+ ext.activeTextEditor.selection,
+ new vscode.Selection(0, 0, 1, 1),
+ );
+ });
+ });
+ describe("kdb.toggleParameterCache", () => {
+ it("should add parameter cache for single line functions", async () => {
+ let edit: vscode.WorkspaceEdit;
+ sinon.stub(client, "sendRequest").value(async () => ({
+ params: ["a"],
+ start: new vscode.Position(0, 0),
+ end: new vscode.Position(0, 10),
+ }));
+ sinon.stub(vscode.workspace, "applyEdit").value(async (a) => (edit = a));
+ await toggleParameterCache(client);
+ assert.strictEqual(edit.size, 1);
+ });
+ it("should add parameter cache for multi line functions", async () => {
+ let edit: vscode.WorkspaceEdit;
+ sinon.stub(client, "sendRequest").value(async () => ({
+ params: ["a"],
+ start: new vscode.Position(0, 0),
+ end: new vscode.Position(1, 10),
+ }));
+ sinon.stub(vscode.workspace, "applyEdit").value(async (a) => (edit = a));
+ await toggleParameterCache(client);
+ assert.strictEqual(edit.size, 1);
+ });
});
});
diff --git a/test/suite/qLangServer.test.ts b/test/suite/qLangServer.test.ts
index 33105551..f8eaf616 100644
--- a/test/suite/qLangServer.test.ts
+++ b/test/suite/qLangServer.test.ts
@@ -28,10 +28,10 @@ const context = { includeDeclaration: true };
describe("qLangServer", () => {
let server: QLangServer;
- function createDocument(content: string) {
+ function createDocument(content: string, offset?: number) {
content = content.trim();
const document = TextDocument.create("test.q", "q", 1, content);
- const position = document.positionAt(content.length);
+ const position = document.positionAt(offset || content.length);
const textDocument = TextDocumentIdentifier.create("test.q");
sinon.stub(server.documents, "get").value(() => document);
return {
@@ -55,6 +55,8 @@ describe("qLangServer", () => {
onRenameRequest() {},
onCompletion() {},
onDidChangeConfiguration() {},
+ onRequest() {},
+ onSelectionRanges() {},
});
const params = {
@@ -77,6 +79,7 @@ describe("qLangServer", () => {
assert.ok(capabilities.definitionProvider);
assert.ok(capabilities.renameProvider);
assert.ok(capabilities.completionProvider);
+ assert.ok(capabilities.selectionRangeProvider);
});
});
@@ -207,4 +210,124 @@ describe("qLangServer", () => {
assert.strictEqual(result.length, 2);
});
});
+
+ describe("onExpressionRange", () => {
+ it("should return the range of the expression", () => {
+ const params = createDocument("a:1;");
+ const result = server.onExpressionRange(params);
+ assert.strictEqual(result.start.line, 0);
+ assert.strictEqual(result.start.character, 0);
+ assert.strictEqual(result.end.line, 0);
+ assert.strictEqual(result.end.character, 3);
+ });
+ it("should return the range of the expression", () => {
+ const params = createDocument("a");
+ const result = server.onExpressionRange(params);
+ assert.strictEqual(result.start.line, 0);
+ assert.strictEqual(result.start.character, 0);
+ assert.strictEqual(result.end.line, 0);
+ assert.strictEqual(result.end.character, 1);
+ });
+ it("should return null", () => {
+ const params = createDocument("");
+ const result = server.onExpressionRange(params);
+ assert.strictEqual(result, null);
+ });
+ it("should return null", () => {
+ const params = createDocument(";");
+ const result = server.onExpressionRange(params);
+ assert.strictEqual(result, null);
+ });
+ it("should return null", () => {
+ const params = createDocument("/a:1");
+ const result = server.onExpressionRange(params);
+ assert.strictEqual(result, null);
+ });
+ });
+
+ describe("onExpressionRange", () => {
+ it("should return range for simple expression", () => {
+ const params = createDocument("a:1;");
+ const result = server.onExpressionRange(params);
+ assert.ok(result);
+ assert.strictEqual(result.start.line, 0);
+ assert.strictEqual(result.start.character, 0);
+ assert.strictEqual(result.end.line, 0);
+ assert.strictEqual(result.end.character, 3);
+ });
+ it("should return range for lambda", () => {
+ const params = createDocument("a:{b:1;b};");
+ const result = server.onExpressionRange(params);
+ assert.ok(result);
+ assert.strictEqual(result.start.line, 0);
+ assert.strictEqual(result.start.character, 0);
+ assert.strictEqual(result.end.line, 0);
+ assert.strictEqual(result.end.character, 9);
+ });
+ it("should skip comments", () => {
+ const params = createDocument("a:1 /comment", 1);
+ const result = server.onExpressionRange(params);
+ assert.ok(result);
+ assert.strictEqual(result.start.line, 0);
+ assert.strictEqual(result.start.character, 0);
+ assert.strictEqual(result.end.line, 0);
+ assert.strictEqual(result.end.character, 3);
+ });
+ });
+
+ describe("omParameterCache", () => {
+ it("should cache paramater", () => {
+ const params = createDocument("{[a;b]}");
+ const result = server.onParameterCache(params);
+ assert.ok(result);
+ assert.deepEqual(result.params, ["a", "b"]);
+ assert.strictEqual(result.start.line, 0);
+ assert.strictEqual(result.start.character, 6);
+ assert.strictEqual(result.end.line, 0);
+ assert.strictEqual(result.end.character, 6);
+ });
+ it("should cache paramater", () => {
+ const params = createDocument("{[a;b]\n }");
+ const result = server.onParameterCache(params);
+ assert.ok(result);
+ assert.deepEqual(result.params, ["a", "b"]);
+ assert.strictEqual(result.start.line, 0);
+ assert.strictEqual(result.start.character, 6);
+ assert.strictEqual(result.end.line, 1);
+ assert.strictEqual(result.end.character, 1);
+ });
+ it("should return null", () => {
+ const params = createDocument("{[]}");
+ const result = server.onParameterCache(params);
+ assert.strictEqual(result, null);
+ });
+ it("should return null", () => {
+ const params = createDocument("{}");
+ const result = server.onParameterCache(params);
+ assert.strictEqual(result, null);
+ });
+ it("should return null", () => {
+ const params = createDocument("a:1;");
+ const result = server.onParameterCache(params);
+ assert.strictEqual(result, null);
+ });
+ it("should return null", () => {
+ const params = createDocument("");
+ const result = server.onParameterCache(params);
+ assert.strictEqual(result, null);
+ });
+ });
+
+ describe("onSelectionRanges", () => {
+ it("should return identifier range", () => {
+ const params = createDocument(".test.ref");
+ const result = server.onSelectionRanges({
+ textDocument: params.textDocument,
+ positions: [params.position],
+ });
+ assert.strictEqual(result.length, 1);
+ assert.strictEqual(result[0].range.start.character, 0);
+ assert.strictEqual(result[0].range.end.character, 9);
+ });
+ });
});
diff --git a/test/suite/services.test.ts b/test/suite/services.test.ts
index 39e3d649..71163bc5 100644
--- a/test/suite/services.test.ts
+++ b/test/suite/services.test.ts
@@ -35,9 +35,11 @@ import {
signOut,
} from "../../src/services/kdbInsights/codeFlowLogin";
import {
+ InsightsMetaNode,
InsightsNode,
KdbNode,
KdbTreeProvider,
+ MetaObjectPayloadNode,
QCategoryNode,
QNamespaceNode,
QServerNode,
@@ -58,12 +60,70 @@ import {
} from "../../src/services/workspaceTreeProvider";
import Path from "path";
import * as utils from "../../src/utils/getUri";
-import { MetaObject } from "../../src/models/meta";
+import { MetaInfoType, MetaObject } from "../../src/models/meta";
import { CompletionProvider } from "../../src/services/completionProvider";
+import { MetaContentProvider } from "../../src/services/metaContentProvider";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const codeFlow = require("../../src/services/kdbInsights/codeFlowLogin");
+const dummyMeta: MetaObject = {
+ header: {
+ ac: "0",
+ agg: ":127.0.0.1:5070",
+ ai: "",
+ api: ".kxi.getMeta",
+ client: ":127.0.0.1:5050",
+ corr: "CorrHash",
+ http: "json",
+ logCorr: "logCorrHash",
+ protocol: "gw",
+ rc: "0",
+ rcvTS: "2099-05-22T11:06:33.650000000",
+ retryCount: "0",
+ to: "2099-05-22T11:07:33.650000000",
+ userID: "dummyID",
+ userName: "testUser",
+ },
+ payload: {
+ rc: [
+ {
+ api: 3,
+ agg: 1,
+ assembly: 1,
+ schema: 1,
+ rc: "dummy-rc",
+ labels: [{ kxname: "dummy-assembly" }],
+ started: "2023-10-04T17:20:57.659088747",
+ },
+ ],
+ dap: [],
+ api: [],
+ agg: [
+ {
+ aggFn: ".sgagg.aggFnDflt",
+ custom: false,
+ full: true,
+ metadata: {
+ description: "dummy desc.",
+ params: [{ description: "dummy desc." }],
+ return: { description: "dummy desc." },
+ misc: {},
+ },
+ procs: [],
+ },
+ ],
+ assembly: [
+ {
+ assembly: "dummy-assembly",
+ kxname: "dummy-assembly",
+ tbls: ["dummyTbl"],
+ },
+ ],
+ schema: [],
+ },
+};
+
describe("kdbTreeProvider", () => {
let servers: Server;
let insights: Insights;
@@ -544,6 +604,112 @@ describe("kdbTreeProvider", () => {
"QServer node creation failed",
);
});
+
+ describe("InsightsMetaNode", () => {
+ it("should initialize fields correctly", () => {
+ const node = new InsightsMetaNode(
+ ["child1", "child2"],
+ "testLabel",
+ "testDetails",
+ TreeItemCollapsibleState.Collapsed,
+ "testConnLabel",
+ );
+
+ assert.deepStrictEqual(node.children, ["child1", "child2"]);
+ assert.strictEqual(node.label, "testLabel");
+ assert.strictEqual(
+ node.collapsibleState,
+ TreeItemCollapsibleState.Collapsed,
+ );
+ assert.strictEqual(node.connLabel, "testConnLabel");
+ assert.strictEqual(node.description, "");
+ assert.strictEqual(node.contextValue, "meta");
+ });
+
+ it("should return empty string from getDescription", () => {
+ const node = new InsightsMetaNode(
+ [],
+ "",
+ "",
+ TreeItemCollapsibleState.None,
+ "",
+ );
+
+ assert.strictEqual(node.getDescription(), "");
+ });
+ });
+
+ describe("MetaObjectPayloadNode", () => {
+ it("should initialize fields correctly", () => {
+ const node = new MetaObjectPayloadNode(
+ ["child1", "child2"],
+ "testLabel",
+ "testDetails",
+ TreeItemCollapsibleState.Collapsed,
+ "testIcon",
+ "testConnLabel",
+ );
+
+ assert.deepStrictEqual(node.children, ["child1", "child2"]);
+ assert.strictEqual(node.label, "testLabel");
+ assert.strictEqual(
+ node.collapsibleState,
+ TreeItemCollapsibleState.Collapsed,
+ );
+ assert.strictEqual(node.coreIcon, "testIcon");
+ assert.strictEqual(node.connLabel, "testConnLabel");
+ assert.strictEqual(node.description, "");
+ });
+ });
+
+ describe("getChildren", () => {
+ const kdbProvider = new KdbTreeProvider(servers, insights);
+ insights = {
+ testInsight: {
+ alias: "testInsightsAlias",
+ server: "testInsightsName",
+ auth: false,
+ },
+ };
+ insightNode = new InsightsNode(
+ ["child1"],
+ "testInsight",
+ insights["testInsight"],
+ TreeItemCollapsibleState.None,
+ );
+ insightNode.contextValue = "testInsight";
+
+ afterEach(() => {
+ ext.kdbinsightsNodes.length = 0;
+ sinon.restore();
+ });
+
+ it("Should return categories for insights connection", async () => {
+ ext.kdbinsightsNodes.push("testInsight");
+ kdbProvider.getChildren(insightNode);
+ const result = await kdbProvider.getChildren(insightNode);
+ assert.notStrictEqual(result, undefined);
+ });
+
+ it("should return metaObjects for parent", async () => {
+ const connMng = new ConnectionManagementService();
+ const metaNode = new InsightsMetaNode(
+ [],
+ "testMeta",
+ "",
+ TreeItemCollapsibleState.None,
+ "insightsConn",
+ );
+ const insightsConn = new InsightsConnection(
+ insightNode.label,
+ insightNode,
+ );
+ sinon.stub(connMng, "retrieveConnectedConnection").returns(insightsConn);
+ insightsConn.meta = dummyMeta;
+ const result = await kdbProvider.getChildren(metaNode);
+ assert.notStrictEqual(result, undefined);
+ });
+ });
});
describe("Code flow login service tests", () => {
@@ -560,13 +726,13 @@ describe("Code flow login service tests", () => {
it("Should return a correct login", async () => {
sinon.stub(codeFlow, "signIn").returns(token);
- const result = await signIn("http://localhost");
+ const result = await signIn("http://localhost", "insights");
assert.strictEqual(result, token, "Invalid token returned");
});
it("Should execute a correct logout", async () => {
sinon.stub(axios, "post").resolves(Promise.resolve({ data: token }));
- const result = await signOut("http://localhost", "token");
+ const result = await signOut("http://localhost", "insights", "token");
assert.strictEqual(result, undefined, "Invalid response from logout");
});
@@ -574,6 +740,7 @@ describe("Code flow login service tests", () => {
sinon.stub(axios, "post").resolves(Promise.resolve({ data: token }));
const result = await refreshToken(
"http://localhost",
+ "insights",
JSON.stringify(token),
);
assert.strictEqual(
@@ -584,7 +751,7 @@ describe("Code flow login service tests", () => {
});
it("Should not return token from secret store", async () => {
- const result = await getCurrentToken("", "testalias");
+ const result = await getCurrentToken("", "testalias", "insights");
assert.strictEqual(
result,
undefined,
@@ -593,7 +760,7 @@ describe("Code flow login service tests", () => {
});
it("Should not return token from secret store", async () => {
- const result = await getCurrentToken("testserver", "");
+ const result = await getCurrentToken("testserver", "", "insights");
assert.strictEqual(
result,
undefined,
@@ -601,9 +768,11 @@ describe("Code flow login service tests", () => {
);
});
- it.skip("Should not sign in if link is not opened", async () => {
- sinon.stub(env, "openExternal").value(async () => false);
- await assert.rejects(() => signIn("http://127.0.0.1"));
+ it("Should continue sign in if link is copied", async () => {
+ sinon.stub(env, "openExternal").value(async () => {
+ throw new Error();
+ });
+ await assert.rejects(() => signIn("http://127.0.0.1", "insights"));
});
});
@@ -1085,6 +1254,107 @@ describe("connectionManagerService", () => {
sinon.assert.calledOnce(getMetaStub);
});
});
+
+ describe("getMetaInfoType", () => {
+ it("should return correct MetaInfoType for valid input", () => {
+ assert.strictEqual(
+ connectionManagerService.getMetaInfoType("meta".toUpperCase()),
+ MetaInfoType.META,
+ );
+ assert.strictEqual(
+ connectionManagerService.getMetaInfoType("schema".toUpperCase()),
+ MetaInfoType.SCHEMA,
+ );
+ assert.strictEqual(
+ connectionManagerService.getMetaInfoType("api".toUpperCase()),
+ MetaInfoType.API,
+ );
+ assert.strictEqual(
+ connectionManagerService.getMetaInfoType("agg".toUpperCase()),
+ MetaInfoType.AGG,
+ );
+ assert.strictEqual(
+ connectionManagerService.getMetaInfoType("dap".toUpperCase()),
+ MetaInfoType.DAP,
+ );
+ assert.strictEqual(
+ connectionManagerService.getMetaInfoType("rc".toUpperCase()),
+ MetaInfoType.RC,
+ );
+ });
+
+ it("should return undefined for invalid input", () => {
+ assert.strictEqual(
+ connectionManagerService.getMetaInfoType("invalid"),
+ undefined,
+ );
+ });
+ });
+
+ describe("retrieveMetaContent", () => {
+ let sandbox: sinon.SinonSandbox;
+
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ });
+
+ it("should return empty string for invalid meta info type", () => {
+ sandbox
+ .stub(connectionManagerService, "getMetaInfoType")
+ .returns(undefined);
+ assert.strictEqual(
+ connectionManagerService.retrieveMetaContent("connLabel", "invalid"),
+ "",
+ );
+ });
+
+ it("should return empty string for not connected connection", () => {
+ sandbox
+ .stub(connectionManagerService, "getMetaInfoType")
+ .returns(MetaInfoType.META);
+ sandbox
+ .stub(connectionManagerService, "retrieveConnectedConnection")
+ .returns(undefined);
+ assert.strictEqual(
+ connectionManagerService.retrieveMetaContent("connLabel", "meta"),
+ "",
+ );
+ });
+
+ it("should return empty string for local connection", () => {
+ sandbox
+ .stub(connectionManagerService, "getMetaInfoType")
+ .returns(MetaInfoType.META);
+ sandbox
+ .stub(connectionManagerService, "retrieveConnectedConnection")
+ .returns(localConn);
+ assert.strictEqual(
+ connectionManagerService.retrieveMetaContent("connLabel", "meta"),
+ "",
+ );
+ });
+
+ it("should return meta object for valid input", () => {
+ insightsConn.meta = dummyMeta;
+ sandbox
+ .stub(connectionManagerService, "getMetaInfoType")
+ .returns(MetaInfoType.META);
+ sandbox
+ .stub(connectionManagerService, "retrieveConnectedConnection")
+ .returns(insightsConn);
+ assert.strictEqual(
+ connectionManagerService.retrieveMetaContent(
+ insightsConn.connLabel,
+ "meta",
+ ),
+ JSON.stringify(dummyMeta.payload),
+ );
+ });
+ });
});
describe("dataSourceEditorProvider", () => {
@@ -1412,3 +1682,37 @@ describe("CompletionProvider", () => {
assert.ok(items);
});
});
+
+describe("MetaContentProvider", () => {
+ let metaContentProvider: MetaContentProvider;
+ let uri: Uri;
+
+ beforeEach(() => {
+ metaContentProvider = new MetaContentProvider();
+ uri = Uri.parse("foo://example.com");
+ });
+
+ it("should update content and fire onDidChange event", () => {
+ const content = "new content";
+ const spy = sinon.spy();
+
+ metaContentProvider.onDidChange(spy);
+
+ metaContentProvider.update(uri, content);
+
+ assert.strictEqual(
+ metaContentProvider.provideTextDocumentContent(uri),
+ content,
+ );
+ assert(spy.calledOnceWith(uri));
+ });
+
+ it("should provide text document content", () => {
+ const content = "content";
+ metaContentProvider.update(uri, content);
+ assert.strictEqual(
+ metaContentProvider.provideTextDocumentContent(uri),
+ content,
+ );
+ });
+});
diff --git a/test/suite/utils.test.ts b/test/suite/utils.test.ts
index 4ebbc944..3572807c 100644
--- a/test/suite/utils.test.ts
+++ b/test/suite/utils.test.ts
@@ -25,6 +25,7 @@ import { InsightsNode, KdbNode } from "../../src/services/kdbTreeProvider";
import { QueryHistoryProvider } from "../../src/services/queryHistoryProvider";
import { KdbResultsViewProvider } from "../../src/services/resultsPanelProvider";
import * as coreUtils from "../../src/utils/core";
+import * as cpUtils from "../../src/utils/cpUtils";
import * as dataSourceUtils from "../../src/utils/dataSource";
import * as decodeUtils from "../../src/utils/decode";
import * as executionUtils from "../../src/utils/execution";
@@ -70,6 +71,36 @@ describe("Utils", () => {
});
describe("core", () => {
+ describe("checkOpenSslInstalled", () => {
+ let tryExecuteCommandStub: sinon.SinonStub;
+ let kdbOutputLogStub: sinon.SinonStub;
+ beforeEach(() => {
+ tryExecuteCommandStub = sinon.stub(cpUtils, "tryExecuteCommand");
+ kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog");
+ });
+
+ afterEach(() => {
+ tryExecuteCommandStub.restore();
+ kdbOutputLogStub.restore();
+ });
+
+ it("should return null if OpenSSL is not installed", async () => {
+ tryExecuteCommandStub.resolves({ code: 1, cmdOutput: "" });
+
+ const result = await coreUtils.checkOpenSslInstalled();
+
+ assert.strictEqual(result, null);
+ });
+
+ it("should handle errors correctly", async () => {
+ const error = new Error("Test error");
+ tryExecuteCommandStub.rejects(error);
+
+ const result = await coreUtils.checkOpenSslInstalled();
+
+ assert.strictEqual(result, null);
+ });
+ });
describe("setOutputWordWrapper", () => {
let getConfigurationStub: sinon.SinonStub;
beforeEach(() => {
@@ -250,6 +281,55 @@ describe("Utils", () => {
assert.strictEqual(result, "");
});
});
+
+ describe("coreLogs", () => {
+ ext.outputChannel = vscode.window.createOutputChannel("kdb");
+ let appendLineSpy, showErrorMessageSpy: sinon.SinonSpy;
+ beforeEach(() => {
+ appendLineSpy = sinon.spy(
+ vscode.window.createOutputChannel("testChannel"),
+ "appendLine",
+ );
+ showErrorMessageSpy = sinon.spy(vscode.window, "showErrorMessage");
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ });
+
+ it("kdbOutputLog should log message with date and type", () => {
+ const message = "test message";
+ const type = "INFO";
+
+ coreUtils.kdbOutputLog(message, type);
+
+ appendLineSpy.calledOnce;
+ appendLineSpy.calledWithMatch(message);
+ appendLineSpy.calledWithMatch(type);
+ });
+
+ it("tokenUndefinedError should log and show error message", () => {
+ const connLabel = "test connection";
+
+ coreUtils.tokenUndefinedError(connLabel);
+
+ appendLineSpy.calledOnce;
+ showErrorMessageSpy.calledOnce;
+ appendLineSpy.calledWithMatch(connLabel);
+ showErrorMessageSpy.calledWithMatch(connLabel);
+ });
+
+ it("invalidUsernameJWT should log and show error message", () => {
+ const connLabel = "test connection";
+
+ coreUtils.invalidUsernameJWT(connLabel);
+
+ appendLineSpy.calledOnce;
+ showErrorMessageSpy.calledOnce;
+ appendLineSpy.calledWithMatch(connLabel);
+ showErrorMessageSpy.calledWithMatch(connLabel);
+ });
+ });
});
describe("dataSource", () => {
@@ -267,6 +347,11 @@ describe("Utils", () => {
assert.strictEqual(result, "2021-01-01T00:00:00.000000000");
});
+ it("convertTimeToTimestamp", () => {
+ const result = dataSourceUtils.convertTimeToTimestamp("testTime");
+ assert.strictEqual(result, "");
+ });
+
it("getConnectedInsightsNode", () => {
const result = dataSourceUtils.getConnectedInsightsNode();
assert.strictEqual(result, "");
@@ -899,6 +984,11 @@ describe("Utils", () => {
});
describe("handleWSResults", () => {
+ afterEach(() => {
+ sinon.restore();
+ ext.isResultsTabVisible = false;
+ });
+
it("should return no results found", () => {
const ab = new ArrayBuffer(128);
const result = queryUtils.handleWSResults(ab);
@@ -914,18 +1004,14 @@ describe("Utils", () => {
rows: [{ Value: "10" }],
};
const uriTest: vscode.Uri = vscode.Uri.parse("test");
- ext.resultsViewProvider = new KdbResultsViewProvider(uriTest);
+ ext.isResultsTabVisible = true;
const qtableStub = sinon
.stub(QTable.default, "toLegacy")
.returns(expectedOutput);
- const isVisibleStub = sinon
- .stub(ext.resultsViewProvider, "isVisible")
- .returns(true);
const convertRowsSpy = sinon.spy(queryUtils, "convertRows");
const result = queryUtils.handleWSResults(ab);
sinon.assert.notCalled(convertRowsSpy);
assert.strictEqual(result, expectedOutput);
- sinon.restore();
});
});
diff --git a/test/suite/webview.test.ts b/test/suite/webview.test.ts
index b888ab4e..740d8948 100644
--- a/test/suite/webview.test.ts
+++ b/test/suite/webview.test.ts
@@ -479,6 +479,7 @@ describe("KdbNewConnectionView", () => {
alias: "",
server: "",
auth: true,
+ realm: "",
};
const data = view["data"];
assert.deepEqual(data, expectedData);