mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 09:35:39 -05:00
Add reference to another sql project (#12186)
* add projects to add database reference dialog * able to add project references * check for circular dependency * only allow adding reference to project in the same workspace * fix location dropdown when project reference is enabled * add tests * more tests * cleanup * fix flakey test * addressing comments
This commit is contained in:
@@ -52,6 +52,7 @@ describe('Add Database Reference Dialog', () => {
|
||||
|
||||
// change location to different database, different server
|
||||
dialog.locationDropdown!.value = constants.differentDbDifferentServer;
|
||||
dialog.updateEnabledInputBoxes();
|
||||
dialog.tryEnableAddReferenceButton();
|
||||
should(dialog.dialog.okButton.enabled).equal(false, 'Ok button should not be enabled because server fields are not filled');
|
||||
|
||||
@@ -63,9 +64,14 @@ describe('Add Database Reference Dialog', () => {
|
||||
|
||||
// change location to same database
|
||||
dialog.locationDropdown!.value = constants.sameDatabase;
|
||||
dialog.updateEnabledInputBoxes();
|
||||
dialog.tryEnableAddReferenceButton();
|
||||
should(dialog.dialog.okButton.enabled).equal(true, 'Ok button should be enabled because only dacpac location is needed for a reference located on the same database');
|
||||
|
||||
// switch to project
|
||||
dialog.projectRadioButtonClick();
|
||||
should(dialog.dialog.okButton.enabled).equal(false, 'Ok button should not be enabled because there are no projects in the dropdown');
|
||||
|
||||
// change reference type back to system db
|
||||
dialog.systemDbRadioButtonClick();
|
||||
should(dialog.databaseNameTextbox?.value).equal('', `Database name textbox should be empty. Actual:${dialog.databaseNameTextbox?.value}`);
|
||||
@@ -77,7 +83,7 @@ describe('Add Database Reference Dialog', () => {
|
||||
const dialog = new AddDatabaseReferenceDialog(project);
|
||||
await dialog.openDialog();
|
||||
|
||||
// dialog starts with system db
|
||||
// dialog starts with system db because there aren't any other projects in the workspace
|
||||
should(dialog.currentReferenceType).equal(ReferenceType.systemDb);
|
||||
validateInputBoxEnabledStates(dialog, { databaseNameEnabled: true, databaseVariableEnabled: false, serverNameEnabled: false, serverVariabledEnabled: false});
|
||||
|
||||
@@ -96,6 +102,12 @@ describe('Add Database Reference Dialog', () => {
|
||||
dialog.locationDropdown!.value = constants.sameDatabase;
|
||||
dialog.updateEnabledInputBoxes();
|
||||
validateInputBoxEnabledStates(dialog, { databaseNameEnabled: false, databaseVariableEnabled: false, serverNameEnabled: false, serverVariabledEnabled: false});
|
||||
|
||||
// change to project reference
|
||||
dialog.projectRadioButtonClick();
|
||||
should(dialog.currentReferenceType).equal(ReferenceType.project);
|
||||
should(dialog.locationDropdown!.value).equal(constants.sameDatabase);
|
||||
validateInputBoxEnabledStates(dialog, { databaseNameEnabled: false, databaseVariableEnabled: false, serverNameEnabled: false, serverVariabledEnabled: false});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -107,8 +119,8 @@ interface inputBoxExpectedStates {
|
||||
}
|
||||
|
||||
function validateInputBoxEnabledStates(dialog: AddDatabaseReferenceDialog, expectedStates: inputBoxExpectedStates): void {
|
||||
should(dialog.databaseNameTextbox?.enabled).equal(expectedStates.databaseNameEnabled);
|
||||
should(dialog.databaseVariableTextbox?.enabled).equal(expectedStates.databaseVariableEnabled);
|
||||
should(dialog.serverNameTextbox?.enabled).equal(expectedStates.serverNameEnabled);
|
||||
should(dialog.serverVariableTextbox?.enabled).equal(expectedStates.serverVariabledEnabled);
|
||||
should(dialog.databaseNameTextbox?.enabled).equal(expectedStates.databaseNameEnabled, `Database name text box should be ${expectedStates.databaseNameEnabled}. Actual: ${dialog.databaseNameTextbox?.enabled}`);
|
||||
should(dialog.databaseVariableTextbox?.enabled).equal(expectedStates.databaseVariableEnabled, `Database variable text box should be ${expectedStates.databaseVariableEnabled}. Actual: ${dialog.databaseVariableTextbox?.enabled}`);
|
||||
should(dialog.serverNameTextbox?.enabled).equal(expectedStates.serverNameEnabled, `Server name text box should be ${expectedStates.serverNameEnabled}. Actual: ${dialog.serverNameTextbox?.enabled}`);
|
||||
should(dialog.serverVariableTextbox?.enabled).equal(expectedStates.serverVariabledEnabled, `Server variable text box should be ${expectedStates.serverVariabledEnabled}. Actual: ${dialog.serverVariableTextbox?.enabled}`);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as testUtils from './testUtils';
|
||||
import * as constants from '../common/constants';
|
||||
|
||||
import { promises as fs } from 'fs';
|
||||
import { Project, EntryType, TargetPlatform, SystemDatabase, DatabaseReferenceLocation, DacpacReferenceProjectEntry, SqlProjectReferenceProjectEntry } from '../models/project';
|
||||
import { Project, EntryType, TargetPlatform, SystemDatabase, DacpacReferenceProjectEntry, SqlProjectReferenceProjectEntry } from '../models/project';
|
||||
import { exists, convertSlashesForSqlProj } from '../common/utils';
|
||||
import { Uri, window } from 'vscode';
|
||||
|
||||
@@ -217,7 +217,7 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
|
||||
// add database reference in the same database
|
||||
should(project.databaseReferences.length).equal(0, 'There should be no database references to start with');
|
||||
await project.addDatabaseReference({ dacpacFileLocation: Uri.file('test1.dacpac'), databaseLocation: DatabaseReferenceLocation.sameDatabase, suppressMissingDependenciesErrors: true });
|
||||
await project.addDatabaseReference({ dacpacFileLocation: Uri.file('test1.dacpac'), suppressMissingDependenciesErrors: true });
|
||||
should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to test1');
|
||||
should(project.databaseReferences[0].databaseName).equal('test1', 'The database reference should be test1');
|
||||
should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(true, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be true');
|
||||
@@ -234,7 +234,6 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
should(project.databaseReferences.length).equal(0, 'There should be no database references to start with');
|
||||
await project.addDatabaseReference({
|
||||
dacpacFileLocation: Uri.file('test2.dacpac'),
|
||||
databaseLocation: DatabaseReferenceLocation.differentDatabaseSameServer,
|
||||
databaseName: 'test2DbName',
|
||||
databaseVariable: 'test2Db',
|
||||
suppressMissingDependenciesErrors: false
|
||||
@@ -257,7 +256,6 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
should(project.databaseReferences.length).equal(0, 'There should be no database references to start with');
|
||||
await project.addDatabaseReference({
|
||||
dacpacFileLocation: Uri.file('test3.dacpac'),
|
||||
databaseLocation: DatabaseReferenceLocation.differentDatabaseDifferentServer,
|
||||
databaseName: 'test3DbName',
|
||||
databaseVariable: 'test3Db',
|
||||
serverName: 'otherServerName',
|
||||
@@ -267,7 +265,7 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to test3');
|
||||
should(project.databaseReferences[0].databaseName).equal('test3', 'The database reference should be test3');
|
||||
should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false');
|
||||
// make sure reference to test2.dacpac and SQLCMD variables were added
|
||||
// make sure reference to test3.dacpac and SQLCMD variables were added
|
||||
let projFileText = (await fs.readFile(projFilePath)).toString();
|
||||
should(projFileText).containEql('test3.dacpac');
|
||||
should(projFileText).containEql('<DatabaseSqlCmdVariable>test3Db</DatabaseSqlCmdVariable>');
|
||||
@@ -276,6 +274,87 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
should(projFileText).containEql('<SqlCmdVariable Include="otherServer">');
|
||||
});
|
||||
|
||||
it('Should add a project reference to the same database correctly', async function (): Promise<void> {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||
const project = await Project.openProject(projFilePath);
|
||||
|
||||
// add database reference to a different database on a different server
|
||||
should(project.databaseReferences.length).equal(0, 'There should be no database references to start with');
|
||||
should(Object.keys(project.sqlCmdVariables).length).equal(0, `There should be no sqlcmd variables to start with. Actual: ${Object.keys(project.sqlCmdVariables).length}`);
|
||||
await project.addProjectReference({
|
||||
projectName: 'project1',
|
||||
projectGuid: '',
|
||||
projectRelativePath: Uri.file(path.join('..','project1', 'project1.sqlproj')),
|
||||
suppressMissingDependenciesErrors: false
|
||||
});
|
||||
should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to project1');
|
||||
should(project.databaseReferences[0].databaseName).equal('project1', 'The database reference should be project1');
|
||||
should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false');
|
||||
should(Object.keys(project.sqlCmdVariables).length).equal(0, `There should be no sqlcmd variables added. Actual: ${Object.keys(project.sqlCmdVariables).length}`);
|
||||
|
||||
// make sure reference to project1 and SQLCMD variables were added
|
||||
let projFileText = (await fs.readFile(projFilePath)).toString();
|
||||
should(projFileText).containEql('project1');
|
||||
});
|
||||
|
||||
it('Should add a project reference to a different database in the same server correctly', async function (): Promise<void> {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||
const project = await Project.openProject(projFilePath);
|
||||
|
||||
// add database reference to a different database on a different server
|
||||
should(project.databaseReferences.length).equal(0, 'There should be no database references to start with');
|
||||
should(Object.keys(project.sqlCmdVariables).length).equal(0, 'There should be no sqlcmd variables to start with');
|
||||
await project.addProjectReference({
|
||||
projectName: 'project1',
|
||||
projectGuid: '',
|
||||
projectRelativePath: Uri.file(path.join('..','project1', 'project1.sqlproj')),
|
||||
databaseName: 'testdbName',
|
||||
databaseVariable: 'testdb',
|
||||
suppressMissingDependenciesErrors: false
|
||||
});
|
||||
should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to project1');
|
||||
should(project.databaseReferences[0].databaseName).equal('project1', 'The database reference should be project1');
|
||||
should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false');
|
||||
should(Object.keys(project.sqlCmdVariables).length).equal(1, `There should be one new sqlcmd variable added. Actual: ${Object.keys(project.sqlCmdVariables).length}`);
|
||||
|
||||
// make sure reference to project1 and SQLCMD variables were added
|
||||
let projFileText = (await fs.readFile(projFilePath)).toString();
|
||||
should(projFileText).containEql('project1');
|
||||
should(projFileText).containEql('<DatabaseSqlCmdVariable>testdb</DatabaseSqlCmdVariable>');
|
||||
should(projFileText).containEql('<SqlCmdVariable Include="testdb">');
|
||||
});
|
||||
|
||||
it('Should add a project reference to a different database in a different server correctly', async function (): Promise<void> {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||
const project = await Project.openProject(projFilePath);
|
||||
|
||||
// add database reference to a different database on a different server
|
||||
should(project.databaseReferences.length).equal(0, 'There should be no database references to start with');
|
||||
should(Object.keys(project.sqlCmdVariables).length).equal(0, 'There should be no sqlcmd variables to start with');
|
||||
await project.addProjectReference({
|
||||
projectName: 'project1',
|
||||
projectGuid: '',
|
||||
projectRelativePath: Uri.file(path.join('..','project1', 'project1.sqlproj')),
|
||||
databaseName: 'testdbName',
|
||||
databaseVariable: 'testdb',
|
||||
serverName: 'otherServerName',
|
||||
serverVariable: 'otherServer',
|
||||
suppressMissingDependenciesErrors: false
|
||||
});
|
||||
should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to project1');
|
||||
should(project.databaseReferences[0].databaseName).equal('project1', 'The database reference should be project1');
|
||||
should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false');
|
||||
should(Object.keys(project.sqlCmdVariables).length).equal(2, `There should be two new sqlcmd variables added. Actual: ${Object.keys(project.sqlCmdVariables).length}`);
|
||||
|
||||
// make sure reference to project1 and SQLCMD variables were added
|
||||
let projFileText = (await fs.readFile(projFilePath)).toString();
|
||||
should(projFileText).containEql('project1');
|
||||
should(projFileText).containEql('<DatabaseSqlCmdVariable>testdb</DatabaseSqlCmdVariable>');
|
||||
should(projFileText).containEql('<SqlCmdVariable Include="testdb">');
|
||||
should(projFileText).containEql('<ServerSqlCmdVariable>otherServer</ServerSqlCmdVariable>');
|
||||
should(projFileText).containEql('<SqlCmdVariable Include="otherServer">');
|
||||
});
|
||||
|
||||
it('Should not allow adding duplicate database references', async function (): Promise<void> {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||
const project = await Project.openProject(projFilePath);
|
||||
@@ -289,12 +368,12 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
await testUtils.shouldThrowSpecificError(async () => await project.addSystemDatabaseReference({ databaseName: 'master', systemDb: SystemDatabase.master, suppressMissingDependenciesErrors: false }), constants.databaseReferenceAlreadyExists);
|
||||
should(project.databaseReferences.length).equal(1, 'There should only be one database reference after trying to add a reference to master again');
|
||||
|
||||
await project.addDatabaseReference({ dacpacFileLocation: Uri.file('test.dacpac'), databaseLocation: DatabaseReferenceLocation.sameDatabase, suppressMissingDependenciesErrors: false });
|
||||
await project.addDatabaseReference({ dacpacFileLocation: Uri.file('test.dacpac'), suppressMissingDependenciesErrors: false });
|
||||
should(project.databaseReferences.length).equal(2, 'There should be two database references after adding a reference to test.dacpac');
|
||||
should(project.databaseReferences[1].databaseName).equal('test', 'project.databaseReferences[1].databaseName should be test');
|
||||
|
||||
// try to add reference to test.dacpac again
|
||||
await testUtils.shouldThrowSpecificError(async () => await project.addDatabaseReference({ dacpacFileLocation: Uri.file('test.dacpac'), databaseLocation: DatabaseReferenceLocation.sameDatabase, suppressMissingDependenciesErrors: false }), constants.databaseReferenceAlreadyExists);
|
||||
await testUtils.shouldThrowSpecificError(async () => await project.addDatabaseReference({ dacpacFileLocation: Uri.file('test.dacpac'), suppressMissingDependenciesErrors: false }), constants.databaseReferenceAlreadyExists);
|
||||
should(project.databaseReferences.length).equal(2, 'There should be two database references after trying to add a reference to test.dacpac again');
|
||||
});
|
||||
|
||||
|
||||
@@ -518,7 +518,7 @@ describe('ProjectsController', function (): void {
|
||||
let opened = false;
|
||||
|
||||
let addDbReferenceDialog = TypeMoq.Mock.ofType(AddDatabaseReferenceDialog);
|
||||
addDbReferenceDialog.setup(x => x.openDialog()).returns(() => { opened = true; return Promise.resolve(undefined) });
|
||||
addDbReferenceDialog.setup(x => x.openDialog()).returns(() => { opened = true; return Promise.resolve(undefined); });
|
||||
|
||||
let projController = TypeMoq.Mock.ofType(ProjectsController);
|
||||
projController.callBase = true;
|
||||
@@ -530,7 +530,6 @@ describe('ProjectsController', function (): void {
|
||||
|
||||
it('Callbacks are hooked up and called from Add database reference dialog', async function (): Promise<void> {
|
||||
const projPath = path.dirname(await testUtils.createTestSqlProjFile(baselines.openProjectFileBaseline));
|
||||
await testUtils.createTestDataSources(baselines.openDataSourcesBaseline, projPath);
|
||||
const proj = new Project(projPath);
|
||||
|
||||
const addDbRefHoller = 'hello from callback for addDatabaseReference()';
|
||||
@@ -557,6 +556,36 @@ describe('ProjectsController', function (): void {
|
||||
|
||||
should(holler).equal(addDbRefHoller, 'executionCallback() is supposed to have been setup and called for add database reference scenario');
|
||||
});
|
||||
|
||||
it('Should not allow adding circular project references', async function (): Promise<void> {
|
||||
const showErrorMessageSpy = sinon.spy(vscode.window, 'showErrorMessage');
|
||||
|
||||
const projPath1 = await testUtils.createTestSqlProjFile(baselines.openProjectFileBaseline);
|
||||
const projPath2 = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||
const projController = new ProjectsController(new SqlDatabaseProjectTreeViewProvider());
|
||||
|
||||
const project1 = await projController.openProject(vscode.Uri.file(projPath1));
|
||||
const project2 = await projController.openProject(vscode.Uri.file(projPath2));
|
||||
|
||||
// add project reference from project1 to project2
|
||||
await projController.addDatabaseReferenceCallback(project1, {
|
||||
projectGuid: '',
|
||||
projectName: 'TestProject',
|
||||
projectRelativePath: undefined,
|
||||
suppressMissingDependenciesErrors: false
|
||||
});
|
||||
should(showErrorMessageSpy.notCalled).be.true('showErrorMessage should not have been called');
|
||||
|
||||
// try to add circular reference
|
||||
await projController.addDatabaseReferenceCallback(project2, {
|
||||
projectGuid: '',
|
||||
projectName: 'TestProjectName',
|
||||
projectRelativePath: undefined,
|
||||
suppressMissingDependenciesErrors: false
|
||||
});
|
||||
should(showErrorMessageSpy.called).be.true('showErrorMessage should have been called');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user