# Tests\UserResolverService.Tests.ps1 # Unit Tests for User Multi-Input Resolver & Ambiguity Detection Service BeforeAll { $scriptDir = Split-Path -Parent $PSScriptRoot . (Join-Path $scriptDir "Modules\UserResolverService.ps1") } Describe "UserResolverService Unit Tests" -Tag "Mock", "Unit" { Context "Parse-MultiUserInput Parsing & Tokenization" { It "returns empty array for null, empty or whitespace strings" { @((Parse-MultiUserInput -RawInput $null)).Count | Should -Be 0 @((Parse-MultiUserInput -RawInput "")).Count | Should -Be 0 @((Parse-MultiUserInput -RawInput " `t`r`n ")).Count | Should -Be 0 } It "parses a single user token cleanly" { $tokens = Parse-MultiUserInput -RawInput "gdhong" $tokens.Count | Should -Be 1 $tokens[0] | Should -Be "gdhong" } It "splits tokens by commas, semicolons, tabs, and newlines" { $raw = "gdhong, cskim; yylee`nmschoi`r`njhbak`tswjung" $tokens = Parse-MultiUserInput -RawInput $raw $tokens.Count | Should -Be 6 $tokens | Should -Contain "gdhong" $tokens | Should -Contain "cskim" $tokens | Should -Contain "yylee" $tokens | Should -Contain "mschoi" $tokens | Should -Contain "jhbak" $tokens | Should -Contain "swjung" } It "strips surrounding whitespace from individual tokens" { $raw = " gdhong , cskim ; yylee " $tokens = Parse-MultiUserInput -RawInput $raw $tokens.Count | Should -Be 3 $tokens[0] | Should -Be "gdhong" $tokens[1] | Should -Be "cskim" $tokens[2] | Should -Be "yylee" } It "deduplicates repeated tokens using Select-Unique" { $raw = "gdhong, cskim, gdhong, yylee, cskim, gdhong" $tokens = Parse-MultiUserInput -RawInput $raw $tokens.Count | Should -Be 3 $tokens | Should -Contain "gdhong" $tokens | Should -Contain "cskim" $tokens | Should -Contain "yylee" } It "handles Korean names and Excel pasted tabular data" { $raw = "홍길동`t김철수`r`n이영희,최민수;박지훈" $tokens = Parse-MultiUserInput -RawInput $raw $tokens.Count | Should -Be 5 $tokens | Should -Contain "홍길동" $tokens | Should -Contain "김철수" $tokens | Should -Contain "이영희" } } Context "Resolve-UsersWithAmbiguityCheck - Exact SamAccountName Matching" { It "resolves exact SamAccountName as Normal Match (정상 매칭)" { $results = Resolve-UsersWithAmbiguityCheck -Tokens @("gdhong") $results.Count | Should -Be 1 $r = $results[0] $r.InputToken | Should -Be "gdhong" $r.Status | Should -Be "정상 매칭" $r.IsAmbiguous | Should -BeFalse $r.IsResolved | Should -BeTrue $r.IsSelected | Should -BeTrue $r.SamAccountName | Should -Be "gdhong" $r.DisplayName | Should -Be "홍길동" $r.Department | Should -Be "클라우드인프라팀" $r.CandidateCount | Should -Be 1 } It "performs case-insensitive matching for SamAccountName" { $results = Resolve-UsersWithAmbiguityCheck -Tokens @("CSKIM", "YyLee") $results.Count | Should -Be 2 $results[0].SamAccountName | Should -Be "cskim" $results[0].Status | Should -Be "정상 매칭" $results[0].IsResolved | Should -BeTrue $results[1].SamAccountName | Should -Be "yylee" $results[1].Status | Should -Be "정상 매칭" $results[1].IsResolved | Should -BeTrue } } Context "Resolve-UsersWithAmbiguityCheck - DisplayName & Ambiguity (동명이인) Detection" { It "resolves unique DisplayName as Single Match (단일 매칭)" { $results = Resolve-UsersWithAmbiguityCheck -Tokens @("정수원") $results.Count | Should -Be 1 $r = $results[0] $r.Status | Should -Be "단일 매칭" $r.IsAmbiguous | Should -BeFalse $r.IsResolved | Should -BeTrue $r.IsSelected | Should -BeTrue $r.SamAccountName | Should -Be "swjung" $r.DisplayName | Should -Be "정수원" $r.Department | Should -Be "인사기획팀" $r.CandidateCount | Should -Be 1 } It "flags duplicate names as Ambiguous (동명이인 확인필요) and generates candidate rows" { # '홍길동' exists twice in sample pool (gdhong: 클라우드인프라팀, gdhong_sales: 영업1팀) $results = Resolve-UsersWithAmbiguityCheck -Tokens @("홍길동") $results.Count | Should -Be 2 foreach ($cand in $results) { $cand.InputToken | Should -Be "홍길동" $cand.Status | Should -Be "동명이인 확인필요" $cand.IsAmbiguous | Should -BeTrue $cand.IsResolved | Should -BeFalse $cand.IsSelected | Should -BeFalse # Operator must explicitly verify before selecting $cand.CandidateCount | Should -Be 2 $cand.DisplayName | Should -Be "홍길동" } $sams = @($results | ForEach-Object { $_.SamAccountName }) $sams | Should -Contain "gdhong" $sams | Should -Contain "gdhong_sales" $depts = @($results | ForEach-Object { $_.Department }) $depts | Should -Contain "클라우드인프라팀" $depts | Should -Contain "영업1팀" } It "flags second duplicate name '김철수' with 2 candidate rows" { # '김철수' exists twice (cskim: 데이터플랫폼팀, cskim_dev: 프론트엔드개발팀) $results = Resolve-UsersWithAmbiguityCheck -Tokens @("김철수") $results.Count | Should -Be 2 $sams = @($results | ForEach-Object { $_.SamAccountName }) $sams | Should -Contain "cskim" $sams | Should -Contain "cskim_dev" } } Context "Resolve-UsersWithAmbiguityCheck - Unmatched / Non-existent Users" { It "identifies nonexistent accounts as Not Found (미발견)" { $results = Resolve-UsersWithAmbiguityCheck -Tokens @("nonexistent_user_999") $results.Count | Should -Be 1 $r = $results[0] $r.Status | Should -Be "미발견 (계정 없음)" $r.IsAmbiguous | Should -BeFalse $r.IsResolved | Should -BeFalse $r.IsSelected | Should -BeFalse $r.SamAccountName | Should -Be "-" $r.CandidateCount | Should -Be 0 } } Context "Resolve-UsersWithAmbiguityCheck - Heterogeneous Mixed Batch" { It "accurately classifies mixed tokens across all resolution states" { $tokens = @("gdhong", "홍길동", "unknown_person_x", "jhbak") $results = Resolve-UsersWithAmbiguityCheck -Tokens $tokens # gdhong (1) + 홍길동 (2 candidates) + unknown (1) + jhbak (1) = 5 rows $results.Count | Should -Be 5 $exactRows = @($results | Where-Object { $_.Status -eq "정상 매칭" }) $exactRows.Count | Should -Be 2 @($exactRows.SamAccountName) | Should -Contain "gdhong" @($exactRows.SamAccountName) | Should -Contain "jhbak" $ambRows = @($results | Where-Object { $_.IsAmbiguous }) $ambRows.Count | Should -Be 2 @($ambRows.InputToken) | Should -Contain "홍길동" $missingRows = @($results | Where-Object { $_.Status -like "*미발견*" }) $missingRows.Count | Should -Be 1 $missingRows[0].InputToken | Should -Be "unknown_person_x" } } Context "Resolve-UsersWithAmbiguityCheck - Edge Cases & Security" { It "does not match all users when passed empty or whitespace tokens" { # Regression test: $_.DisplayName -like '**' previously returned all 8 pool users $resEmpty = Resolve-UsersWithAmbiguityCheck -Tokens @("", " `t`r`n ") @($resEmpty).Count | Should -Be 0 } It "safely handles wildcard tokens without pattern expansion" { # An asterisk should NOT match all users via wildcard expansion $resWildcard = Resolve-UsersWithAmbiguityCheck -Tokens @("*") @($resWildcard).Count | Should -Be 1 $resWildcard[0].Status | Should -Be "미발견 (계정 없음)" $resWildcard[0].CandidateCount | Should -Be 0 } } }