feat: add comprehensive Pester and standalone E2E test suite (96 tests) and code audit report
This commit is contained in:
+14
-8
@@ -11,7 +11,7 @@ function Test-ADConnection {
|
||||
)
|
||||
|
||||
try {
|
||||
$protocol = if ($UseLdaps) { "LDAP://$DomainController:636" } else { "LDAP://$DomainController" }
|
||||
$protocol = if ($UseLdaps) { "LDAP://${DomainController}:636" } else { "LDAP://$DomainController" }
|
||||
$path = if ([string]::IsNullOrWhiteSpace($BaseOU)) { "$protocol/RootDSE" } else { "$protocol/$BaseOU" }
|
||||
|
||||
$entry = if ([string]::IsNullOrWhiteSpace($Username)) {
|
||||
@@ -21,7 +21,10 @@ function Test-ADConnection {
|
||||
}
|
||||
|
||||
# 바인딩 강제 테스트
|
||||
$null = $entry.NativeObject
|
||||
if ($null -eq $entry.NativeObject) {
|
||||
throw "Active Directory DC에 바인딩할 수 없습니다. (NativeObject null)"
|
||||
}
|
||||
$null = $entry.Guid
|
||||
return [PSCustomObject]@{
|
||||
Success = $true
|
||||
Message = "Active Directory DC ($DomainController) 연결 성공 (RootDSE 응답 확인)"
|
||||
@@ -52,7 +55,7 @@ function Get-ADUserList {
|
||||
$pass = $Config.ActiveDirectory.Password
|
||||
|
||||
try {
|
||||
$prefix = if ($useLdaps) { "LDAP://$dc:636" } else { "LDAP://$dc" }
|
||||
$prefix = if ($useLdaps) { "LDAP://${dc}:636" } else { "LDAP://$dc" }
|
||||
$rootPath = if ([string]::IsNullOrWhiteSpace($baseOu)) { $prefix } else { "$prefix/$baseOu" }
|
||||
|
||||
$rootEntry = if ([string]::IsNullOrWhiteSpace($user)) {
|
||||
@@ -102,7 +105,9 @@ function Get-ADUserList {
|
||||
if ($fileTime -gt 0) {
|
||||
$lastLogonStr = [DateTime]::FromFileTime($fileTime).ToString("yyyy-MM-dd HH:mm:ss")
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
Write-Verbose "lastLogonTimestamp 파싱 건너뜀: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
$userList.Add([PSCustomObject]@{
|
||||
@@ -201,7 +206,7 @@ function Reset-ADUserPassword {
|
||||
$pass = $Config.ActiveDirectory.Password
|
||||
|
||||
try {
|
||||
$rootPath = if ([string]::IsNullOrWhiteSpace($baseOu)) { "LDAP://$dc:636" } else { "LDAP://$dc:636/$baseOu" }
|
||||
$rootPath = if ([string]::IsNullOrWhiteSpace($baseOu)) { "LDAP://${dc}:636" } else { "LDAP://${dc}:636/$baseOu" }
|
||||
$rootEntry = if ([string]::IsNullOrWhiteSpace($user)) {
|
||||
New-Object System.DirectoryServices.DirectoryEntry($rootPath)
|
||||
} else {
|
||||
@@ -367,13 +372,14 @@ function Get-ADSampleUsers {
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Filter)) {
|
||||
return $users
|
||||
return ,$users
|
||||
} else {
|
||||
return @($users | Where-Object {
|
||||
$matched = @($users | Where-Object {
|
||||
$_.SamAccountName -like "*$Filter*" -or
|
||||
$_.DisplayName -like "*$Filter*" -or
|
||||
$_.Department -like "*$Filter*"
|
||||
})
|
||||
return ,$matched
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +448,7 @@ function New-ADSecurityGroupOnTargetDc {
|
||||
$user = $Config.ActiveDirectory.Username
|
||||
$pass = $Config.ActiveDirectory.Password
|
||||
$useLdaps = [bool]$Config.ActiveDirectory.UseLdaps
|
||||
$proto = if ($useLdaps) { "LDAP://$TargetDc:636" } else { "LDAP://$TargetDc" }
|
||||
$proto = if ($useLdaps) { "LDAP://${TargetDc}:636" } else { "LDAP://$TargetDc" }
|
||||
$ouPath = "$proto/$TargetOU"
|
||||
|
||||
$ouEntry = if ([string]::IsNullOrWhiteSpace($user)) {
|
||||
|
||||
@@ -566,7 +566,7 @@ function New-NetAppQtreeAction {
|
||||
$body["svm"] = @{ name = $svm }
|
||||
}
|
||||
|
||||
$res = Invoke-NetAppRestRequest -ClusterEndpoint $endpoint -UriPath "/api/storage/qtrees" -Method "POST" -Body $body -Username $user -Password $pass -IgnoreSsl $ignoreSsl -TimeoutSec $TimeoutSec
|
||||
$null = Invoke-NetAppRestRequest -ClusterEndpoint $endpoint -UriPath "/api/storage/qtrees" -Method "POST" -Body $body -Username $user -Password $pass -IgnoreSsl $ignoreSsl -TimeoutSec $TimeoutSec
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Success = $true
|
||||
@@ -624,7 +624,7 @@ function New-NetAppShareAction {
|
||||
$body["svm"] = @{ name = $svm }
|
||||
}
|
||||
|
||||
$res = Invoke-NetAppRestRequest -ClusterEndpoint $endpoint -UriPath "/api/protocols/cifs/shares" -Method "POST" -Body $body -Username $user -Password $pass -IgnoreSsl $ignoreSsl -TimeoutSec $TimeoutSec
|
||||
$null = Invoke-NetAppRestRequest -ClusterEndpoint $endpoint -UriPath "/api/protocols/cifs/shares" -Method "POST" -Body $body -Username $user -Password $pass -IgnoreSsl $ignoreSsl -TimeoutSec $TimeoutSec
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Success = $true
|
||||
@@ -711,7 +711,7 @@ function Set-NetAppShareAcl {
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Success = $false
|
||||
Message = "NetApp Share ACL 반영 실패 ($MaxRetries회 재시도 초과): $lastError"
|
||||
Message = "NetApp Share ACL 반영 실패 ($($MaxRetries)회 재시도 초과): $lastError"
|
||||
Attempts = $attempt
|
||||
IsLive = $true
|
||||
}
|
||||
@@ -772,7 +772,7 @@ function Set-NetAppQuotaRule {
|
||||
elseif ($QuotaType -eq "group") { $body["group"] = @{ name = $Target } }
|
||||
}
|
||||
|
||||
$res = Invoke-NetAppRestRequest -ClusterEndpoint $endpoint -UriPath "/api/storage/quota/rules" -Method "POST" -Body $body -Username $user -Password $pass -IgnoreSsl $ignoreSsl -TimeoutSec $TimeoutSec
|
||||
$null = Invoke-NetAppRestRequest -ClusterEndpoint $endpoint -UriPath "/api/storage/quota/rules" -Method "POST" -Body $body -Username $user -Password $pass -IgnoreSsl $ignoreSsl -TimeoutSec $TimeoutSec
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Success = $true
|
||||
|
||||
@@ -106,6 +106,40 @@ SELECT
|
||||
'jhbak' AS SamAccountName, '박지훈' AS UserName, '시스템관리팀' AS Department, 1 AS IsAssigned, '2026-10-01' AS AssignedDate;
|
||||
"@
|
||||
|
||||
# 실제 DB 연결 모드일 경우 실행
|
||||
$useLive = if ($Config -and $Config.MsSql) { [bool]$Config.MsSql.UseLive } else { $false }
|
||||
if ($useLive) {
|
||||
try {
|
||||
$timeout = if ($Config.MsSql.ConnectionTimeout) { [int]$Config.MsSql.ConnectionTimeout } else { 3 }
|
||||
$connStr = Build-SqlConnectionString -Server $Config.MsSql.Server -AuthType $Config.MsSql.AuthType -Username $Config.MsSql.Username -Password $Config.MsSql.Password -Timeout $timeout
|
||||
$conn = New-Object System.Data.SqlClient.SqlConnection($connStr)
|
||||
$conn.Open()
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = $customQuery
|
||||
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
|
||||
$table = New-Object System.Data.DataTable
|
||||
$adapter.Fill($table) | Out-Null
|
||||
$conn.Close()
|
||||
|
||||
$list = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
foreach ($row in $table.Rows) {
|
||||
$isAssigned = [bool]$row["IsAssigned"]
|
||||
$list.Add([PSCustomObject]@{
|
||||
IsSelected = $false
|
||||
SamAccountName = $row["SamAccountName"]
|
||||
UserName = $row["UserName"]
|
||||
Department = $row["Department"]
|
||||
Status = if ($isAssigned) { "할당됨" } else { "미할당 (해제됨)" }
|
||||
IsAssigned = $isAssigned
|
||||
AssignedDate = if ($row["AssignedDate"] -and $row["AssignedDate"] -ne [System.DBNull]::Value) { $row["AssignedDate"].ToString() } else { "-" }
|
||||
})
|
||||
}
|
||||
return $list.ToArray()
|
||||
} catch {
|
||||
Write-Warning "DB 사용자 권한 조회 실패: $($_.Exception.Message) (샘플 데이터 반환)"
|
||||
}
|
||||
}
|
||||
|
||||
# 목업 및 DB 결과 파싱
|
||||
return @(
|
||||
[PSCustomObject]@{ IsSelected = $false; SamAccountName = "gdhong"; UserName = "홍길동"; Department = "클라우드인프라팀"; Status = "할당됨"; IsAssigned = $true; AssignedDate = "2026-09-01" },
|
||||
@@ -184,6 +218,10 @@ function Add-UsersToShareAndGroup {
|
||||
|
||||
$logs = [System.Collections.Generic.List[string]]::new()
|
||||
$logs.Add("[시작] Share [$ShareName] (AD 보안그룹: $ShareName)에 사용자 추가 작업")
|
||||
$useLive = if ($Config -and $Config.ActiveDirectory) { [bool]$Config.ActiveDirectory.UseLive } else { $false }
|
||||
if ($useLive) {
|
||||
$logs.Add(" -> [Live Mode] 실제 AD 및 DB 인프라 반영 모드 활성화됨")
|
||||
}
|
||||
|
||||
foreach ($sam in $SamAccountNames) {
|
||||
# --- (A) AD 보안그룹에 사용자 객체 추가 로직 ---
|
||||
@@ -218,6 +256,10 @@ function Remove-UsersFromShareAndGroup {
|
||||
|
||||
$logs = [System.Collections.Generic.List[string]]::new()
|
||||
$logs.Add("[시작] Share [$ShareName] (AD 보안그룹: $ShareName)에서 사용자 제거/해제 작업")
|
||||
$useLive = if ($Config -and $Config.ActiveDirectory) { [bool]$Config.ActiveDirectory.UseLive } else { $false }
|
||||
if ($useLive) {
|
||||
$logs.Add(" -> [Live Mode] 실제 AD 및 DB 인프라 반영 모드 활성화됨")
|
||||
}
|
||||
|
||||
foreach ($sam in $SamAccountNames) {
|
||||
# --- (A) AD 보안그룹에서 사용자 객체 제거 로직 ---
|
||||
|
||||
@@ -46,7 +46,7 @@ public class WinAuthUniversalImpersonator {
|
||||
}
|
||||
"@
|
||||
|
||||
if (-not ([System.Management.Automation.PSTypeName]"WinAuthUniversalImpersonator").Type) {
|
||||
if (-not ("WinAuthUniversalImpersonator" -as [type])) {
|
||||
Add-Type -TypeDefinition $winAuthTypeDef
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ function Invoke-SqlQuerySnippet {
|
||||
return [PSCustomObject]@{
|
||||
Success = $true
|
||||
RowCount = 10
|
||||
Output = "(데모 모드 성공: 10개 행 반환됨 - $((Get-Date).ToString('HH:mm:ss')))`n1. tbl_Users (Row: 14,200)`n2. tbl_AuditLogs (Row: 240,119)`n3. tbl_Orders (Row: 82,900)"
|
||||
Output = "(데모 모드 성공: [$Query] - 10개 행 반환됨 - $((Get-Date).ToString('HH:mm:ss')))`n1. tbl_Users (Row: 14,200)`n2. tbl_AuditLogs (Row: 240,119)`n3. tbl_Orders (Row: 82,900)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,11 +431,12 @@ function Get-SqlSampleDatabases {
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Filter)) {
|
||||
return $databases
|
||||
return ,$databases
|
||||
} else {
|
||||
return @($databases | Where-Object {
|
||||
$matched = @($databases | Where-Object {
|
||||
$_.DatabaseName -like "*$Filter*" -or
|
||||
$_.StateDesc -like "*$Filter*"
|
||||
})
|
||||
return ,$matched
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,13 @@ function Parse-MultiUserInput {
|
||||
param([string]$RawInput)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($RawInput)) {
|
||||
return @()
|
||||
return ,@()
|
||||
}
|
||||
|
||||
# 쉼표, 세미콜론, 줄바꿈, 탭, 공백 등 모든 구분자로 분리
|
||||
$tokens = $RawInput -split "[\r\n,;\t]+" | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
|
||||
return @($tokens | Select-Object -Unique)
|
||||
$uniqueTokens = @($tokens | Select-Object -Unique)
|
||||
return ,$uniqueTokens
|
||||
}
|
||||
|
||||
function Resolve-UsersWithAmbiguityCheck {
|
||||
@@ -39,11 +40,28 @@ function Resolve-UsersWithAmbiguityCheck {
|
||||
[PSCustomObject]@{ SamAccountName = "swjung"; DisplayName = "정수원"; Department = "인사기획팀"; Title = "차장"; Mail = "swjung@corp.local"; EmpId = "EMP1006" }
|
||||
)
|
||||
|
||||
$pool = $allPool
|
||||
if ($Config -and $Config.ActiveDirectory -and [bool]$Config.ActiveDirectory.UseLive -and (Get-Command Get-ADUserList -ErrorAction SilentlyContinue)) {
|
||||
try {
|
||||
$liveUsers = Get-ADUserList -Config $Config
|
||||
if ($liveUsers -and $liveUsers.Count -gt 0) {
|
||||
$pool = $liveUsers
|
||||
}
|
||||
} catch {
|
||||
Write-Warning "Live AD 사용자 풀 조회 실패, 기본 풀을 사용합니다: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
$resolvedResults = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
|
||||
foreach ($token in $Tokens) {
|
||||
foreach ($rawToken in $Tokens) {
|
||||
if ([string]::IsNullOrWhiteSpace($rawToken)) {
|
||||
continue
|
||||
}
|
||||
$token = $rawToken.Trim()
|
||||
|
||||
# 1. SamAccountName 정확 일치 우선 검색
|
||||
$matchById = @($allPool | Where-Object { $_.SamAccountName -ieq $token })
|
||||
$matchById = @($pool | Where-Object { $_.SamAccountName -ieq $token })
|
||||
if ($matchById.Count -eq 1) {
|
||||
$u = $matchById[0]
|
||||
$resolvedResults.Add([PSCustomObject]@{
|
||||
@@ -64,8 +82,11 @@ function Resolve-UsersWithAmbiguityCheck {
|
||||
continue
|
||||
}
|
||||
|
||||
# 2. 이름(DisplayName) 일치 검색 (동명이인 판정)
|
||||
$matchByName = @($allPool | Where-Object { $_.DisplayName -ieq $token -or $_.DisplayName -like "*$token*" })
|
||||
# 2. 이름(DisplayName) 일치 검색 (동명이인 판정 - 와일드카드 안전 처리)
|
||||
$matchByName = @($pool | Where-Object {
|
||||
$_.DisplayName -ieq $token -or
|
||||
($_.DisplayName -and ($_.DisplayName.IndexOf($token, [System.StringComparison]::OrdinalIgnoreCase) -ge 0))
|
||||
})
|
||||
if ($matchByName.Count -eq 1) {
|
||||
$u = $matchByName[0]
|
||||
$resolvedResults.Add([PSCustomObject]@{
|
||||
@@ -124,5 +145,5 @@ function Resolve-UsersWithAmbiguityCheck {
|
||||
}
|
||||
}
|
||||
|
||||
return $resolvedResults.ToArray()
|
||||
return ,$resolvedResults.ToArray()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user