Adds unit tests for vcore quickstart feature code (#1618)

* Safety checkin

* Adding vcoremongo to Main

* Safety checkin

* Adding vcoremongo to Main

* Safety commit

* Safety checkin

* Adding vcoremongo to Main

* Safety commit

* Integrating mongo shell

* Safety checkin

* Adding vcoremongo to Main

* Safety commit

* Integrating mongo shell

* Safety checkin

* Safety commit

* Enable mongo shell in its own tab

* Safety checkin

* Adding vcoremongo to Main

* Safety commit

* Integrating mongo shell

* Safety checkin

* Safety commit

* Safety commit

* Integrating mongo shell

* Safety checkin

* Safety commit

* Enable mongo shell in its own tab

* Adding message

* Integrated mongo shell

* Moving Juno endpoint back to prod

* Fixed command bar unit tests

* Fixing spelling

* Adds unit tests for vcore quickstart feature code

* Fix lint
This commit is contained in:
vchske 2023-09-19 17:12:41 -07:00 committed by GitHub
parent ae5811306b
commit 754354dbf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 335 additions and 12 deletions

View File

@ -32,6 +32,20 @@ const testCassandraAccount: DataModels.DatabaseAccount = {
}, },
}; };
const testPostgresAccount: DataModels.DatabaseAccount = {
...testAccount,
properties: {
postgresqlEndpoint: "https://testPostgresEndpoint.azure.com/",
},
};
const testVCoreMongoAccount: DataModels.DatabaseAccount = {
...testAccount,
properties: {
vcoreMongoEndpoint: "https://testVCoreMongoEndpoint.azure.com/",
},
};
const testNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = { const testNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
authToken: "authToken", authToken: "authToken",
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com", notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com",
@ -50,6 +64,18 @@ const testCassandraNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInf
forwardingId: "Id", forwardingId: "Id",
}; };
const testPostgresNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
authToken: "authToken",
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/postgresql",
forwardingId: "Id",
};
const testVCoreMongoNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
authToken: "authToken",
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/mongovcore",
forwardingId: "Id",
};
describe("NotebookTerminalComponent", () => { describe("NotebookTerminalComponent", () => {
it("renders terminal", () => { it("renders terminal", () => {
const props: NotebookTerminalComponentProps = { const props: NotebookTerminalComponentProps = {
@ -94,4 +120,27 @@ describe("NotebookTerminalComponent", () => {
const wrapper = shallow(<NotebookTerminalComponent {...props} />); const wrapper = shallow(<NotebookTerminalComponent {...props} />);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
it("renders Postgres shell", () => {
const props: NotebookTerminalComponentProps = {
databaseAccount: testPostgresAccount,
notebookServerInfo: testPostgresNotebookServerInfo,
tabId: undefined,
};
const wrapper = shallow(<NotebookTerminalComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders vCore Mongo shell", () => {
const props: NotebookTerminalComponentProps = {
databaseAccount: testVCoreMongoAccount,
notebookServerInfo: testVCoreMongoNotebookServerInfo,
tabId: undefined,
username: "username",
};
const wrapper = shallow(<NotebookTerminalComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
}); });

View File

@ -1,5 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NotebookTerminalComponent renders Postgres shell 1`] = `
<div
className="notebookTerminalContainer"
>
<iframe
onLoad={[Function]}
src="terminal.html"
title="Terminal to Notebook Server"
/>
</div>
`;
exports[`NotebookTerminalComponent renders cassandra shell 1`] = ` exports[`NotebookTerminalComponent renders cassandra shell 1`] = `
<div <div
className="notebookTerminalContainer" className="notebookTerminalContainer"
@ -47,3 +59,15 @@ exports[`NotebookTerminalComponent renders terminal 1`] = `
/> />
</div> </div>
`; `;
exports[`NotebookTerminalComponent renders vCore Mongo shell 1`] = `
<div
className="notebookTerminalContainer"
>
<iframe
onLoad={[Function]}
src="terminal.html"
title="Terminal to Notebook Server"
/>
</div>
`;

View File

@ -343,6 +343,31 @@ describe("CommandBarComponentButtonFactory tests", () => {
}); });
}); });
describe("Open Postgres and vCore Mongo buttons", () => {
const openPostgresShellButtonLabel = "Open PSQL shell";
const openVCoreMongoShellButtonLabel = "Open MongoDB (vcore) shell";
beforeAll(() => {
mockExplorer = {} as Explorer;
});
it("creates Postgres shell button", () => {
const buttons = CommandBarComponentButtonFactory.createPostgreButtons(mockExplorer);
const openPostgresShellButton = buttons.find(
(button) => button.commandButtonLabel === openPostgresShellButtonLabel
);
expect(openPostgresShellButton).toBeDefined();
});
it("creates vCore Mongo shell button", () => {
const buttons = CommandBarComponentButtonFactory.createVCoreMongoButtons(mockExplorer);
const openVCoreMongoShellButton = buttons.find(
(button) => button.commandButtonLabel === openVCoreMongoShellButtonLabel
);
expect(openVCoreMongoShellButton).toBeDefined();
});
});
describe("GitHub buttons", () => { describe("GitHub buttons", () => {
const connectToGitHubBtnLabel = "Connect to GitHub"; const connectToGitHubBtnLabel = "Connect to GitHub";
const manageGitHubSettingsBtnLabel = "Manage GitHub settings"; const manageGitHubSettingsBtnLabel = "Manage GitHub settings";

View File

@ -0,0 +1,91 @@
import { DatabaseAccount, FirewallRule } from "Contracts/DataModels";
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
import { updateUserContext } from "UserContext";
import { mockFunction } from "Utils/JestUtils";
import { armRequest } from "Utils/arm/request";
import React from "react";
jest.mock("Utils/arm/request");
const armRequestMock = mockFunction(armRequest);
describe("CheckFirewallRule tests", () => {
const apiVersion = "2023-03-15-preview";
const rulePredicate = (rule: FirewallRule) =>
rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255";
let isAllPublicIPAddressEnabled: boolean;
const setIsAllPublicIPAddressEnabled = jest.fn((value) => (isAllPublicIPAddressEnabled = value));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const useStateMock: any = (initState: any) => [initState, setIsAllPublicIPAddressEnabled];
jest.spyOn(React, "useState").mockImplementation(useStateMock);
beforeAll(() => {
updateUserContext({
databaseAccount: {
id: "testResourceId",
} as DatabaseAccount,
});
});
it("returns 'all public IP addresses' is enabled for account with the proper firewall rule", async () => {
armRequestMock.mockResolvedValueOnce({
value: [
{
id: "resourceId",
name: "AllowAll",
type: "Microsoft.DocumentDB/mongoClusters/firewallRules",
properties: {
provisioningState: "Succeeded",
startIpAddress: "0.0.0.0",
endIpAddress: "255.255.255.255",
},
},
],
});
await checkFirewallRules(apiVersion, rulePredicate, setIsAllPublicIPAddressEnabled);
expect(isAllPublicIPAddressEnabled).toBe(true);
});
it("returns 'all public IP addresses' is NOT enabled for account without the proper firewall rule", async () => {
armRequestMock.mockResolvedValueOnce([
{
id: "resourceId",
name: "AllowAll",
type: "Microsoft.DocumentDB/mongoClusters/firewallRules",
properties: {
provisioningState: "Succeeded",
startIpAddress: "10.10.10.10",
endIpAddress: "10.10.10.10",
},
},
]);
await checkFirewallRules(apiVersion, rulePredicate, setIsAllPublicIPAddressEnabled);
expect(isAllPublicIPAddressEnabled).toBe(false);
});
it("sets message for account without the proper firewall rule", async () => {
armRequestMock.mockResolvedValueOnce([
{
id: "resourceId",
name: "AllowAll",
type: "Microsoft.DocumentDB/mongoClusters/firewallRules",
properties: {
provisioningState: "Succeeded",
startIpAddress: "0.0.0.0",
endIpAddress: "255.255.255.255",
},
},
]);
const warningMessage = "This is a warning message";
let warningMessageResult: string;
const warningMessageFunc = (msg: string) => (warningMessageResult = msg);
await checkFirewallRules(apiVersion, rulePredicate, undefined, warningMessageFunc, warningMessage);
expect(warningMessageResult).toEqual(warningMessage);
});
});

View File

@ -55,6 +55,17 @@ export const defaultAllowedBackendEndpoints: ReadonlyArray<string> = [
"https://localhost:1234", "https://localhost:1234",
]; ];
export const PortalBackendIPs: { [key: string]: string[] } = {
"https://main.documentdb.ext.azure.com": ["104.42.195.92", "40.76.54.131"],
// DE doesn't talk to prod2 (main2) but it might be added
//"https://main2.documentdb.ext.azure.com": ["104.42.196.69"],
"https://main.documentdb.ext.azure.cn": ["139.217.8.252"],
"https://main.documentdb.ext.azure.us": ["52.244.48.71"],
// Add ussec and usnat when endpoint address is known:
//ussec: ["29.26.26.67", "29.26.26.66"],
//usnat: ["7.28.202.68"],
};
export const allowedMongoProxyEndpoints: ReadonlyArray<string> = [ export const allowedMongoProxyEndpoints: ReadonlyArray<string> = [
"https://main.documentdb.ext.azure.com", "https://main.documentdb.ext.azure.com",
"https://main.documentdb.ext.azure.cn", "https://main.documentdb.ext.azure.cn",

4
src/Utils/JestUtils.ts Normal file
View File

@ -0,0 +1,4 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function mockFunction<T extends (...args: any[]) => any>(fn: T): jest.MockedFunction<T> {
return fn as jest.MockedFunction<T>;
}

View File

@ -0,0 +1,106 @@
import { resetConfigContext, updateConfigContext } from "ConfigContext";
import { DatabaseAccount, IpRule } from "Contracts/DataModels";
import { updateUserContext } from "UserContext";
import { PortalBackendIPs } from "Utils/EndpointValidation";
import { getNetworkSettingsWarningMessage } from "./NetworkUtility";
describe("NetworkUtility tests", () => {
describe("getNetworkSettingsWarningMessage", () => {
const publicAccessMessagePart = "Please enable public access to proceed";
const accessMessagePart = "Please allow access from Azure Portal to proceed";
// validEnpoints are a subset of those from Utils/EndpointValidation/PortalBackendIPs
const validEndpoints = [
"https://main.documentdb.ext.azure.com",
"https://main.documentdb.ext.azure.cn",
"https://main.documentdb.ext.azure.us",
];
let warningMessageResult: string;
const warningMessageFunc = (msg: string) => (warningMessageResult = msg);
beforeEach(() => {
warningMessageResult = undefined;
});
afterEach(() => {
resetConfigContext();
});
it("should return no message when publicNetworkAccess is enabled", async () => {
updateUserContext({
databaseAccount: {
properties: {
publicNetworkAccess: "Enabled",
},
} as DatabaseAccount,
});
await getNetworkSettingsWarningMessage(warningMessageFunc);
expect(warningMessageResult).toBeUndefined();
});
it("should return publicAccessMessage when publicNetworkAccess is disabled", async () => {
updateUserContext({
databaseAccount: {
properties: {
publicNetworkAccess: "Disabled",
},
} as DatabaseAccount,
});
await getNetworkSettingsWarningMessage(warningMessageFunc);
expect(warningMessageResult).toContain(publicAccessMessagePart);
});
it(`should return no message when the appropriate ip rules are added to mongo/cassandra account per endpoint`, () => {
validEndpoints.forEach(async (endpoint) => {
updateUserContext({
databaseAccount: {
kind: "MongoDB",
properties: {
ipRules: PortalBackendIPs[endpoint].map((ip: string) => ({ ipAddressOrRange: ip } as IpRule)),
publicNetworkAccess: "Enabled",
},
} as DatabaseAccount,
});
updateConfigContext({
BACKEND_ENDPOINT: endpoint,
});
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toBeUndefined();
});
});
it("should return accessMessage when incorrent ip rule is added to mongo/cassandra account per endpoint", () => {
validEndpoints.forEach(async (endpoint) => {
updateUserContext({
databaseAccount: {
kind: "MongoDB",
properties: {
ipRules: [{ ipAddressOrRange: "1.1.1.1" }],
publicNetworkAccess: "Enabled",
},
} as DatabaseAccount,
});
updateConfigContext({
BACKEND_ENDPOINT: endpoint,
});
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toContain(accessMessagePart);
});
});
// Postgres and vcore mongo account checks basically pass through to CheckFirewallRules so those
// tests are omitted here and included in CheckFirewallRules.test.ts
});
});

View File

@ -1,15 +1,7 @@
import { configContext } from "ConfigContext";
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules"; import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { PortalBackendIPs } from "Utils/EndpointValidation";
const PortalIPs: { [key: string]: string[] } = {
prod1: ["104.42.195.92", "40.76.54.131"],
prod2: ["104.42.196.69"],
mooncake: ["139.217.8.252"],
blackforest: ["51.4.229.218"],
fairfax: ["52.244.48.71"],
ussec: ["29.26.26.67", "29.26.26.66"],
usnat: ["7.28.202.68"],
};
export const getNetworkSettingsWarningMessage = async ( export const getNetworkSettingsWarningMessage = async (
setStateFunc: (warningMessage: string) => void setStateFunc: (warningMessage: string) => void
@ -28,6 +20,7 @@ export const getNetworkSettingsWarningMessage = async (
setStateFunc, setStateFunc,
accessMessage accessMessage
); );
return;
} else if (userContext.apiType === "VCoreMongo") { } else if (userContext.apiType === "VCoreMongo") {
checkFirewallRules( checkFirewallRules(
"2023-03-01-preview", "2023-03-01-preview",
@ -38,6 +31,7 @@ export const getNetworkSettingsWarningMessage = async (
setStateFunc, setStateFunc,
accessMessage accessMessage
); );
return;
} else if (accountProperties) { } else if (accountProperties) {
// public network access is disabled // public network access is disabled
if ( if (
@ -45,13 +39,14 @@ export const getNetworkSettingsWarningMessage = async (
accountProperties.publicNetworkAccess !== "SecuredByPerimeter" accountProperties.publicNetworkAccess !== "SecuredByPerimeter"
) { ) {
setStateFunc(publicAccessMessage); setStateFunc(publicAccessMessage);
return;
} }
const ipRules = accountProperties.ipRules; const ipRules = accountProperties.ipRules;
// public network access is NOT set to "All networks" // public network access is NOT set to "All networks"
if (ipRules.length > 0) { if (ipRules?.length > 0) {
if (userContext.apiType === "Cassandra" || userContext.apiType === "Mongo") { if (userContext.apiType === "Cassandra" || userContext.apiType === "Mongo") {
const portalIPs = PortalIPs[userContext.portalEnv]; const portalIPs = PortalBackendIPs[configContext.BACKEND_ENDPOINT];
let numberOfMatches = 0; let numberOfMatches = 0;
ipRules.forEach((ipRule) => { ipRules.forEach((ipRule) => {
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) { if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {

View File

@ -57,4 +57,22 @@ describe("shouldShowQueryPageOptions()", () => {
}); });
expect(userContext.apiType).toBe("Mongo"); expect(userContext.apiType).toBe("Mongo");
}); });
it("should be 'Postgres' for Postgres API", () => {
updateUserContext({
databaseAccount: {
kind: "Postgres",
} as DatabaseAccount,
});
expect(userContext.apiType).toBe("Postgres");
});
it("should be 'VCoreMongo' for vCore Mongo", () => {
updateUserContext({
databaseAccount: {
kind: "VCoreMongo",
} as DatabaseAccount,
});
expect(userContext.apiType).toBe("VCoreMongo");
});
}); });