Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 45 additions & 117 deletions .pipelines/mssql-pipelines.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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
161 changes: 161 additions & 0 deletions .pipelines/templates/mssql-test-steps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# 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: 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: CmdLine@2
displayName: 'Set flag to publish received files when tests fail'
condition: failed()
inputs:
script: 'echo ##vso[task.setvariable variable=publishverify]Yes'

- 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'
25 changes: 24 additions & 1 deletion src/Config/ConfigFileWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ namespace Azure.DataApiBuilder.Config;
/// <seealso cref="https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher.onchanged#remarks"/>
/// <seealso cref="https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher.notifyfilter"/>
/// <seealso cref="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/change-tokens#:~:text=exponential%20back%2Doff.-,Utilities/Utilities.cs%3A,-C%23"/>
public class ConfigFileWatcher
public class ConfigFileWatcher : IDisposable
{
private bool _disposed;

/// <summary>
/// Watches a specific file for modifications and alerts
/// this class when a change is detected.
Expand Down Expand Up @@ -120,4 +122,25 @@ private void OnConfigFileChange(object sender, FileSystemEventArgs e)
Console.WriteLine("Unable to hot reload configuration file due to " + ex.Message);
}
}

/// <summary>
/// Disposes the file watcher and unsubscribes from events to release
/// file handles and prevent further file change notifications.
/// </summary>
public void Dispose()
{
if (_disposed)
{
return;
}

_disposed = true;

if (_fileWatcher is not null)
{
_fileWatcher.EnableRaisingEvents = false;
_fileWatcher.Changed -= OnConfigFileChange;
_fileWatcher.Dispose();
}
}
}
Loading