Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { FirewallService } from "../../src/firewall/firewallService";
import { AddFirewallRuleState } from "../../src/sharedInterfaces/addFirewallRule";
import { ApiStatus } from "../../src/sharedInterfaces/webview";
import * as azureHelperStubs from "./azureHelperStubs";
import { observeWebviewReady } from "./utils";

suite("AddFirewallRuleWebviewController Tests", () => {
let sandbox: sinon.SinonSandbox;
Expand Down Expand Up @@ -128,6 +129,7 @@ suite("AddFirewallRuleWebviewController Tests", () => {
},
mockFirewallService,
);
observeWebviewReady(controller);

return await controller.initialized;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import SqlToolsServerClient from "../../src/languageservice/serviceclient";
import { VSCodeAzureSubscriptionProvider } from "@microsoft/vscode-azext-azureauth";
import {
initializeIconUtils,
observeWebviewReady,
stubGetCapabilitiesRequest,
stubMessageBoxes,
stubPreviewService,
Expand Down Expand Up @@ -89,6 +90,7 @@ suite("ConnectionDialogWebviewController Tests", () => {
let mockObjectExplorerProvider: sinon.SinonStubbedInstance<ObjectExplorerProvider>;
let azureAccountService: sinon.SinonStubbedInstance<AzureAccountService>;
let serviceClientMock: sinon.SinonStubbedInstance<SqlToolsServerClient>;
let loadVscodeEntraDataAsyncStub: sinon.SinonStub;

const testMruConnection = {
profileSource: CredentialsQuickPickItemType.Mru,
Expand Down Expand Up @@ -168,13 +170,22 @@ suite("ConnectionDialogWebviewController Tests", () => {
mainController.azureAccountService = azureAccountService;
await mainController["initializeObjectExplorer"](mockObjectExplorerProvider);

// Neutralize the constructor's fire-and-forget background VS Code Entra data load so it
// does not call updateState after the controller is disposed (which surfaces as an
// unhandled "Cannot send notification on disposed controller" rejection). Tests that
// exercise the real method restore this stub first.
loadVscodeEntraDataAsyncStub = sandbox
.stub(ConnectionDialogWebviewController.prototype, "loadVscodeEntraDataAsync")
.resolves();

controller = new ConnectionDialogWebviewController(
mockContext,
mainController,
mockObjectExplorerProvider,
undefined /* connection to edit */,
);

observeWebviewReady(controller);
await controller.initialized;
});

Expand Down Expand Up @@ -291,6 +302,7 @@ suite("ConnectionDialogWebviewController Tests", () => {
mockObjectExplorerProvider,
undefined,
);
observeWebviewReady(controller);
await controller.initialized;

expect(controller.state.recentConnections).to.have.lengthOf(1);
Expand Down Expand Up @@ -326,6 +338,7 @@ suite("ConnectionDialogWebviewController Tests", () => {
mockObjectExplorerProvider,
undefined,
);
observeWebviewReady(controller);
await controller.initialized;

expect(controller.state.recentConnections).to.have.lengthOf(1);
Expand Down Expand Up @@ -362,6 +375,7 @@ suite("ConnectionDialogWebviewController Tests", () => {
mockObjectExplorerProvider,
undefined,
);
observeWebviewReady(controller);
await controller.initialized;

expect(controller.state.recentConnections).to.have.lengthOf(1);
Expand Down Expand Up @@ -398,6 +412,7 @@ suite("ConnectionDialogWebviewController Tests", () => {
mockObjectExplorerProvider,
undefined,
);
observeWebviewReady(controller);
await controller.initialized;

expect(controller.state.recentConnections).to.have.lengthOf(1);
Expand All @@ -419,6 +434,7 @@ suite("ConnectionDialogWebviewController Tests", () => {
mockObjectExplorerProvider,
editedConnection,
);
observeWebviewReady(controller);
await controller.initialized;

expect(controller["_connectionBeingEdited"]).to.deep.equal(
Expand Down Expand Up @@ -451,6 +467,7 @@ suite("ConnectionDialogWebviewController Tests", () => {
mockObjectExplorerProvider,
editedConnection,
);
observeWebviewReady(controller);
await controller.initialized;

expect(controller["_connectionBeingEdited"]).to.deep.equal(
Expand Down Expand Up @@ -971,6 +988,7 @@ suite("ConnectionDialogWebviewController Tests", () => {
.resolves([mockTenants[0], mockTenants[1]]);

// Pre-populate the Entra account and tenant caches
loadVscodeEntraDataAsyncStub.restore();
await controller["loadVscodeEntraDataAsync"]();

const testConnection = {
Expand Down Expand Up @@ -1750,6 +1768,7 @@ suite("ConnectionDialogWebviewController Tests", () => {
});

test("getAzureActionButtons uses VS Code sign-in when VS Code account mode is enabled", async () => {
loadVscodeEntraDataAsyncStub.restore();
stubPreviewService(sandbox, { [PreviewFeature.UseVscodeAccountsForEntraMFA]: true });

sandbox
Expand Down
34 changes: 30 additions & 4 deletions extensions/mssql/test/unit/connectionManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ suite("ConnectionManager Tests", () => {
};
}

setup(() => {
setup(async () => {
stubPreviewService(sandbox, {
[PreviewFeature.UseVscodeAccountsForEntraMFA]: true,
});
Expand All @@ -750,6 +750,7 @@ suite("ConnectionManager Tests", () => {
sendNotificationStub = mockServiceClient.sendNotification as sinon.SinonStub;
sendNotificationStub.reset();
connectionManager["client"] = mockServiceClient;
await connectionManager.initialized;
});

test("happy path: sends TokenRefreshedNotification with token and expiresOn", async () => {
Expand Down Expand Up @@ -809,7 +810,7 @@ suite("ConnectionManager Tests", () => {
} as IConnectionInfo;
}

setup(() => {
setup(async () => {
// Test the MSAL (non-VS-Code-accounts) path
stubPreviewService(sandbox, { [PreviewFeature.UseVscodeAccountsForEntraMFA]: false });
connectionManager = new ConnectionManager(
Expand All @@ -823,6 +824,7 @@ suite("ConnectionManager Tests", () => {
undefined,
undefined,
);
await connectionManager.initialized;

mockAccountStore = sandbox.createStubInstance(AccountStore);
mockAzureController = sandbox.createStubInstance(MsalAzureController);
Expand Down Expand Up @@ -887,12 +889,19 @@ suite("ConnectionManager Tests", () => {
let handlePasswordBasedCredentialsStub: sinon.SinonStub;
let refreshEntraTokenIfNeededStub: sinon.SinonStub;

setup(() => {
setup(async () => {
mockConnectionStore = sandbox.createStubInstance(ConnectionStore);
mockConnectionUI = sandbox.createStubInstance(ConnectionUI);
mockAccountStore = sandbox.createStubInstance(AccountStore);
mockAzureController = sandbox.createStubInstance(AzureController);

const initializedDeferred = new Deferred<void>();
initializedDeferred.resolve();
Object.defineProperty(mockConnectionStore, "initialized", {
get: () => initializedDeferred,
});
Comment on lines +898 to +902
Comment on lines +898 to +902
mockConnectionStore.readAllConnections.resolves([]);

const mockPrompter = sandbox.createStubInstance(TestPrompter);

// Create a new connection manager instance for this test suite
Expand All @@ -901,6 +910,10 @@ suite("ConnectionManager Tests", () => {
mockStatusView,
mockPrompter,
mockLogger,
undefined, // serviceClient
mockVscodeWrapper,
mockConnectionStore,
mockCredentialStore,
);

testConnectionManager.connectionStore = mockConnectionStore;
Expand All @@ -916,6 +929,7 @@ suite("ConnectionManager Tests", () => {
refreshEntraTokenIfNeededStub = sandbox
.stub(testConnectionManager, "refreshEntraTokenIfNeeded")
.resolves();
await testConnectionManager.initialized;
});

teardown(() => {
Expand Down Expand Up @@ -1093,21 +1107,33 @@ suite("ConnectionManager Tests", () => {
let mockConnectionUI: sinon.SinonStubbedInstance<ConnectionUI>;
let testConnectionManager: ConnectionManager;

setup(() => {
setup(async () => {
mockConnectionStore = sandbox.createStubInstance(ConnectionStore);
mockConnectionUI = sandbox.createStubInstance(ConnectionUI);

const initializedDeferred = new Deferred<void>();
initializedDeferred.resolve();
Object.defineProperty(mockConnectionStore, "initialized", {
get: () => initializedDeferred,
});
Comment on lines +1113 to +1117
Comment on lines +1113 to +1117
mockConnectionStore.readAllConnections.resolves([]);

const mockPrompter = sandbox.createStubInstance(TestPrompter);

testConnectionManager = new ConnectionManager(
mockContext,
mockStatusView,
mockPrompter,
mockLogger,
undefined, // serviceClient
mockVscodeWrapper,
mockConnectionStore,
mockCredentialStore,
);

testConnectionManager.connectionStore = mockConnectionStore;
(testConnectionManager as any)._connectionUI = mockConnectionUI;
await testConnectionManager.initialized;
});

teardown(() => {
Expand Down
28 changes: 26 additions & 2 deletions extensions/mssql/test/unit/dockerUtilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,20 @@ suite("Docker Utilities", () => {

setup(async () => {
sandbox = sinon.createSandbox();

// execDockerCommand() calls fixPath() (the "fix-path" package) on every invocation, which
// synchronously spawns a real login shell via child_process.spawnSync to resolve $PATH.
// That is not covered by the spawn stub and adds ~0.6-1.2s per call. Stub spawnSync so the
// PATH fix-up is a no-op and tests don't shell out for real.
sandbox.stub(childProcess, "spawnSync").returns({
pid: 0,
output: [],
stdout: Buffer.from(""),
stderr: Buffer.from(""),
status: 0,
signal: null,
} as unknown as ReturnType<typeof childProcess.spawnSync>);

node = {
connectionProfile: {
containerName: "testContainer",
Expand Down Expand Up @@ -568,7 +582,12 @@ suite("Docker Utilities", () => {
return createSpawnSuccessProcess("");
});

const result = await dockerUtils.startDocker();
// startDocker() polls for readiness via setInterval(2000ms). Use fake timers so the test
// advances through the poll instead of waiting ~2s in real time.
const clock = sandbox.useFakeTimers();
const resultPromise = dockerUtils.startDocker();
await clock.tickAsync(2100);
const result = await resultPromise;
expect(result.error).to.equal(undefined);
expect(result.success, "Docker should start successfully on Windows").to.be.true;
expect(spawnStub.callCount).to.be.greaterThanOrEqual(4);
Expand All @@ -595,7 +614,12 @@ suite("Docker Utilities", () => {
return createSpawnSuccessProcess("");
});

const result = await dockerUtils.startDocker();
// startDocker() polls for readiness via setInterval(2000ms). Use fake timers so the test
// advances through the poll instead of waiting ~2s in real time.
const clock = sandbox.useFakeTimers();
const resultPromise = dockerUtils.startDocker();
await clock.tickAsync(2100);
const result = await resultPromise;
expect(result.success, "Docker should start successfully on Linux").to.be.true;
expect(spawnStub.callCount).to.be.greaterThanOrEqual(3);
});
Expand Down
38 changes: 36 additions & 2 deletions extensions/mssql/test/unit/objectExplorerService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ suite("OE Service Tests", () => {
sandbox = sinon.createSandbox();
mockConnectionManager = sandbox.createStubInstance(ConnectionManager);
mockConnectionStore = sandbox.createStubInstance(ConnectionStore);
mockConnectionStore.readAllConnections.resolves([]);
mockConnectionStore.readAllConnectionGroups.resolves([createMockRootConnectionGroup()]);
mockClient = sandbox.createStubInstance(SqlToolsServiceClient);
stubLogger(sandbox);

Expand Down Expand Up @@ -1086,6 +1088,11 @@ suite("OE Service Tests", () => {
mockClient = sandbox.createStubInstance(SqlToolsServiceClient);
mockAccountStore = sandbox.createStubInstance(AccountStore);

const mockConnectionStore = sandbox.createStubInstance(ConnectionStore);
mockConnectionStore.readAllConnectionGroups.resolves([createMockRootConnectionGroup()]);
mockConnectionStore.readAllConnections.resolves([]);
mockConnectionManager.connectionStore = mockConnectionStore;

mockConnectionManager.client = mockClient;
(mockConnectionManager as any)._connectionUI = mockConnectionUI;
(mockConnectionManager as any)._firewallService = mockFirewallService;
Expand Down Expand Up @@ -1305,6 +1312,8 @@ suite("OE Service Tests", () => {
mockConnectionUI = sandbox.createStubInstance(ConnectionUI);
mockClient = sandbox.createStubInstance(SqlToolsServiceClient);
mockConnectionStore = sandbox.createStubInstance(ConnectionStore);
mockConnectionStore.readAllConnections.resolves([]);
mockConnectionStore.readAllConnectionGroups.resolves([createMockRootConnectionGroup()]);

mockConnectionManager.connectionStore = mockConnectionStore;
mockConnectionManager.client = mockClient;
Expand Down Expand Up @@ -1497,6 +1506,13 @@ suite("OE Service Tests", () => {
const mockClient = sandbox.createStubInstance(SqlToolsServiceClient);
stubLogger(sandbox);
mockConnectionManager.client = mockClient;

const mockConnectionStore = sandbox.createStubInstance(ConnectionStore);
mockConnectionStore.readAllConnectionGroups.resolves([createMockRootConnectionGroup()]);
mockConnectionStore.readAllConnections.resolves([]);

mockConnectionManager.connectionStore = mockConnectionStore;

objectExplorerService = new ObjectExplorerService(mockConnectionManager, () => {});
objectExplorerService.initialized.resolve();
});
Expand Down Expand Up @@ -1558,6 +1574,7 @@ suite("OE Service Tests", () => {

mockConnectionStore = sandbox.createStubInstance(ConnectionStore);
mockConnectionStore.readAllConnections.resolves([]);
mockConnectionStore.readAllConnectionGroups.resolves([createMockRootConnectionGroup()]);

const mockConnectionManager = sandbox.createStubInstance(ConnectionManager);
mockConnectionManager.client = mockClient;
Expand Down Expand Up @@ -2241,11 +2258,13 @@ suite("OE Service Tests", () => {
let startActivityStub: sinon.SinonStub;
let objectExplorerService: ObjectExplorerService;

setup(() => {
setup(async () => {
sandbox = sinon.createSandbox();
mockConnectionManager = sandbox.createStubInstance(ConnectionManager);
mockClient = sandbox.createStubInstance(SqlToolsServiceClient);
mockConnectionStore = sandbox.createStubInstance(ConnectionStore);
mockConnectionStore.readAllConnections.resolves([]);
mockConnectionStore.readAllConnectionGroups.resolves([createMockRootConnectionGroup()]);
mockConnectionManager.connectionStore = mockConnectionStore;
mockConnectionManager.client = mockClient;

Expand All @@ -2260,6 +2279,14 @@ suite("OE Service Tests", () => {
});
stubLogger(sandbox);
objectExplorerService = new ObjectExplorerService(mockConnectionManager, () => {});
// Wait for the constructor's fire-and-forget initialize() (which calls
// getRootNodes) to settle before tests override the connection store stubs.
await objectExplorerService.initialized;
// Clear telemetry call history recorded by the setup-time initialize() so
// tests observe only the calls they make themselves.
startActivityStub.resetHistory();
endStub.resetHistory();
endFailedStub.resetHistory();
});

teardown(() => {
Expand Down Expand Up @@ -2551,6 +2578,8 @@ suite("OE Service Tests", () => {
mockClient = sandbox.createStubInstance(SqlToolsServiceClient);
mockConnectionManager = sandbox.createStubInstance(ConnectionManager);
mockConnectionStore = sandbox.createStubInstance(ConnectionStore);
mockConnectionStore.readAllConnections.resolves([]);
mockConnectionStore.readAllConnectionGroups.resolves([createMockRootConnectionGroup()]);

mockConnectionManager.client = mockClient;
mockConnectionManager.connectionStore = mockConnectionStore;
Expand Down Expand Up @@ -2794,7 +2823,12 @@ suite("OE Service Tests", () => {
sandbox = sinon.createSandbox();
messageBoxes = stubMessageBoxes(sandbox);
mockConnectionManager = sandbox.createStubInstance(ConnectionManager);
mockConnectionManager.connectionStore = sandbox.createStubInstance(ConnectionStore);
const entraMockConnectionStore = sandbox.createStubInstance(ConnectionStore);
entraMockConnectionStore.readAllConnections.resolves([]);
entraMockConnectionStore.readAllConnectionGroups.resolves([
createMockRootConnectionGroup(),
]);
mockConnectionManager.connectionStore = entraMockConnectionStore;
mockConnectionManager.client = sandbox.createStubInstance(SqlToolsServiceClient);

stubLogger(sandbox);
Expand Down
Loading
Loading