Adding External Streaming Job I/O validation (#13195)

* Added Tools Service call for ValidateStreamingJob

* Partial addition of ESJ

* adding test mocks

* Validation working

* Modifying command visibility logic to submatch ESJs in addition to files

* Changed string literal to constant, corrected attribute order

* Added tests

* correcting casing that's causing test failures on linux

* Swapping Thenable for Promise

* excluded validate from command palette
This commit is contained in:
Benjin Dubishar
2020-11-02 19:02:20 -08:00
committed by GitHub
parent ba80000e27
commit 342ff47e51
20 changed files with 198 additions and 37 deletions

View File

@@ -74,6 +74,9 @@
<Folder Include="Views\User" />
<Build Include="Views\User\Profile.sql" />
</ItemGroup>
<ItemGroup>
<Build Include="MyExternalStreamingJob.sql" Type="ExternalStreamingJob" />
</ItemGroup>
<ItemGroup>
<SqlCmdVariable Include="ProdDatabaseName">
<DefaultValue>MyProdDatabase</DefaultValue>

View File

@@ -74,6 +74,9 @@
<Folder Include="Views\User"/>
<Build Include="Views\User\Profile.sql"/>
</ItemGroup>
<ItemGroup>
<Build Include="MyExternalStreamingJob.sql" Type="ExternalStreamingJob"/>
</ItemGroup>
<ItemGroup>
<SqlCmdVariable Include="BackupDatabaseName">
<DefaultValue>MyBackupDatabase</DefaultValue>

View File

@@ -31,12 +31,14 @@ describe('Project: sqlproj content operations', function (): void {
const project: Project = await Project.openProject(projFilePath);
// Files and folders
should(project.files.filter(f => f.type === EntryType.File).length).equal(5);
should(project.files.filter(f => f.type === EntryType.File).length).equal(6);
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(4);
should(project.files.find(f => f.type === EntryType.Folder && f.relativePath === 'Views\\User')).not.equal(undefined); // mixed ItemGroup folder
should(project.files.find(f => f.type === EntryType.File && f.relativePath === 'Views\\User\\Profile.sql')).not.equal(undefined); // mixed ItemGroup file
should(project.files.find(f => f.type === EntryType.File && f.relativePath === '..\\Test\\Test.sql')).not.equal(undefined); // mixed ItemGroup file
should(project.files.find(f => f.type === EntryType.File && f.relativePath === 'MyExternalStreamingJob.sql')).not.equal(undefined); // entry with custom attribute
// SqlCmdVariables
should(Object.keys(project.sqlCmdVariables).length).equal(2);
@@ -94,20 +96,26 @@ describe('Project: sqlproj content operations', function (): void {
const project = await Project.openProject(projFilePath);
const folderPath = 'Stored Procedures';
const filePath = path.join(folderPath, 'Fake Stored Proc.sql');
const fileContents = 'SELECT \'This is not actually a stored procedure.\'';
const scriptPath = path.join(folderPath, 'Fake Stored Proc.sql');
const scriptContents = 'SELECT \'This is not actually a stored procedure.\'';
const scriptPathTagged = path.join(folderPath, 'Fake External Streaming Job.sql');
const scriptContentsTagged = 'EXEC sys.sp_create_streaming_job \'job\', \'SELECT 7\'';
await project.addFolderItem(folderPath);
await project.addScriptItem(filePath, fileContents);
await project.addScriptItem(scriptPath, scriptContents);
await project.addScriptItem(scriptPathTagged, scriptContentsTagged, templates.externalStreamingJob);
const newProject = await Project.openProject(projFilePath);
should(newProject.files.find(f => f.type === EntryType.Folder && f.relativePath === convertSlashesForSqlProj(folderPath))).not.equal(undefined);
should(newProject.files.find(f => f.type === EntryType.File && f.relativePath === convertSlashesForSqlProj(filePath))).not.equal(undefined);
should(newProject.files.find(f => f.type === EntryType.File && f.relativePath === convertSlashesForSqlProj(scriptPath))).not.equal(undefined);
should(newProject.files.find(f => f.type === EntryType.File && f.relativePath === convertSlashesForSqlProj(scriptPathTagged))).not.equal(undefined);
should(newProject.files.find(f => f.type === EntryType.File && f.relativePath === convertSlashesForSqlProj(scriptPathTagged))?.sqlObjectType).equal(constants.ExternalStreamingJob);
const newFileContents = (await fs.readFile(path.join(newProject.projectFolderPath, filePath))).toString();
const newScriptContents = (await fs.readFile(path.join(newProject.projectFolderPath, scriptPath))).toString();
should(newFileContents).equal(fileContents);
should(newScriptContents).equal(scriptContents);
});
it('Should add Folder and Build entries to sqlproj with pre-existing scripts on disk', async function (): Promise<void> {

View File

@@ -85,7 +85,7 @@ describe('ProjectsController', function (): void {
const project = await projController.openProject(vscode.Uri.file(sqlProjPath));
should(project.files.length).equal(9); // detailed sqlproj tests in their own test file
should(project.files.length).equal(10); // detailed sqlproj tests in their own test file
should(project.dataSources.length).equal(3); // detailed datasources tests in their own test file
});

View File

@@ -23,7 +23,7 @@ describe('Templates: loading templates from disk', function (): void {
// check expected counts
const numScriptObjectTypes = 6;
const numScriptObjectTypes = 7;
should(templates.projectScriptTypes().length).equal(numScriptObjectTypes);
should(Object.keys(templates.projectScriptTypes()).length).equal(numScriptObjectTypes);

View File

@@ -115,6 +115,7 @@ export class MockDacFxService implements mssql.IDacFxService {
public generateDeployScript(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode, ______?: Record<string, string>): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
public generateDeployPlan(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode): Thenable<mssql.GenerateDeployPlanResult> { return Promise.resolve(mockDacFxResult); }
public getOptionsFromProfile(_: string): Thenable<mssql.DacFxOptionsResult> { return Promise.resolve(mockDacFxOptionsResult); }
public validateStreamingJob(_: string, __: string): Thenable<mssql.ValidateStreamingJobResult> { return Promise.resolve(mockDacFxResult); }
}
export function createContext(): TestContext {