Files
ModernAdminStudio/Tests/SqlService.Tests.ps1
T

172 lines
8.0 KiB
PowerShell
Raw Normal View History

# 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
}
}
}