diff --git a/.pipelines/mssql-pipelines.yml b/.pipelines/mssql-pipelines.yml index d2d2d8c7fa..c11bfa133c 100644 --- a/.pipelines/mssql-pipelines.yml +++ b/.pipelines/mssql-pipelines.yml @@ -1,9 +1,10 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -# MsSql Integration Testing Pipeline config is split into two jobs: -# 1) LinuxTests -> Run SQL Server 2019 in Linux Docker Image -# 2) WindowsTests -> Run LocalDB preinstalled on machine +# MsSql Integration Testing Pipeline config is split into parallel jobs: +# 1) linux (disabled) -> Run SQL Server 2019 in Linux Docker Image +# 2) windows_combined -> GraphQL, REST, Unit, HotReload, OpenApi, Auth, Telemetry, Caching on LocalDB +# 3) windows_configuration -> Configuration tests on LocalDB (with schema init) trigger: batch: true @@ -151,7 +152,19 @@ jobs: summaryFileLocation: '$(Agent.TempDirectory)/**/*cobertura.xml' -- job: windows +# MsSql Integration Testing is split into two parallel jobs (~20 min each): +# +# 1) windows_combined -> GraphQL, HotReload, REST, Unit, OpenApi, Auth, +# Telemetry, and Caching tests. +# SqlTestBase-inheriting tests (GraphQL, REST) create the DB schema; +# the remaining tests find it already in place. +# +# 2) windows_configuration -> Pure ConfigurationTests. +# No SqlTestBase tests in this bucket, so initDbSchema creates the +# schema via SqlClient before tests run. + +- job: windows_combined + displayName: 'Windows - Combined Integration Tests' pool: vmImage: 'windows-latest' variables: @@ -167,117 +180,32 @@ jobs: SqlVersionCode: '15.0' steps: - - task: CmdLine@2 - displayName: 'Set flag to publish received files when previous step fails' - condition: failed() - inputs: - script: 'echo ##vso[task.setvariable variable=publishverify]Yes' - - - task: NuGetAuthenticate@1 - displayName: 'NuGet Authenticate' - - # The .NET CLI commands in proceeding tasks use the .NET SDK version specified ("selected") here. - # Per Microsoft Learn Docs, "Selecting the .NET SDK version is independent from - # specifying the runtime version a project targets." - - task: UseDotNet@2 - displayName: Setup .NET SDK v8.0.x - inputs: - packageType: sdk - version: 8.0.x - - - task: NuGetToolInstaller@1 - - - task: DotNetCoreCLI@2 - displayName: Restore NuGet packages - inputs: - command: restore - projects: '$(solution)' - feedsToUse: config - nugetConfigPath: Nuget.config - restoreArguments: '/p:RuntimeIdentifiers=""' - - - task: PowerShell@2 - displayName: Install SQL LocalDB - inputs: - targetType: 'inline' - script: | - SqlLocalDb.exe start - SqlLocalDB.exe info "MSSQLLocalDB" - Write-Host "Downloading" - Import-Module BitsTransfer - Start-BitsTransfer -Source $(InstallerUrl) -Destination SqlLocalDB.msi - Write-Host "Installing" - Start-Process -FilePath "SqlLocalDB.msi" -Wait -ArgumentList "/qn", "/norestart", "/l*v SqlLocalDBInstall.log", "IACCEPTSQLLOCALDBLICENSETERMS=YES"; - SqlLocalDB.exe stop MSSQLLocalDB -k - SqlLocalDB.exe delete MSSQLLocalDB - - - task: PowerShell@2 - displayName: 'Start MSSQLLocalDB' - inputs: - targetType: 'inline' - script: | - SqlLocalDb.exe start MSSQLLocalDB - SqlLocalDb.exe info "MSSQLLocalDB" - - - task: DotNetCoreCLI@2 - displayName: Build - inputs: - command: build - projects: | - **/*.csproj - !**/*Tests*.csproj - arguments: '-p:generateConfigFileForDbType=MsSql --configuration $(buildConfiguration)' # Update this to match your need - - - task: DotNetCoreCLI@2 - displayName: Build Test Projects - inputs: - command: build - projects: '**/*Tests/*.csproj' - arguments: '--configuration $(buildConfiguration)' - - - task: FileTransform@1.206.0 - displayName: 'Generate dab-config.MsSql.json' - inputs: - folderPath: '$(System.DefaultWorkingDirectory)' - fileType: 'json' - targetFiles: 'src/out/tests/*/dab-config.MsSql.json' - - - task: DotNetCoreCLI@2 - displayName: 'MsSql Integration Tests' - inputs: - command: test - arguments: '--filter "TestCategory=MsSql&FullyQualifiedName!~ConfigurationHotReloadTests" --no-build --configuration $(buildConfiguration) --collect "XPlat Code coverage"' - projects: '**/*Tests/*.csproj' - - - - task: DotNetCoreCLI@2 - displayName: 'Hot-Reload Tests' - inputs: - command: test - arguments: '--filter "TestCategory=MsSql&FullyQualifiedName~ConfigurationHotReloadTests" --no-build --configuration $(buildConfiguration) --collect "XPlat Code coverage" --logger "console;verbosity=detailed"' - projects: '**/*Tests/*.csproj' - timeoutInMinutes: 45 - - - task: PublishCodeCoverageResults@1 - displayName: 'Publish code coverage' - inputs: - codeCoverageTool: Cobertura - summaryFileLocation: '$(Agent.TempDirectory)/**/*cobertura.xml' - - - task: CopyFiles@2 - condition: eq(variables['publishverify'], 'Yes') - displayName: 'Copy received files to Artifact Staging' - inputs: - contents: '**\*.received.*' - targetFolder: '$(Build.ArtifactStagingDirectory)\Verify' - cleanTargetFolder: true - overWrite: true + - template: templates/mssql-test-steps.yml + parameters: + testFilter: 'TestCategory=MsSql&(FullyQualifiedName~SqlTests.GraphQL|FullyQualifiedName~ConfigurationHotReloadTests|FullyQualifiedName~SqlTests.Rest|FullyQualifiedName~UnitTests|FullyQualifiedName~OpenApi|FullyQualifiedName~Telemetry|FullyQualifiedName~Authorization|FullyQualifiedName~Caching)' + testDisplayName: 'MsSql Combined Integration Tests' + artifactSuffix: '-Combined' + +- job: windows_configuration + displayName: 'Windows - Configuration Tests' + pool: + vmImage: 'windows-latest' + variables: + solution: '**/*.sln' + buildPlatform: 'Any CPU' + buildConfiguration: 'Release' + # Need to override the connection string set on the pipeline UI + # since windows needs a different string. + # The variable setting on the pipeline UI sets the connection string + # for the linux job above. + data-source.connection-string: Server=(localdb)\MSSQLLocalDB;Persist Security Info=False;Integrated Security=True;MultipleActiveResultSets=False;Connection Timeout=30;TrustServerCertificate=True; + InstallerUrl: https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi + SqlVersionCode: '15.0' - - task: PublishBuildArtifacts@1 - displayName: 'Publish received files as Artifacts' - name: 'verifypublish' - condition: eq(variables['publishverify'], 'Yes') - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)\Verify' - ArtifactName: 'Verify' - publishLocation: 'Container' + steps: + - template: templates/mssql-test-steps.yml + parameters: + testFilter: 'TestCategory=MsSql&FullyQualifiedName!~SqlTests.GraphQL&FullyQualifiedName!~ConfigurationHotReloadTests&FullyQualifiedName!~SqlTests.Rest&FullyQualifiedName!~UnitTests&FullyQualifiedName!~OpenApi&FullyQualifiedName!~Telemetry&FullyQualifiedName!~Authorization&FullyQualifiedName!~Caching' + testDisplayName: 'MsSql Configuration Tests' + artifactSuffix: '-Configuration' + initDbSchema: true diff --git a/.pipelines/templates/mssql-test-steps.yml b/.pipelines/templates/mssql-test-steps.yml new file mode 100644 index 0000000000..d39f5cf8e0 --- /dev/null +++ b/.pipelines/templates/mssql-test-steps.yml @@ -0,0 +1,160 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# Common setup and test execution steps for MSSQL integration test jobs. +# Used by mssql-pipelines.yml to run parallel test jobs. + +parameters: + - name: testFilter + type: string + - name: testDisplayName + type: string + default: 'MsSql Integration Tests' + - name: testTimeout + type: number + default: 0 + - name: additionalTestArgs + type: string + default: '' + - name: artifactSuffix + type: string + default: '' + - name: initDbSchema + type: boolean + default: false + +steps: +- task: CmdLine@2 + displayName: 'Set flag to publish received files' + inputs: + script: 'echo ##vso[task.setvariable variable=publishverify]Yes' + +- task: NuGetAuthenticate@1 + displayName: 'NuGet Authenticate' + +# The .NET CLI commands in proceeding tasks use the .NET SDK version specified ("selected") here. +# Per Microsoft Learn Docs, "Selecting the .NET SDK version is independent from +# specifying the runtime version a project targets." +- task: UseDotNet@2 + displayName: Setup .NET SDK v8.0.x + inputs: + packageType: sdk + version: 8.0.x + +- task: NuGetToolInstaller@1 + +- task: DotNetCoreCLI@2 + displayName: Restore NuGet packages + inputs: + command: restore + projects: '$(solution)' + feedsToUse: config + nugetConfigPath: Nuget.config + restoreArguments: '/p:RuntimeIdentifiers=""' + +- task: PowerShell@2 + displayName: Install SQL LocalDB + inputs: + targetType: 'inline' + script: | + SqlLocalDb.exe start + SqlLocalDB.exe info "MSSQLLocalDB" + Write-Host "Downloading" + Import-Module BitsTransfer + Start-BitsTransfer -Source $(InstallerUrl) -Destination SqlLocalDB.msi + Write-Host "Installing" + Start-Process -FilePath "SqlLocalDB.msi" -Wait -ArgumentList "/qn", "/norestart", "/l*v SqlLocalDBInstall.log", "IACCEPTSQLLOCALDBLICENSETERMS=YES"; + SqlLocalDB.exe stop MSSQLLocalDB -k + SqlLocalDB.exe delete MSSQLLocalDB + +- task: PowerShell@2 + displayName: 'Start MSSQLLocalDB' + inputs: + targetType: 'inline' + script: | + SqlLocalDb.exe start MSSQLLocalDB + SqlLocalDb.exe info "MSSQLLocalDB" + +- ${{ if eq(parameters.initDbSchema, true) }}: + - task: PowerShell@2 + displayName: 'Initialize database schema' + inputs: + targetType: 'inline' + script: | + Write-Host "Running DatabaseSchema-MsSql.sql against LocalDB via SqlClient..." + $sqlFile = "$(System.DefaultWorkingDirectory)\src\Service.Tests\DatabaseSchema-MsSql.sql" + $sql = Get-Content $sqlFile -Raw + $connStr = "Server=(localdb)\MSSQLLocalDB;Integrated Security=True;TrustServerCertificate=True;Connection Timeout=30;" + $connection = New-Object System.Data.SqlClient.SqlConnection($connStr) + try { + $connection.Open() + Write-Host "Connected to LocalDB successfully." + $command = $connection.CreateCommand() + $command.CommandText = $sql + $command.CommandTimeout = 120 + $command.ExecuteNonQuery() | Out-Null + Write-Host "Database schema initialized successfully." + } + catch { + Write-Error "Schema initialization failed: $_" + throw + } + finally { + $connection.Close() + } + +- task: DotNetCoreCLI@2 + displayName: Build + inputs: + command: build + projects: | + **/*.csproj + !**/*Tests*.csproj + arguments: '-p:generateConfigFileForDbType=MsSql --configuration $(buildConfiguration)' # Update this to match your need + +- task: DotNetCoreCLI@2 + displayName: Build Test Projects + inputs: + command: build + projects: '**/*Tests/*.csproj' + arguments: '--configuration $(buildConfiguration)' + +- task: FileTransform@1.206.0 + displayName: 'Generate dab-config.MsSql.json' + inputs: + folderPath: '$(System.DefaultWorkingDirectory)' + fileType: 'json' + targetFiles: 'src/out/tests/*/dab-config.MsSql.json' + +- task: DotNetCoreCLI@2 + displayName: '${{ parameters.testDisplayName }}' + inputs: + command: test + arguments: '--filter "${{ parameters.testFilter }}" --no-build --configuration $(buildConfiguration) --collect "XPlat Code coverage" ${{ parameters.additionalTestArgs }}' + projects: '**/*Tests/*.csproj' + ${{ if ne(parameters.testTimeout, 0) }}: + timeoutInMinutes: ${{ parameters.testTimeout }} + +- task: PublishCodeCoverageResults@1 + displayName: 'Publish code coverage' + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: '$(Agent.TempDirectory)/**/*cobertura.xml' + +- task: CopyFiles@2 + condition: eq(variables['publishverify'], 'Yes') + displayName: 'Copy received files to Artifact Staging' + inputs: + contents: '**\*.received.*' + targetFolder: '$(Build.ArtifactStagingDirectory)\Verify' + cleanTargetFolder: true + overWrite: true + +- task: PublishBuildArtifacts@1 + displayName: 'Publish received files as Artifacts' + name: 'verifypublish' + condition: eq(variables['publishverify'], 'Yes') + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)\Verify' + ArtifactName: 'Verify${{ parameters.artifactSuffix }}' + publishLocation: 'Container' diff --git a/src/Service.Tests/UnitTests/EntitySourceNamesParserUnitTests.cs b/src/Service.Tests/UnitTests/EntitySourceNamesParserUnitTests.cs index 3acf94b946..3e04e4353b 100644 --- a/src/Service.Tests/UnitTests/EntitySourceNamesParserUnitTests.cs +++ b/src/Service.Tests/UnitTests/EntitySourceNamesParserUnitTests.cs @@ -13,7 +13,7 @@ namespace Azure.DataApiBuilder.Service.Tests.UnitTests /// can handle a wide range of valid formats correctly, /// and throws exceptions for invalid formats as expected. /// - [TestClass, TestCategory(TestCategory.MSSQL)] + [TestClass] public class EntitySourceNamesParserUnitTests { diff --git a/src/Service.Tests/UnitTests/RequestContextUnitTests.cs b/src/Service.Tests/UnitTests/RequestContextUnitTests.cs index afbd812628..d6ab84d838 100644 --- a/src/Service.Tests/UnitTests/RequestContextUnitTests.cs +++ b/src/Service.Tests/UnitTests/RequestContextUnitTests.cs @@ -16,7 +16,7 @@ namespace Azure.DataApiBuilder.Service.Tests.UnitTests /// Context classes that are not easily tested through /// integration testing. /// - [TestClass, TestCategory(TestCategory.MSSQL)] + [TestClass] public class RequestContextUnitTests { private static DatabaseObject _defaultDbObject = new DatabaseTable() diff --git a/src/Service.Tests/UnitTests/RequestValidatorUnitTests.cs b/src/Service.Tests/UnitTests/RequestValidatorUnitTests.cs index 5be1375c0f..35c86abc82 100644 --- a/src/Service.Tests/UnitTests/RequestValidatorUnitTests.cs +++ b/src/Service.Tests/UnitTests/RequestValidatorUnitTests.cs @@ -25,7 +25,7 @@ namespace Azure.DataApiBuilder.Service.Tests.UnitTests /// Unit tests for RequestValidator.cs. Makes sure the proper primary key validation /// occurs for REST requests for FindOne(). /// - [TestClass, TestCategory(TestCategory.MSSQL)] + [TestClass] public class RequestValidatorUnitTests { private static Mock _mockMetadataStore; diff --git a/src/Service.Tests/UnitTests/RestServiceUnitTests.cs b/src/Service.Tests/UnitTests/RestServiceUnitTests.cs index 3f296b4403..ac7b3667e6 100644 --- a/src/Service.Tests/UnitTests/RestServiceUnitTests.cs +++ b/src/Service.Tests/UnitTests/RestServiceUnitTests.cs @@ -25,7 +25,7 @@ namespace Azure.DataApiBuilder.Service.Tests.UnitTests { - [TestClass, TestCategory(TestCategory.MSSQL)] + [TestClass] public class RestServiceUnitTests { private static RestService _restService; diff --git a/src/Service.Tests/UnitTests/RuntimeConfigLoaderJsonDeserializerTests.cs b/src/Service.Tests/UnitTests/RuntimeConfigLoaderJsonDeserializerTests.cs index c34290999a..347bf5e328 100644 --- a/src/Service.Tests/UnitTests/RuntimeConfigLoaderJsonDeserializerTests.cs +++ b/src/Service.Tests/UnitTests/RuntimeConfigLoaderJsonDeserializerTests.cs @@ -27,7 +27,7 @@ namespace Azure.DataApiBuilder.Service.Tests.UnitTests /// we throw the right exception when environment /// variable names are not found. /// - [TestClass, TestCategory(TestCategory.MSSQL)] + [TestClass] public class RuntimeConfigLoaderJsonDeserializerTests { #region Positive Tests