mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 17:22:45 -05:00
New UI for deploying SQL project to a new Azure server (#18833)
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import { AzureSqlClient } from '../../models/deploy/azureSqlClient';
|
||||
import { IAccount, IAzureAccountService, IAzureAccountSession, IAzureResourceService, azure } from 'vscode-mssql';
|
||||
|
||||
|
||||
|
||||
export interface TestContext {
|
||||
azureAccountService: IAzureAccountService;
|
||||
azureResourceService: IAzureResourceService;
|
||||
accounts: IAccount[];
|
||||
session: IAzureAccountSession;
|
||||
subscriptions: azure.subscription.Subscription[];
|
||||
locations: azure.subscription.Location[];
|
||||
groups: azure.resources.ResourceGroup[];
|
||||
}
|
||||
|
||||
export function createContext(): TestContext {
|
||||
const accounts = [{
|
||||
key: undefined!,
|
||||
displayInfo: undefined!,
|
||||
properties: {
|
||||
tenants: [{
|
||||
id: '',
|
||||
displayName: ''
|
||||
}]
|
||||
},
|
||||
isStale: false,
|
||||
isSignedIn: true
|
||||
}];
|
||||
const subscriptions: azure.subscription.Subscription[] = [{ subscriptionId: 'id1' }, { subscriptionId: 'id2' }];
|
||||
const locations: azure.subscription.Location[] = [{ id: 'id1' }, { id: 'id2' }];
|
||||
const groups: azure.resources.ResourceGroup[] = [{ id: 'id1', location: 'l1' }, { id: 'id2', location: 'l2' }];
|
||||
const session: IAzureAccountSession = {
|
||||
account: accounts[0],
|
||||
subscription: subscriptions[0],
|
||||
tenantId: 'tenantId',
|
||||
token: {
|
||||
key: '',
|
||||
token: '',
|
||||
tokenType: '',
|
||||
}
|
||||
};
|
||||
return {
|
||||
groups: groups,
|
||||
locations: locations,
|
||||
subscriptions: subscriptions,
|
||||
session: session,
|
||||
accounts: accounts,
|
||||
azureAccountService: {
|
||||
addAccount: () => Promise.resolve(accounts[0]),
|
||||
getAccounts: () => Promise.resolve(accounts),
|
||||
getAccountSecurityToken: () => Promise.resolve({
|
||||
key: '',
|
||||
token: '',
|
||||
tokenType: ''
|
||||
}),
|
||||
getAccountSessions: () => Promise.resolve([session])
|
||||
},
|
||||
azureResourceService: {
|
||||
getLocations: () => Promise.resolve(locations),
|
||||
getResourceGroups: () => Promise.resolve(groups),
|
||||
createOrUpdateServer: () => Promise.resolve('new_server')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe('Azure SQL client', function (): void {
|
||||
|
||||
it('Should return accounts successfully', async function (): Promise<void> {
|
||||
const testContext = createContext();
|
||||
const azureSqlClient = new AzureSqlClient(() => Promise.resolve(testContext.azureAccountService));
|
||||
const accounts = await azureSqlClient.getAccounts();
|
||||
should(accounts.length).equal(testContext.accounts.length);
|
||||
});
|
||||
|
||||
it('Should create and return new account successfully', async function (): Promise<void> {
|
||||
const testContext = createContext();
|
||||
const azureSqlClient = new AzureSqlClient(() => Promise.resolve(testContext.azureAccountService));
|
||||
const account = await azureSqlClient.getAccount();
|
||||
should(account.key).equal(testContext.accounts[0].key);
|
||||
});
|
||||
|
||||
it('Should return subscriptions successfully', async function (): Promise<void> {
|
||||
const testContext = createContext();
|
||||
const azureSqlClient = new AzureSqlClient(() => Promise.resolve(testContext.azureAccountService));
|
||||
const result = await azureSqlClient.getSessions(testContext.accounts[0]);
|
||||
should(result[0].subscription.id).deepEqual(testContext.subscriptions[0].id);
|
||||
});
|
||||
|
||||
it('Should return locations successfully', async function (): Promise<void> {
|
||||
const testContext = createContext();
|
||||
const azureSqlClient = new AzureSqlClient(() => Promise.resolve(testContext.azureAccountService), () => Promise.resolve(testContext.azureResourceService));
|
||||
const result = await azureSqlClient.getLocations(testContext.session);
|
||||
should(result.length).deepEqual(testContext.locations.length);
|
||||
});
|
||||
|
||||
it('Should return resource groups successfully', async function (): Promise<void> {
|
||||
const testContext = createContext();
|
||||
const azureSqlClient = new AzureSqlClient(() => Promise.resolve(testContext.azureAccountService), () => Promise.resolve(testContext.azureResourceService));
|
||||
const result = await azureSqlClient.getResourceGroups(testContext.session);
|
||||
should(result.length).deepEqual(testContext.groups.length);
|
||||
should(result[0].location).deepEqual(testContext.groups[0].location);
|
||||
});
|
||||
});
|
||||
@@ -11,16 +11,18 @@ import { DeployService } from '../../models/deploy/deployService';
|
||||
import { Project } from '../../models/project';
|
||||
import * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import { AppSettingType, IDeployProfile } from '../../models/deploy/deployProfile';
|
||||
import { AppSettingType, ILocalDbDeployProfile, ISqlDbDeployProfile } from '../../models/deploy/deployProfile';
|
||||
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as constants from '../../common/constants';
|
||||
import { ShellExecutionHelper } from '../../tools/shellExecutionHelper';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { AzureSqlClient } from '../../models/deploy/azureSqlClient';
|
||||
|
||||
export interface TestContext {
|
||||
outputChannel: vscode.OutputChannel;
|
||||
azureSqlClient: TypeMoq.IMock<AzureSqlClient>;
|
||||
}
|
||||
|
||||
export const mockConnectionResult: azdata.ConnectionResult = {
|
||||
@@ -47,7 +49,8 @@ export function createContext(): TestContext {
|
||||
show: () => { },
|
||||
hide: () => { },
|
||||
dispose: () => { }
|
||||
}
|
||||
},
|
||||
azureSqlClient: TypeMoq.Mock.ofType(AzureSqlClient)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,7 +71,7 @@ describe('deploy service', function (): void {
|
||||
|
||||
it('Should deploy a database to docker container successfully', async function (): Promise<void> {
|
||||
const testContext = createContext();
|
||||
const deployProfile: IDeployProfile = {
|
||||
const deployProfile: ILocalDbDeployProfile = {
|
||||
localDbSetting: {
|
||||
dbName: 'test',
|
||||
password: 'PLACEHOLDER',
|
||||
@@ -84,21 +87,21 @@ describe('deploy service', function (): void {
|
||||
const project1 = await Project.openProject(vscode.Uri.file(projFilePath).fsPath);
|
||||
const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper);
|
||||
shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(),
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id'));
|
||||
const deployService = new DeployService(testContext.outputChannel, shellExecutionHelper.object);
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id'));
|
||||
const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object);
|
||||
sandbox.stub(azdata.connection, 'connect').returns(Promise.resolve(mockConnectionResult));
|
||||
sandbox.stub(azdata.connection, 'getUriForConnection').returns(Promise.resolve('connection'));
|
||||
sandbox.stub(vscode.window, 'showWarningMessage').returns(<any>Promise.resolve(constants.yesString));
|
||||
sandbox.stub(azdata.tasks, 'startBackgroundOperation').callThrough();
|
||||
|
||||
let connection = await deployService.deploy(deployProfile, project1);
|
||||
let connection = await deployService.deployToContainer(deployProfile, project1);
|
||||
should(connection).equals('connection');
|
||||
|
||||
});
|
||||
|
||||
it('Should fail the deploy if docker is not running', async function (): Promise<void> {
|
||||
const testContext = createContext();
|
||||
const deployProfile: IDeployProfile = {
|
||||
const deployProfile: ILocalDbDeployProfile = {
|
||||
localDbSetting: {
|
||||
dbName: 'test',
|
||||
password: 'PLACEHOLDER',
|
||||
@@ -114,11 +117,11 @@ describe('deploy service', function (): void {
|
||||
const project1 = await Project.openProject(vscode.Uri.file(projFilePath).fsPath);
|
||||
const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper);
|
||||
shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(),
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.reject('error'));
|
||||
const deployService = new DeployService(testContext.outputChannel, shellExecutionHelper.object);
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.reject('error'));
|
||||
const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object);
|
||||
sandbox.stub(azdata.tasks, 'startBackgroundOperation').callThrough();
|
||||
|
||||
await should(deployService.deploy(deployProfile, project1)).rejected();
|
||||
await should(deployService.deployToContainer(deployProfile, project1)).rejected();
|
||||
});
|
||||
|
||||
it('Should retry connecting to the server', async function (): Promise<void> {
|
||||
@@ -136,8 +139,8 @@ describe('deploy service', function (): void {
|
||||
|
||||
const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper);
|
||||
shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(),
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id'));
|
||||
const deployService = new DeployService(testContext.outputChannel, shellExecutionHelper.object);
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id'));
|
||||
const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object);
|
||||
let connectionStub = sandbox.stub(azdata.connection, 'connect');
|
||||
connectionStub.onFirstCall().returns(Promise.resolve(mockFailedConnectionResult));
|
||||
connectionStub.onSecondCall().returns(Promise.resolve(mockConnectionResult));
|
||||
@@ -173,7 +176,7 @@ describe('deploy service', function (): void {
|
||||
const filePath = path.join(project1.projectFolderPath, 'local.settings.json');
|
||||
await fse.writeFile(filePath, settingContent);
|
||||
|
||||
const deployProfile: IDeployProfile = {
|
||||
const deployProfile: ILocalDbDeployProfile = {
|
||||
localDbSetting: {
|
||||
dbName: 'test',
|
||||
password: 'PLACEHOLDER',
|
||||
@@ -194,8 +197,8 @@ describe('deploy service', function (): void {
|
||||
|
||||
const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper);
|
||||
shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(),
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id'));
|
||||
const deployService = new DeployService(testContext.outputChannel, shellExecutionHelper.object);
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id'));
|
||||
const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object);
|
||||
|
||||
await deployService.updateAppSettings(appInteg, deployProfile);
|
||||
let newContent = JSON.parse(fse.readFileSync(filePath, 'utf8'));
|
||||
@@ -228,7 +231,7 @@ describe('deploy service', function (): void {
|
||||
const filePath = path.join(project1.projectFolderPath, 'local.settings.json');
|
||||
await fse.writeFile(filePath, settingContent);
|
||||
|
||||
const deployProfile: IDeployProfile = {
|
||||
const deployProfile: ILocalDbDeployProfile = {
|
||||
|
||||
deploySettings: {
|
||||
connectionUri: 'connection',
|
||||
@@ -245,8 +248,8 @@ describe('deploy service', function (): void {
|
||||
};
|
||||
const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper);
|
||||
shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(),
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id'));
|
||||
const deployService = new DeployService(testContext.outputChannel, shellExecutionHelper.object);
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id'));
|
||||
const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object);
|
||||
let connection = new azdata.connection.ConnectionProfile();
|
||||
sandbox.stub(azdata.connection, 'getConnection').returns(Promise.resolve(connection));
|
||||
|
||||
@@ -260,10 +263,10 @@ describe('deploy service', function (): void {
|
||||
const testContext = createContext();
|
||||
const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper);
|
||||
shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(),
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(`id
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(`id
|
||||
id2
|
||||
id3`));
|
||||
const deployService = new DeployService(testContext.outputChannel, shellExecutionHelper.object);
|
||||
const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object);
|
||||
const ids = await deployService.getCurrentDockerContainer('label');
|
||||
await deployService.cleanDockerObjects(ids, ['docker stop', 'docker rm']);
|
||||
shellExecutionHelper.verify(x => x.runStreamedCommand(TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(7));
|
||||
@@ -271,7 +274,7 @@ describe('deploy service', function (): void {
|
||||
|
||||
it('Should create docker image info correctly', () => {
|
||||
const testContext = createContext();
|
||||
const deployService = new DeployService(testContext.outputChannel);
|
||||
const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel);
|
||||
const id = UUID.generateUuid().toLocaleLowerCase();
|
||||
const baseImage = 'baseImage:latest';
|
||||
const tag = baseImage.replace(':', '-').replace(constants.sqlServerDockerRegistry, '').replace(/[^a-zA-Z0-9_,\-]/g, '').toLocaleLowerCase();
|
||||
@@ -311,4 +314,53 @@ describe('deploy service', function (): void {
|
||||
tag: `${constants.dockerImageNamePrefix}-${imageProjectName}-${tag}`
|
||||
});
|
||||
});
|
||||
|
||||
it('Should create a new Azure SQL server successfully', async function (): Promise<void> {
|
||||
const testContext = createContext();
|
||||
const deployProfile: ISqlDbDeployProfile = {
|
||||
sqlDbSetting: {
|
||||
dbName: 'test',
|
||||
password: 'PLACEHOLDER',
|
||||
port: 1433,
|
||||
serverName: 'localhost',
|
||||
userName: 'sa',
|
||||
connectionRetryTimeout: 1,
|
||||
resourceGroupName: 'resourceGroups',
|
||||
session: {
|
||||
subscription: {
|
||||
subscriptionId: 'subscriptionId',
|
||||
},token: {
|
||||
key: '',
|
||||
token: '',
|
||||
tokenType: '',
|
||||
},
|
||||
tenantId: '',
|
||||
account: undefined!
|
||||
},
|
||||
location: 'location'
|
||||
}
|
||||
};
|
||||
const fullyQualifiedDomainName = 'servername';
|
||||
const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper);
|
||||
shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(),
|
||||
undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id'));
|
||||
const session = deployProfile?.sqlDbSetting?.session;
|
||||
if (deployProfile?.sqlDbSetting?.session && session) {
|
||||
testContext.azureSqlClient.setup(x => x.createOrUpdateServer(
|
||||
session,
|
||||
deployProfile.sqlDbSetting?.resourceGroupName || '',
|
||||
deployProfile.sqlDbSetting?.serverName || '',
|
||||
{
|
||||
location: deployProfile?.sqlDbSetting?.location || '',
|
||||
administratorLogin: deployProfile?.sqlDbSetting?.userName,
|
||||
administratorLoginPassword: deployProfile?.sqlDbSetting?.password
|
||||
})).returns(() => Promise.resolve(fullyQualifiedDomainName));
|
||||
}
|
||||
sandbox.stub(azdata.connection, 'connect').returns(Promise.resolve(mockConnectionResult));
|
||||
sandbox.stub(azdata.connection, 'getUriForConnection').returns(Promise.resolve('connection'));
|
||||
const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object);
|
||||
let connection = await deployService.createNewAzureSqlServer(deployProfile);
|
||||
should(deployProfile.sqlDbSetting?.serverName).equal(fullyQualifiedDomainName);
|
||||
should(connection).equals('connection');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user