172 lines
8.0 KiB
PowerShell
172 lines
8.0 KiB
PowerShell
# Tests\SqlService.Tests.ps1
|
|
# Unit & Integration Tests for SqlService Module
|
|
|
|
BeforeAll {
|
|
$scriptDir = Split-Path -Parent $PSScriptRoot
|
|
. (Join-Path $scriptDir "Modules\SqlService.ps1")
|
|
}
|
|
|
|
Describe "SqlService Unit Tests" -Tag "Mock", "Unit" {
|
|
Context "Build-SqlConnectionString Builder" {
|
|
It "builds Integrated Security connection string for 'Integrated' auth mode" {
|
|
$connStr = Build-SqlConnectionString -Server "sql01.corp.local" -AuthType "Integrated" -DefaultDatabase "master" -Timeout 10
|
|
$connStr | Should -Match "Data Source=sql01\.corp\.local"
|
|
$connStr | Should -Match "Integrated Security=True"
|
|
$connStr | Should -Match "Initial Catalog=master"
|
|
$connStr | Should -Match "Connect Timeout=10"
|
|
$connStr | Should -Match "Application Name=EnterpriseAdminStudio"
|
|
}
|
|
|
|
It "builds Integrated Security connection string for 'AdImpersonated' auth mode" {
|
|
# AdImpersonated uses Windows token impersonation on caller thread; SQL Server sees Windows Integrated Security
|
|
$connStr = Build-SqlConnectionString -Server "sql01.corp.local" -AuthType "AdImpersonated" -Username "CORP\admin_user" -Password "Secret123!" -DefaultDatabase "ERP_DB"
|
|
$connStr | Should -Match "Integrated Security=True"
|
|
$connStr | Should -Match "Initial Catalog=ERP_DB"
|
|
$connStr | Should -Not -Match "Password="
|
|
$connStr | Should -Not -Match "User ID="
|
|
}
|
|
|
|
It "builds SQL authentication connection string with User ID and Password for 'Sql' auth mode" {
|
|
$connStr = Build-SqlConnectionString -Server "sql01.corp.local" -AuthType "Sql" -Username "sa" -Password "SqlPass!2026" -DefaultDatabase "HR_DB"
|
|
$connStr | Should -Match "Integrated Security=False"
|
|
$connStr | Should -Match "User ID=sa"
|
|
$connStr | Should -Match "Password=SqlPass!2026"
|
|
$connStr | Should -Match "Initial Catalog=HR_DB"
|
|
}
|
|
|
|
It "defaults Initial Catalog to master when DefaultDatabase is empty" {
|
|
$connStr = Build-SqlConnectionString -Server "localhost" -AuthType "Integrated" -DefaultDatabase ""
|
|
$connStr | Should -Match "Initial Catalog=master"
|
|
}
|
|
}
|
|
|
|
Context "Windows Impersonation (LOGON32_LOGON_NEW_CREDENTIALS) Architecture" {
|
|
It "verifies WinAuthUniversalImpersonator type is compiled and available" {
|
|
$type = "WinAuthUniversalImpersonator" -as [type]
|
|
$type | Should -Not -BeNullOrEmpty
|
|
}
|
|
|
|
It "verifies LOGON32_LOGON_NEW_CREDENTIALS constant is set to 9 (/netonly outbound network token)" {
|
|
[WinAuthUniversalImpersonator]::LOGON32_LOGON_NEW_CREDENTIALS | Should -Be 9
|
|
}
|
|
|
|
It "verifies LOGON32_PROVIDER_DEFAULT constant is set to 0" {
|
|
[WinAuthUniversalImpersonator]::LOGON32_PROVIDER_DEFAULT | Should -Be 0
|
|
}
|
|
|
|
It "executes standard Action directly when AuthType is Integrated" {
|
|
$state = @{ Executed = $false }
|
|
$res = Invoke-WithSqlAuthContext -AuthType "Integrated" -Action {
|
|
$state.Executed = $true
|
|
return "integrated_result"
|
|
}
|
|
$state.Executed | Should -BeTrue
|
|
$res | Should -Be "integrated_result"
|
|
}
|
|
|
|
It "executes standard Action directly when AuthType is Sql" {
|
|
$state = @{ Executed = $false }
|
|
$res = Invoke-WithSqlAuthContext -AuthType "Sql" -Username "sa" -Password "pass" -Action {
|
|
$state.Executed = $true
|
|
return "sql_auth_result"
|
|
}
|
|
$state.Executed | Should -BeTrue
|
|
$res | Should -Be "sql_auth_result"
|
|
}
|
|
|
|
It "runs action inside RunImpersonatedAction when AuthType is AdImpersonated" {
|
|
# LOGON32_LOGON_NEW_CREDENTIALS creates outbound credentials handle locally
|
|
$res = Invoke-WithSqlAuthContext -AuthType "AdImpersonated" -Domain "CORP" -Username "svc_sql_admin" -Password "SecretPass123!" -Action {
|
|
return "impersonation_block_success"
|
|
}
|
|
$res | Should -Be "impersonation_block_success"
|
|
}
|
|
|
|
It "correctly parses DOMAIN\Username and Username@domain into domain and user parts" {
|
|
$res1 = Invoke-WithSqlAuthContext -AuthType "AdImpersonated" -Username "CORP\admin_svc" -Password "Pass1!" -Action {
|
|
return "domain_slash_handled"
|
|
}
|
|
$res1 | Should -Be "domain_slash_handled"
|
|
|
|
$res2 = Invoke-WithSqlAuthContext -AuthType "AdImpersonated" -Username "admin_svc@corp.local" -Password "Pass1!" -Action {
|
|
return "upn_handled"
|
|
}
|
|
$res2 | Should -Be "upn_handled"
|
|
}
|
|
|
|
It "propagates exceptions thrown inside impersonated action block" {
|
|
{
|
|
Invoke-WithSqlAuthContext -AuthType "AdImpersonated" -Domain "CORP" -Username "admin_svc" -Password "Pass1!" -Action {
|
|
throw "SQL connection aborted inside impersonation context"
|
|
}
|
|
} | Should -Throw "*SQL connection aborted*"
|
|
}
|
|
}
|
|
|
|
Context "Mock Data & Database Inspection" {
|
|
It "retrieves sample databases with complete schema attributes" {
|
|
$dbs = Get-SqlSampleDatabases
|
|
$dbs.Count | Should -Be 5
|
|
|
|
$erp = $dbs | Where-Object { $_.DatabaseName -eq "ERP_PRODUCTION" }
|
|
$erp | Should -Not -BeNullOrEmpty
|
|
$erp.StateDesc | Should -Be "ONLINE"
|
|
$erp.RecoveryModel | Should -Be "FULL"
|
|
$erp.IsLive | Should -BeFalse
|
|
}
|
|
|
|
It "filters sample databases by name" {
|
|
$filtered = Get-SqlSampleDatabases -Filter "ERP"
|
|
@($filtered).Count | Should -Be 1
|
|
$filtered[0].DatabaseName | Should -Be "ERP_PRODUCTION"
|
|
}
|
|
|
|
It "filters sample databases by status" {
|
|
$readOnly = Get-SqlSampleDatabases -Filter "READ_ONLY"
|
|
@($readOnly).Count | Should -Be 1
|
|
$readOnly[0].DatabaseName | Should -Be "ARCHIVE_2025"
|
|
}
|
|
|
|
It "returns fallback sample data via Get-SqlDatabases when UseLive is false" {
|
|
$cfg = @{ MsSql = @{ UseLive = $false } }
|
|
$dbs = Get-SqlDatabases -Config $cfg
|
|
$dbs.Count | Should -Be 5
|
|
}
|
|
}
|
|
|
|
Context "Database Backup & Query Operations (Mock Mode)" {
|
|
It "simulates full database backup and returns target backup path with timestamp" {
|
|
$cfg = @{ MsSql = @{ UseLive = $false } }
|
|
$res = Invoke-SqlBackupAction -DatabaseName "ERP_PRODUCTION" -BackupType "FULL" -Config $cfg
|
|
$res.Success | Should -BeTrue
|
|
$res.Database | Should -Be "ERP_PRODUCTION"
|
|
$res.BackupPath | Should -Match "ERP_PRODUCTION.*\.bak$"
|
|
$res.Message | Should -Match "가상 완료"
|
|
}
|
|
|
|
It "simulates query execution and returns formatted table output" {
|
|
$cfg = @{ MsSql = @{ UseLive = $false } }
|
|
$res = Invoke-SqlQuerySnippet -Query "SELECT name FROM sys.databases" -Config $cfg
|
|
$res.Success | Should -BeTrue
|
|
$res.RowCount | Should -Be 10
|
|
$res.Output | Should -Match "tbl_Users"
|
|
}
|
|
}
|
|
|
|
Context "Connection Testing & Resilience (Live Mode)" -Tag "Live" {
|
|
It "safely handles unreachable server without throwing uncaught exception" {
|
|
# Use immediate rejection on localhost port to avoid long TCP timeout
|
|
$res = Test-SqlConnection -Server "127.0.0.1,65530" -AuthType "Integrated" -Timeout 1
|
|
$res.Success | Should -BeFalse
|
|
$res.Message | Should -Match "SQL Server 연결 실패"
|
|
}
|
|
|
|
It "falls back to sample databases when live SQL server is unreachable" {
|
|
$cfg = @{ MsSql = @{ UseLive = $true; Server = "127.0.0.1,65530"; ConnectionTimeout = 1; AuthType = "Integrated" } }
|
|
$dbs = Get-SqlDatabases -Config $cfg
|
|
$dbs | Should -Not -BeNullOrEmpty
|
|
$dbs.Count | Should -Be 5
|
|
}
|
|
}
|
|
}
|