MegaBites-AI/Windows-powershell
0372
1# Copyright (c) Microsoft Corporation.2# Licensed under the MIT License.3Describe 'ConvertTo-Json' -tags "CI" {4 BeforeAll {5 $newline = [System.Environment]::NewLine6 }7 8 It 'Newtonsoft.Json.Linq.Jproperty should be converted to Json properly' {9 $EgJObject = New-Object -TypeName Newtonsoft.Json.Linq.JObject10 $EgJObject.Add("TestValue1", "123456")11 $EgJObject.Add("TestValue2", "78910")12 $EgJObject.Add("TestValue3", "99999")13 $dict = @{}14 $dict.Add('JObject', $EgJObject)15 $dict.Add('StrObject', 'This is a string Object')16 $properties = @{'DictObject' = $dict; 'RandomString' = 'A quick brown fox jumped over the lazy dog'}17 $object = New-Object -TypeName psobject -Property $properties18 $jsonFormat = ConvertTo-Json -InputObject $object19 $jsonFormat | Should -Match '"TestValue1": 123456'20 $jsonFormat | Should -Match '"TestValue2": 78910'21 $jsonFormat | Should -Match '"TestValue3": 99999'22 }23 24 It "StopProcessing should succeed" -Pending:$true {25 $ps = [PowerShell]::Create()26 $null = $ps.AddScript({27 $obj = [PSCustomObject]@{P1 = ''; P2 = ''; P3 = ''; P4 = ''; P5 = ''; P6 = ''}28 $obj.P1 = $obj.P2 = $obj.P3 = $obj.P4 = $obj.P5 = $obj.P6 = $obj29 1..100 | ForEach-Object { $obj } | ConvertTo-Json -Depth 10 -Verbose30 # the conversion is expected to take some time, this throw is in case it doesn't31 throw "Should not have thrown exception"32 })33 $null = $ps.BeginInvoke()34 # wait for verbose message from ConvertTo-Json to ensure cmdlet is processing35 Wait-UntilTrue { $ps.Streams.Verbose.Count -gt 0 } | Should -BeTrue36 $null = $ps.BeginStop($null, $null)37 # wait a bit to ensure state has changed, not using synchronous Stop() to avoid blocking Pester38 Start-Sleep -Milliseconds 10039 $ps.InvocationStateInfo.State | Should -BeExactly "Stopped"40 $ps.Dispose()41 }42 43 It "Should accept minimum depth as 0." {44 $ComplexObject = [PSCustomObject] @{45 FirstLevel1 = @{46 Child1_1 = 047 Bool = $True48 }49 FirstLevel2 = @{50 Child2_1 = 'Child_2_1_Value'51 Child2_2 = @{52 ChildOfChild2_2= @(1,2,3)53 }54 Float = 1.255 }56 Integer = 1057 Bool = $False58 }59 60 $ExpectedOutput = '{61 "FirstLevel1": "System.Collections.Hashtable",62 "FirstLevel2": "System.Collections.Hashtable",63 "Integer": 10,64 "Bool": false65}'66 67 $output = $ComplexObject | ConvertTo-Json -Depth 068 $output | Should -Be $ExpectedOutput69 }70 71 It "The result string is packed in an array symbols when AsArray parameter is used." {72 $output = 1 | ConvertTo-Json -AsArray73 $output | Should -BeLike "``[*1*]"74 75 $output = 1,2 | ConvertTo-Json -AsArray76 $output | Should -BeLike "``[*1*2*]"77 }78 79 It "The result string is not packed in the array symbols when there is only one input object and AsArray parameter is not used." {80 $output = 1 | ConvertTo-Json81 $output | Should -BeExactly '1'82 }83 84 It "The result string should <Name>." -TestCases @(85 @{name = "be not escaped by default."; params = @{}; expected = "{$newline ""abc"": ""'def'""$newline}" }86 @{name = "be not escaped with '-EscapeHandling Default'."; params = @{EscapeHandling = 'Default'}; expected = "{$newline ""abc"": ""'def'""$newline}" }87 @{name = "be escaped with '-EscapeHandling EscapeHtml'."; params = @{EscapeHandling = 'EscapeHtml'}; expected = "{$newline ""abc"": ""\u0027def\u0027""$newline}" }88 ) {89 param ($name, $params ,$expected)90 91 @{ 'abc' = "'def'" } | ConvertTo-Json @params | Should -BeExactly $expected92 }93 94 It "Should handle null" {95 [pscustomobject] @{ prop=$null } | ConvertTo-Json -Compress | Should -BeExactly '{"prop":null}'96 $null | ConvertTo-Json -Compress | Should -Be 'null'97 ConvertTo-Json -Compress $null | Should -Be 'null'98 1, $null, 2 | ConvertTo-Json -Compress | Should -Be '[1,null,2]'99 }100 101 It "Should handle 'AutomationNull.Value' and 'NullString.Value' correctly" {102 [ordered]@{103 a = $null;104 b = [System.Management.Automation.Internal.AutomationNull]::Value;105 c = [System.DBNull]::Value;106 d = [NullString]::Value107 } | ConvertTo-Json -Compress | Should -BeExactly '{"a":null,"b":null,"c":null,"d":null}'108 109 ConvertTo-Json ([System.Management.Automation.Internal.AutomationNull]::Value) | Should -BeExactly 'null'110 ConvertTo-Json ([NullString]::Value) | Should -BeExactly 'null'111 112 ConvertTo-Json -Compress @(113 $null,114 [System.Management.Automation.Internal.AutomationNull]::Value,115 [System.DBNull]::Value,116 [NullString]::Value117 ) | Should -BeExactly '[null,null,null,null]'118 }119 120 It "Should handle the ETS properties added to 'DBNull.Value' and 'NullString.Value'" {121 try122 {123 $p1 = Add-Member -InputObject ([System.DBNull]::Value) -MemberType NoteProperty -Name dbnull -Value 'dbnull' -PassThru124 $p2 = Add-Member -InputObject ([NullString]::Value) -MemberType NoteProperty -Name nullstr -Value 'nullstr' -PassThru125 126 $p1, $p2 | ConvertTo-Json -Compress | Should -BeExactly '[{"value":null,"dbnull":"dbnull"},{"value":null,"nullstr":"nullstr"}]'127 }128 finally129 {130 $p1.psobject.Properties.Remove('dbnull')131 $p2.psobject.Properties.Remove('nullstr')132 }133 }134 135 It 'Should not serialize ETS properties added to DateTime' {136 $date = "2021-06-24T15:54:06.796999-07:00"137 $d = [DateTime]::Parse($date)138 139 # need to use wildcard here due to some systems may be configured with different culture setting showing time in different format140 $d | ConvertTo-Json -Compress | Should -BeLike '"2021-06-24T*'141 $d | ConvertTo-Json | ConvertFrom-Json | Should -Be $d142 }143 144 It 'Should not serialize ETS properties added to String' {145 $text = "Hello there"146 $t = Add-Member -InputObject $text -MemberType NoteProperty -Name text -Value $text -PassThru147 $t | ConvertTo-Json -Compress | Should -BeExactly "`"$text`""148 }149 150 It 'Should serialize BigInteger values' {151 $obj = [Ordered]@{152 Positive = 18446744073709551615n153 Negative = -18446744073709551615n154 }155 156 $actual = ConvertTo-Json -Compress -InputObject $obj157 $actual | Should -Be '{"Positive":18446744073709551615,"Negative":-18446744073709551615}'158 }159 #region Comprehensive Scalar Type Tests (Phase 1)160 # Test coverage for ConvertTo-Json scalar serialization161 # Covers: Pipeline vs InputObject, ETS vs no ETS, all primitive and special types162 163 Context 'Primitive scalar types' {164 It 'Should serialize <TypeName> value <Value> correctly via Pipeline and InputObject' -TestCases @(165 # Byte types166 @{ TypeName = 'byte'; Value = [byte]0; Expected = '0' }167 @{ TypeName = 'byte'; Value = [byte]255; Expected = '255' }168 @{ TypeName = 'sbyte'; Value = [sbyte]-128; Expected = '-128' }169 @{ TypeName = 'sbyte'; Value = [sbyte]127; Expected = '127' }170 # Short types171 @{ TypeName = 'short'; Value = [short]-32768; Expected = '-32768' }172 @{ TypeName = 'short'; Value = [short]32767; Expected = '32767' }173 @{ TypeName = 'ushort'; Value = [ushort]0; Expected = '0' }174 @{ TypeName = 'ushort'; Value = [ushort]65535; Expected = '65535' }175 # Integer types176 @{ TypeName = 'int'; Value = 42; Expected = '42' }177 @{ TypeName = 'int'; Value = -42; Expected = '-42' }178 @{ TypeName = 'int'; Value = 0; Expected = '0' }179 @{ TypeName = 'int'; Value = [int]::MaxValue; Expected = '2147483647' }180 @{ TypeName = 'int'; Value = [int]::MinValue; Expected = '-2147483648' }181 @{ TypeName = 'uint'; Value = [uint]0; Expected = '0' }182 @{ TypeName = 'uint'; Value = [uint]::MaxValue; Expected = '4294967295' }183 # Long types184 @{ TypeName = 'long'; Value = [long]::MaxValue; Expected = '9223372036854775807' }185 @{ TypeName = 'long'; Value = [long]::MinValue; Expected = '-9223372036854775808' }186 @{ TypeName = 'ulong'; Value = [ulong]0; Expected = '0' }187 @{ TypeName = 'ulong'; Value = [ulong]::MaxValue; Expected = '18446744073709551615' }188 # Floating-point types189 @{ TypeName = 'float'; Value = [float]3.14; Expected = '3.14' }190 @{ TypeName = 'float'; Value = [float]::NaN; Expected = '"NaN"' }191 @{ TypeName = 'float'; Value = [float]::PositiveInfinity; Expected = '"Infinity"' }192 @{ TypeName = 'float'; Value = [float]::NegativeInfinity; Expected = '"-Infinity"' }193 @{ TypeName = 'double'; Value = 3.14159; Expected = '3.14159' }194 @{ TypeName = 'double'; Value = -3.14159; Expected = '-3.14159' }195 @{ TypeName = 'double'; Value = 0.0; Expected = '0.0' }196 @{ TypeName = 'double'; Value = [double]::NaN; Expected = '"NaN"' }197 @{ TypeName = 'double'; Value = [double]::PositiveInfinity; Expected = '"Infinity"' }198 @{ TypeName = 'double'; Value = [double]::NegativeInfinity; Expected = '"-Infinity"' }199 @{ TypeName = 'decimal'; Value = 123.456d; Expected = '123.456' }200 # BigInteger201 @{ TypeName = 'BigInteger'; Value = 18446744073709551615n; Expected = '18446744073709551615' }202 # Boolean203 @{ TypeName = 'bool'; Value = $true; Expected = 'true' }204 @{ TypeName = 'bool'; Value = $false; Expected = 'false' }205 # Null206 @{ TypeName = 'null'; Value = $null; Expected = 'null' }207 ) {208 param($TypeName, $Value, $Expected)209 $jsonPipeline = $Value | ConvertTo-Json -Compress210 $jsonInputObject = ConvertTo-Json -InputObject $Value -Compress211 $jsonPipeline | Should -BeExactly $Expected212 $jsonInputObject | Should -BeExactly $Expected213 }214 215 It 'Should include ETS properties on <TypeName>' -TestCases @(216 @{ TypeName = 'int'; Value = 42; Expected = '{"value":42,"MyProp":"test"}' }217 @{ TypeName = 'double'; Value = 3.14; Expected = '{"value":3.14,"MyProp":"test"}' }218 ) {219 param($TypeName, $Value, $Expected)220 $valueWithEts = Add-Member -InputObject $Value -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru221 $json = $valueWithEts | ConvertTo-Json -Compress222 $json | Should -BeExactly $Expected223 }224 }225 226 Context 'String scalar types' {227 It 'Should serialize string <Description> correctly via Pipeline and InputObject' -TestCases @(228 @{ Description = 'regular'; Value = 'hello'; Expected = '"hello"' }229 @{ Description = 'empty'; Value = ''; Expected = '""' }230 @{ Description = 'with spaces'; Value = 'hello world'; Expected = '"hello world"' }231 @{ Description = 'with newline'; Value = "line1`nline2"; Expected = '"line1\nline2"' }232 @{ Description = 'with tab'; Value = "col1`tcol2"; Expected = '"col1\tcol2"' }233 @{ Description = 'with quotes'; Value = 'say "hello"'; Expected = '"say \"hello\""' }234 @{ Description = 'with backslash'; Value = 'c:\path'; Expected = '"c:\\path"' }235 @{ Description = 'unicode'; Value = '???'; Expected = '"???"' }236 @{ Description = 'emoji'; Value = '??'; Expected = '"??"' }237 ) {238 param($Description, $Value, $Expected)239 $jsonPipeline = $Value | ConvertTo-Json -Compress240 $jsonInputObject = ConvertTo-Json -InputObject $Value -Compress241 $jsonPipeline | Should -BeExactly $Expected242 $jsonInputObject | Should -BeExactly $Expected243 }244 245 It 'Should ignore ETS properties on string' {246 $str = Add-Member -InputObject 'hello' -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru247 $json = $str | ConvertTo-Json -Compress248 $json | Should -BeExactly '"hello"'249 }250 }251 252 Context 'DateTime and related types' {253 It 'Should serialize DateTime with UTC kind via Pipeline and InputObject' {254 $dt = [DateTime]::new(2024, 6, 15, 10, 30, 0, [DateTimeKind]::Utc)255 $jsonPipeline = $dt | ConvertTo-Json -Compress256 $jsonInputObject = ConvertTo-Json -InputObject $dt -Compress257 $jsonPipeline | Should -BeExactly '"2024-06-15T10:30:00Z"'258 $jsonInputObject | Should -BeExactly '"2024-06-15T10:30:00Z"'259 }260 261 It 'Should serialize DateTime with Local kind' {262 $dt = [DateTime]::new(2024, 6, 15, 10, 30, 0, [DateTimeKind]::Local)263 $json = $dt | ConvertTo-Json -Compress264 $offset = $dt.ToString('zzz')265 $expected = '"2024-06-15T10:30:00' + $offset + '"'266 $json | Should -BeExactly $expected267 }268 269 It 'Should serialize DateTime with Unspecified kind via Pipeline and InputObject' {270 $dt = [DateTime]::new(2024, 6, 15, 10, 30, 0, [DateTimeKind]::Unspecified)271 $jsonPipeline = $dt | ConvertTo-Json -Compress272 $jsonInputObject = ConvertTo-Json -InputObject $dt -Compress273 $jsonPipeline | Should -BeExactly '"2024-06-15T10:30:00"'274 $jsonInputObject | Should -BeExactly '"2024-06-15T10:30:00"'275 }276 277 It 'Should serialize DateTimeOffset correctly via Pipeline and InputObject' {278 $dto = [DateTimeOffset]::new(2024, 6, 15, 10, 30, 0, [TimeSpan]::FromHours(9))279 $jsonPipeline = $dto | ConvertTo-Json -Compress280 $jsonInputObject = ConvertTo-Json -InputObject $dto -Compress281 $jsonPipeline | Should -BeExactly '"2024-06-15T10:30:00+09:00"'282 $jsonInputObject | Should -BeExactly '"2024-06-15T10:30:00+09:00"'283 }284 285 It 'Should serialize DateOnly as object with properties' {286 $d = [DateOnly]::new(2024, 6, 15)287 $json = $d | ConvertTo-Json -Compress288 $json | Should -BeExactly '{"Year":2024,"Month":6,"Day":15,"DayOfWeek":6,"DayOfYear":167,"DayNumber":739051}'289 }290 291 It 'Should serialize TimeOnly as object with properties' {292 $t = [TimeOnly]::new(10, 30, 45)293 $json = $t | ConvertTo-Json -Compress294 $json | Should -BeExactly '{"Hour":10,"Minute":30,"Second":45,"Millisecond":0,"Microsecond":0,"Nanosecond":0,"Ticks":378450000000}'295 }296 297 It 'Should serialize TimeSpan as object with properties' {298 $ts = [TimeSpan]::new(1, 2, 3, 4, 5)299 $json = $ts | ConvertTo-Json -Compress300 $json | Should -BeExactly '{"Ticks":937840050000,"Days":1,"Hours":2,"Milliseconds":5,"Microseconds":0,"Nanoseconds":0,"Minutes":3,"Seconds":4,"TotalDays":1.0854630208333333,"TotalHours":26.0511125,"TotalMilliseconds":93784005.0,"TotalMicroseconds":93784005000.0,"TotalNanoseconds":93784005000000.0,"TotalMinutes":1563.06675,"TotalSeconds":93784.005}'301 }302 303 It 'Should ignore ETS properties on DateTime' {304 $dt = [DateTime]::new(2024, 6, 15, 0, 0, 0, [DateTimeKind]::Utc)305 $dt = Add-Member -InputObject $dt -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru306 $json = $dt | ConvertTo-Json -Compress307 $json | Should -BeExactly '"2024-06-15T00:00:00Z"'308 }309 310 It 'Should include ETS properties on DateTimeOffset' {311 $dto = [DateTimeOffset]::new(2024, 6, 15, 10, 30, 0, [TimeSpan]::Zero)312 $dto = Add-Member -InputObject $dto -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru313 $json = $dto | ConvertTo-Json -Compress314 $json | Should -BeExactly '{"value":"2024-06-15T10:30:00+00:00","MyProp":"test"}'315 }316 317 It 'Should include ETS properties on DateOnly' {318 $d = [DateOnly]::new(2024, 6, 15)319 $d = Add-Member -InputObject $d -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru320 $json = $d | ConvertTo-Json -Compress321 $json | Should -BeExactly '{"Year":2024,"Month":6,"Day":15,"DayOfWeek":6,"DayOfYear":167,"DayNumber":739051,"MyProp":"test"}'322 }323 324 It 'Should include ETS properties on TimeOnly' {325 $t = [TimeOnly]::new(10, 30, 45)326 $t = Add-Member -InputObject $t -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru327 $json = $t | ConvertTo-Json -Compress328 $json | Should -BeExactly '{"Hour":10,"Minute":30,"Second":45,"Millisecond":0,"Microsecond":0,"Nanosecond":0,"Ticks":378450000000,"MyProp":"test"}'329 }330 }331 332 Context 'Guid type' {333 It 'Should serialize Guid as string via InputObject' {334 $guid = [Guid]::new('12345678-1234-1234-1234-123456789abc')335 $json = ConvertTo-Json -InputObject $guid -Compress336 $json | Should -BeExactly '"12345678-1234-1234-1234-123456789abc"'337 }338 339 It 'Should serialize Guid with Extended properties via Pipeline' {340 $guid = [Guid]::new('12345678-1234-1234-1234-123456789abc')341 $json = $guid | ConvertTo-Json -Compress342 $json | Should -BeExactly '{"value":"12345678-1234-1234-1234-123456789abc","Guid":"12345678-1234-1234-1234-123456789abc"}'343 }344 345 It 'Should serialize empty Guid correctly via InputObject' {346 $json = ConvertTo-Json -InputObject ([Guid]::Empty) -Compress347 $json | Should -BeExactly '"00000000-0000-0000-0000-000000000000"'348 }349 350 It 'Should include ETS properties on Guid via Pipeline' {351 $guid = [Guid]::new('12345678-1234-1234-1234-123456789abc')352 $guid = Add-Member -InputObject $guid -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru353 $json = $guid | ConvertTo-Json -Compress354 $json | Should -BeExactly '{"value":"12345678-1234-1234-1234-123456789abc","MyProp":"test","Guid":"12345678-1234-1234-1234-123456789abc"}'355 }356 }357 358 Context 'Uri type' {359 It 'Should serialize Uri <Description> correctly via Pipeline and InputObject' -TestCases @(360 @{ Description = 'http'; UriString = 'http://example.com'; Expected = '"http://example.com"' }361 @{ Description = 'https with path'; UriString = 'https://example.com/path'; Expected = '"https://example.com/path"' }362 @{ Description = 'with query'; UriString = 'https://example.com/search?q=test'; Expected = '"https://example.com/search?q=test"' }363 @{ Description = 'file'; UriString = 'file:///c:/temp/file.txt'; Expected = '"file:///c:/temp/file.txt"' }364 ) {365 param($Description, $UriString, $Expected)366 $uri = [Uri]$UriString367 $jsonPipeline = $uri | ConvertTo-Json -Compress368 $jsonInputObject = ConvertTo-Json -InputObject $uri -Compress369 $jsonPipeline | Should -BeExactly $Expected370 $jsonInputObject | Should -BeExactly $Expected371 }372 373 It 'Should include ETS properties on Uri' {374 $uri = [Uri]'https://example.com'375 $uri = Add-Member -InputObject $uri -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru376 $json = $uri | ConvertTo-Json -Compress377 $json | Should -BeExactly '{"value":"https://example.com","MyProp":"test"}'378 }379 }380 381 Context 'Enum types' {382 It 'Should serialize enum <EnumType>::<Value> as <Expected> via Pipeline and InputObject' -TestCases @(383 @{ EnumType = 'System.DayOfWeek'; Value = 'Sunday'; Expected = '0' }384 @{ EnumType = 'System.DayOfWeek'; Value = 'Monday'; Expected = '1' }385 @{ EnumType = 'System.DayOfWeek'; Value = 'Saturday'; Expected = '6' }386 @{ EnumType = 'System.ConsoleColor'; Value = 'Red'; Expected = '12' }387 @{ EnumType = 'System.IO.FileAttributes'; Value = 'ReadOnly'; Expected = '1' }388 @{ EnumType = 'System.IO.FileAttributes'; Value = 'Hidden'; Expected = '2' }389 ) {390 param($EnumType, $Value, $Expected)391 $enumValue = [Enum]::Parse($EnumType, $Value)392 $jsonPipeline = $enumValue | ConvertTo-Json -Compress393 $jsonInputObject = ConvertTo-Json -InputObject $enumValue -Compress394 $jsonPipeline | Should -BeExactly $Expected395 $jsonInputObject | Should -BeExactly $Expected396 }397 398 It 'Should serialize enum as "<Expected>" with -EnumsAsStrings' -TestCases @(399 @{ EnumType = 'System.DayOfWeek'; Value = 'Sunday'; Expected = 'Sunday' }400 @{ EnumType = 'System.DayOfWeek'; Value = 'Monday'; Expected = 'Monday' }401 @{ EnumType = 'System.ConsoleColor'; Value = 'Red'; Expected = 'Red' }402 ) {403 param($EnumType, $Value, $Expected)404 $enumValue = [Enum]::Parse($EnumType, $Value)405 $json = $enumValue | ConvertTo-Json -Compress -EnumsAsStrings406 $json | Should -BeExactly "`"$Expected`""407 }408 409 It 'Should serialize flags enum correctly via Pipeline and InputObject' {410 $flags = [System.IO.FileAttributes]::ReadOnly -bor [System.IO.FileAttributes]::Hidden411 $jsonPipeline = $flags | ConvertTo-Json -Compress412 $jsonInputObject = ConvertTo-Json -InputObject $flags -Compress413 $jsonPipeline | Should -BeExactly '3'414 $jsonInputObject | Should -BeExactly '3'415 }416 417 It 'Should serialize flags enum as string with -EnumsAsStrings' {418 $flags = [System.IO.FileAttributes]::ReadOnly -bor [System.IO.FileAttributes]::Hidden419 $json = $flags | ConvertTo-Json -Compress -EnumsAsStrings420 $json | Should -BeExactly '"ReadOnly, Hidden"'421 }422 423 It 'Should include ETS properties on Enum' {424 $enum = Add-Member -InputObject ([DayOfWeek]::Monday) -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru425 $json = $enum | ConvertTo-Json -Compress426 $json | Should -BeExactly '{"value":1,"MyProp":"test"}'427 }428 }429 430 Context 'IPAddress type' {431 It 'Should serialize IPAddress v4 correctly via InputObject' {432 $ip = [System.Net.IPAddress]::Parse('192.168.1.1')433 $json = ConvertTo-Json -InputObject $ip -Compress434 $json | Should -BeExactly '{"AddressFamily":2,"ScopeId":null,"IsIPv6Multicast":false,"IsIPv6LinkLocal":false,"IsIPv6SiteLocal":false,"IsIPv6Teredo":false,"IsIPv6UniqueLocal":false,"IsIPv4MappedToIPv6":false,"Address":16885952}'435 }436 437 It 'Should serialize IPAddress v4 correctly via Pipeline' {438 $ip = [System.Net.IPAddress]::Parse('192.168.1.1')439 $json = $ip | ConvertTo-Json -Compress440 $json | Should -BeExactly '{"AddressFamily":2,"ScopeId":null,"IsIPv6Multicast":false,"IsIPv6LinkLocal":false,"IsIPv6SiteLocal":false,"IsIPv6Teredo":false,"IsIPv6UniqueLocal":false,"IsIPv4MappedToIPv6":false,"Address":16885952,"IPAddressToString":"192.168.1.1"}'441 }442 443 It 'Should serialize IPAddress v6 correctly via InputObject' {444 $ip = [System.Net.IPAddress]::Parse('::1')445 $json = ConvertTo-Json -InputObject $ip -Compress446 $json | Should -BeExactly '{"AddressFamily":23,"ScopeId":0,"IsIPv6Multicast":false,"IsIPv6LinkLocal":false,"IsIPv6SiteLocal":false,"IsIPv6Teredo":false,"IsIPv6UniqueLocal":false,"IsIPv4MappedToIPv6":false,"Address":null}'447 }448 449 It 'Should serialize IPAddress v6 correctly via Pipeline' {450 $ip = [System.Net.IPAddress]::Parse('::1')451 $json = $ip | ConvertTo-Json -Compress452 $json | Should -BeExactly '{"AddressFamily":23,"ScopeId":0,"IsIPv6Multicast":false,"IsIPv6LinkLocal":false,"IsIPv6SiteLocal":false,"IsIPv6Teredo":false,"IsIPv6UniqueLocal":false,"IsIPv4MappedToIPv6":false,"Address":null,"IPAddressToString":"::1"}'453 }454 455 It 'Should include ETS properties on IPAddress' {456 $ip = [System.Net.IPAddress]::Parse('192.168.1.1')457 $ip = Add-Member -InputObject $ip -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru458 $json = $ip | ConvertTo-Json -Compress459 $json | Should -BeExactly '{"AddressFamily":2,"ScopeId":null,"IsIPv6Multicast":false,"IsIPv6LinkLocal":false,"IsIPv6SiteLocal":false,"IsIPv6Teredo":false,"IsIPv6UniqueLocal":false,"IsIPv4MappedToIPv6":false,"Address":16885952,"MyProp":"test","IPAddressToString":"192.168.1.1"}'460 }461 }462 463 Context 'Scalars as elements of arrays' {464 It 'Should serialize array of <TypeName> correctly via Pipeline and InputObject' -TestCases @(465 @{ TypeName = 'int'; Values = @(1, 2, 3); Expected = '[1,2,3]' }466 @{ TypeName = 'string'; Values = @('a', 'b', 'c'); Expected = '["a","b","c"]' }467 @{ TypeName = 'double'; Values = @(1.1, 2.2, 3.3); Expected = '[1.1,2.2,3.3]' }468 ) {469 param($TypeName, $Values, $Expected)470 $jsonPipeline = $Values | ConvertTo-Json -Compress471 $jsonInputObject = ConvertTo-Json -InputObject $Values -Compress472 $jsonPipeline | Should -BeExactly $Expected473 $jsonInputObject | Should -BeExactly $Expected474 }475 476 # Note: bool array test uses InputObject only because $true/$false are singletons477 # and ETS properties added in other tests would affect Pipeline serialization478 It 'Should serialize array of bool correctly via InputObject' {479 $bools = @($true, $false, $true)480 $json = ConvertTo-Json -InputObject $bools -Compress481 $json | Should -BeExactly '[true,false,true]'482 }483 484 It 'Should serialize array of Guid with Extended properties via Pipeline' {485 $guids = @(486 [Guid]'11111111-1111-1111-1111-111111111111',487 [Guid]'22222222-2222-2222-2222-222222222222'488 )489 $json = $guids | ConvertTo-Json -Compress490 $json | Should -BeExactly '[{"value":"11111111-1111-1111-1111-111111111111","Guid":"11111111-1111-1111-1111-111111111111"},{"value":"22222222-2222-2222-2222-222222222222","Guid":"22222222-2222-2222-2222-222222222222"}]'491 }492 493 It 'Should serialize array of enum correctly via Pipeline and InputObject' {494 $enums = @([DayOfWeek]::Monday, [DayOfWeek]::Wednesday, [DayOfWeek]::Friday)495 $jsonPipeline = $enums | ConvertTo-Json -Compress496 $jsonInputObject = ConvertTo-Json -InputObject $enums -Compress497 $jsonPipeline | Should -BeExactly '[1,3,5]'498 $jsonInputObject | Should -BeExactly '[1,3,5]'499 }500 501 It 'Should serialize array of enum as strings with -EnumsAsStrings' {502 $enums = @([DayOfWeek]::Monday, [DayOfWeek]::Wednesday, [DayOfWeek]::Friday)503 $json = $enums | ConvertTo-Json -Compress -EnumsAsStrings504 $json | Should -BeExactly '["Monday","Wednesday","Friday"]'505 }506 507 # Note: mixed array test uses InputObject only due to $true singleton issue508 It 'Should serialize mixed type array correctly via InputObject' {509 $mixed = @(1, 'two', $true, 3.14)510 $json = ConvertTo-Json -InputObject $mixed -Compress511 $json | Should -BeExactly '[1,"two",true,3.14]'512 }513 514 It 'Should serialize array with null elements correctly' {515 $arr = @(1, $null, 'three')516 $json = $arr | ConvertTo-Json -Compress517 $json | Should -BeExactly '[1,null,"three"]'518 }519 520 It 'Should include ETS properties on array via InputObject' {521 $arr = @(1, 2, 3)522 $arr = Add-Member -InputObject $arr -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru523 $json = ConvertTo-Json -InputObject $arr -Compress524 $json | Should -BeExactly '{"value":[1,2,3],"MyProp":"test"}'525 }526 }527 528 Context 'Scalars as values in hashtables and PSCustomObject' {529 It 'Should serialize hashtable with scalar values correctly via Pipeline and InputObject' {530 $hash = [ordered]@{531 intVal = 42532 strVal = 'hello'533 boolVal = $true534 doubleVal = 3.14535 nullVal = $null536 }537 $expected = '{"intVal":42,"strVal":"hello","boolVal":true,"doubleVal":3.14,"nullVal":null}'538 $jsonPipeline = $hash | ConvertTo-Json -Compress539 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress540 $jsonPipeline | Should -BeExactly $expected541 $jsonInputObject | Should -BeExactly $expected542 }543 544 It 'Should serialize PSCustomObject with scalar values correctly via Pipeline and InputObject' {545 $obj = [PSCustomObject]@{546 intVal = 42547 strVal = 'hello'548 boolVal = $true549 doubleVal = 3.14550 }551 $expected = '{"intVal":42,"strVal":"hello","boolVal":true,"doubleVal":3.14}'552 $jsonPipeline = $obj | ConvertTo-Json -Compress553 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress554 $jsonPipeline | Should -BeExactly $expected555 $jsonInputObject | Should -BeExactly $expected556 }557 558 It 'Should serialize hashtable with <TypeName> value correctly' -TestCases @(559 @{ TypeName = 'DateTime'; Value = [DateTime]::new(2024, 6, 15, 10, 30, 0, [DateTimeKind]::Utc); Expected = '{"val":"2024-06-15T10:30:00Z"}' }560 @{ TypeName = 'Guid'; Value = [Guid]'12345678-1234-1234-1234-123456789abc'; Expected = '{"val":"12345678-1234-1234-1234-123456789abc"}' }561 @{ TypeName = 'Enum'; Value = [DayOfWeek]::Monday; Expected = '{"val":1}' }562 @{ TypeName = 'Uri'; Value = [Uri]'https://example.com'; Expected = '{"val":"https://example.com"}' }563 @{ TypeName = 'BigInteger'; Value = 18446744073709551615n; Expected = '{"val":18446744073709551615}' }564 ) {565 param($TypeName, $Value, $Expected)566 $hash = @{ val = $Value }567 $jsonPipeline = $hash | ConvertTo-Json -Compress568 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress569 $jsonPipeline | Should -BeExactly $Expected570 $jsonInputObject | Should -BeExactly $Expected571 }572 573 It 'Should serialize hashtable with enum as string correctly' {574 $hash = @{ day = [DayOfWeek]::Monday }575 $json = $hash | ConvertTo-Json -Compress -EnumsAsStrings576 $json | Should -BeExactly '{"day":"Monday"}'577 }578 579 It 'Should include ETS properties on hashtable via InputObject' {580 $hash = @{ a = 1 }581 $hash = Add-Member -InputObject $hash -MemberType NoteProperty -Name MyProp -Value 'test' -PassThru582 $json = ConvertTo-Json -InputObject $hash -Compress583 $json | Should -BeExactly '{"a":1,"MyProp":"test"}'584 }585 }586 587 #endregion Comprehensive Scalar Type Tests (Phase 1)588 589 #region Comprehensive Array and Dictionary Tests (Phase 2)590 # Test coverage for ConvertTo-Json array and dictionary serialization591 # Covers: Pipeline vs InputObject, ETS vs no ETS, nested structures592 593 Context 'Array basic serialization' {594 It 'Should serialize empty array correctly via Pipeline and InputObject' {595 $arr = @()596 $jsonPipeline = ,$arr | ConvertTo-Json -Compress597 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress598 $jsonPipeline | Should -BeExactly '[]'599 $jsonInputObject | Should -BeExactly '[]'600 }601 602 It 'Should serialize single element array correctly via Pipeline and InputObject' {603 $arr = @(42)604 $jsonPipeline = ,$arr | ConvertTo-Json -Compress605 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress606 $jsonPipeline | Should -BeExactly '[42]'607 $jsonInputObject | Should -BeExactly '[42]'608 }609 610 It 'Should serialize multi-element array correctly via Pipeline and InputObject' {611 $arr = @(1, 2, 3, 4, 5)612 $jsonPipeline = ,$arr | ConvertTo-Json -Compress613 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress614 $jsonPipeline | Should -BeExactly '[1,2,3,4,5]'615 $jsonInputObject | Should -BeExactly '[1,2,3,4,5]'616 }617 618 It 'Should serialize string array correctly via Pipeline and InputObject' {619 $arr = @('apple', 'banana', 'cherry')620 $jsonPipeline = ,$arr | ConvertTo-Json -Compress621 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress622 $jsonPipeline | Should -BeExactly '["apple","banana","cherry"]'623 $jsonInputObject | Should -BeExactly '["apple","banana","cherry"]'624 }625 626 It 'Should serialize typed array correctly via Pipeline and InputObject' {627 [int[]]$arr = @(10, 20, 30)628 $jsonPipeline = ,$arr | ConvertTo-Json -Compress629 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress630 $jsonPipeline | Should -BeExactly '[10,20,30]'631 $jsonInputObject | Should -BeExactly '[10,20,30]'632 }633 634 It 'Should serialize array with single null element correctly via Pipeline and InputObject' {635 $arr = @($null)636 $jsonPipeline = ,$arr | ConvertTo-Json -Compress637 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress638 $jsonPipeline | Should -BeExactly '[null]'639 $jsonInputObject | Should -BeExactly '[null]'640 }641 642 It 'Should serialize array with multiple null elements correctly via Pipeline and InputObject' {643 $arr = @($null, $null, $null)644 $jsonPipeline = ,$arr | ConvertTo-Json -Compress645 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress646 $jsonPipeline | Should -BeExactly '[null,null,null]'647 $jsonInputObject | Should -BeExactly '[null,null,null]'648 }649 }650 651 Context 'Nested arrays' {652 It 'Should serialize 2D array correctly via Pipeline and InputObject' {653 $arr = @(@(1, 2), @(3, 4))654 $jsonPipeline = ,$arr | ConvertTo-Json -Compress655 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress656 $jsonPipeline | Should -BeExactly '[[1,2],[3,4]]'657 $jsonInputObject | Should -BeExactly '[[1,2],[3,4]]'658 }659 660 It 'Should serialize 3D array correctly via Pipeline and InputObject' {661 $arr = @(@(@(1, 2), @(3, 4)), @(@(5, 6), @(7, 8)))662 $jsonPipeline = ,$arr | ConvertTo-Json -Compress663 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress664 $jsonPipeline | Should -BeExactly '[[[1,2],[3,4]],[[5,6],[7,8]]]'665 $jsonInputObject | Should -BeExactly '[[[1,2],[3,4]],[[5,6],[7,8]]]'666 }667 668 It 'Should serialize jagged array correctly via Pipeline and InputObject' {669 $arr = @(@(1), @(2, 3), @(4, 5, 6))670 $jsonPipeline = ,$arr | ConvertTo-Json -Compress671 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress672 $jsonPipeline | Should -BeExactly '[[1],[2,3],[4,5,6]]'673 $jsonInputObject | Should -BeExactly '[[1],[2,3],[4,5,6]]'674 }675 676 It 'Should serialize array containing empty arrays correctly via Pipeline and InputObject' {677 $arr = @(@(), @(1), @())678 $jsonPipeline = ,$arr | ConvertTo-Json -Compress679 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress680 $jsonPipeline | Should -BeExactly '[[],[1],[]]'681 $jsonInputObject | Should -BeExactly '[[],[1],[]]'682 }683 684 It 'Should serialize deeply nested array with Depth limit using ToString via Pipeline and InputObject' {685 $ip = [System.Net.IPAddress]::Parse('192.168.1.1')686 $arr = ,(,(,(,($ip))))687 $jsonPipeline = ,$arr | ConvertTo-Json -Compress -Depth 2688 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress -Depth 2689 $jsonPipeline | Should -BeExactly '[[["192.168.1.1"]]]'690 $jsonInputObject | Should -BeExactly '[[["192.168.1.1"]]]'691 }692 693 It 'Should serialize deeply nested array with sufficient Depth as full object via Pipeline and InputObject' {694 $ip = [System.Net.IPAddress]::Parse('192.168.1.1')695 $arr = ,(,(,(,($ip))))696 $expected = '[[[[{"AddressFamily":2,"ScopeId":null,"IsIPv6Multicast":false,"IsIPv6LinkLocal":false,"IsIPv6SiteLocal":false,"IsIPv6Teredo":false,"IsIPv6UniqueLocal":false,"IsIPv4MappedToIPv6":false,"Address":16885952}]]]]'697 $jsonPipeline = ,$arr | ConvertTo-Json -Compress -Depth 10698 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress -Depth 10699 $jsonPipeline | Should -BeExactly $expected700 $jsonInputObject | Should -BeExactly $expected701 }702 }703 704 Context 'Array with mixed content types' {705 It 'Should serialize array with mixed scalars correctly via Pipeline and InputObject' {706 $arr = @(1, 'two', 3.14, $true, $null)707 $jsonPipeline = ,$arr | ConvertTo-Json -Compress708 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress709 $jsonPipeline | Should -BeExactly '[1,"two",3.14,true,null]'710 $jsonInputObject | Should -BeExactly '[1,"two",3.14,true,null]'711 }712 713 It 'Should serialize array with nested array and scalars correctly via Pipeline and InputObject' {714 $arr = @(1, @(2, 3), 4)715 $jsonPipeline = ,$arr | ConvertTo-Json -Compress716 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress717 $jsonPipeline | Should -BeExactly '[1,[2,3],4]'718 $jsonInputObject | Should -BeExactly '[1,[2,3],4]'719 }720 721 It 'Should serialize array with PSCustomObject elements correctly via Pipeline and InputObject' {722 $arr = @([PSCustomObject]@{x = 1}, [PSCustomObject]@{y = 2})723 $jsonPipeline = ,$arr | ConvertTo-Json -Compress724 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress725 $jsonPipeline | Should -BeExactly '[{"x":1},{"y":2}]'726 $jsonInputObject | Should -BeExactly '[{"x":1},{"y":2}]'727 }728 729 It 'Should serialize array with DateTime elements correctly via Pipeline and InputObject' {730 $date1 = [DateTime]::new(2024, 6, 15, 10, 30, 0, [DateTimeKind]::Utc)731 $date2 = [DateTime]::new(2024, 12, 25, 0, 0, 0, [DateTimeKind]::Utc)732 $arr = @($date1, $date2)733 $expected = '["2024-06-15T10:30:00Z","2024-12-25T00:00:00Z"]'734 $jsonPipeline = ,$arr | ConvertTo-Json -Compress735 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress736 $jsonPipeline | Should -BeExactly $expected737 $jsonInputObject | Should -BeExactly $expected738 }739 740 It 'Should serialize array with Guid elements correctly via Pipeline and InputObject' {741 $guid1 = [Guid]'12345678-1234-1234-1234-123456789abc'742 $guid2 = [Guid]'87654321-4321-4321-4321-cba987654321'743 $arr = @($guid1, $guid2)744 $expected = '["12345678-1234-1234-1234-123456789abc","87654321-4321-4321-4321-cba987654321"]'745 $jsonPipeline = ,$arr | ConvertTo-Json -Compress746 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress747 $jsonPipeline | Should -BeExactly $expected748 $jsonInputObject | Should -BeExactly $expected749 }750 751 It 'Should serialize array with enum elements correctly via Pipeline and InputObject' {752 $arr = @([DayOfWeek]::Monday, [DayOfWeek]::Friday)753 $jsonPipeline = ,$arr | ConvertTo-Json -Compress754 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress755 $jsonPipeline | Should -BeExactly '[1,5]'756 $jsonInputObject | Should -BeExactly '[1,5]'757 }758 759 It 'Should serialize array with enum as string correctly via Pipeline and InputObject' {760 $arr = @([DayOfWeek]::Monday, [DayOfWeek]::Friday)761 $jsonPipeline = ,$arr | ConvertTo-Json -Compress -EnumsAsStrings762 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress -EnumsAsStrings763 $jsonPipeline | Should -BeExactly '["Monday","Friday"]'764 $jsonInputObject | Should -BeExactly '["Monday","Friday"]'765 }766 }767 768 Context 'Array ETS properties' {769 It 'Should include ETS properties on array via Pipeline and InputObject' {770 $arr = @(1, 2, 3)771 $arr = Add-Member -InputObject $arr -MemberType NoteProperty -Name ArrayName -Value 'MyArray' -PassThru772 $jsonPipeline = ,$arr | ConvertTo-Json -Compress773 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress774 $jsonPipeline | Should -BeExactly '{"value":[1,2,3],"ArrayName":"MyArray"}'775 $jsonInputObject | Should -BeExactly '{"value":[1,2,3],"ArrayName":"MyArray"}'776 }777 778 It 'Should include multiple ETS properties on array via Pipeline and InputObject' {779 $arr = @('a', 'b')780 $arr = Add-Member -InputObject $arr -MemberType NoteProperty -Name Prop1 -Value 'val1' -PassThru781 $arr = Add-Member -InputObject $arr -MemberType NoteProperty -Name Prop2 -Value 'val2' -PassThru782 $jsonPipeline = ,$arr | ConvertTo-Json -Compress783 $jsonInputObject = ConvertTo-Json -InputObject $arr -Compress784 $jsonPipeline | Should -BeExactly '{"value":["a","b"],"Prop1":"val1","Prop2":"val2"}'785 $jsonInputObject | Should -BeExactly '{"value":["a","b"],"Prop1":"val1","Prop2":"val2"}'786 }787 }788 789 Context 'Hashtable basic serialization' {790 It 'Should serialize empty hashtable correctly via Pipeline and InputObject' {791 $hash = @{}792 $jsonPipeline = $hash | ConvertTo-Json -Compress793 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress794 $jsonPipeline | Should -BeExactly '{}'795 $jsonInputObject | Should -BeExactly '{}'796 }797 798 It 'Should serialize single key hashtable correctly via Pipeline and InputObject' {799 $hash = @{ key = 'value' }800 $jsonPipeline = $hash | ConvertTo-Json -Compress801 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress802 $jsonPipeline | Should -BeExactly '{"key":"value"}'803 $jsonInputObject | Should -BeExactly '{"key":"value"}'804 }805 806 It 'Should serialize hashtable with null value correctly via Pipeline and InputObject' {807 $hash = @{ nullKey = $null }808 $jsonPipeline = $hash | ConvertTo-Json -Compress809 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress810 $jsonPipeline | Should -BeExactly '{"nullKey":null}'811 $jsonInputObject | Should -BeExactly '{"nullKey":null}'812 }813 814 It 'Should serialize hashtable with various scalar types correctly via Pipeline and InputObject' {815 $hash = [ordered]@{816 intKey = 42817 strKey = 'hello'818 boolKey = $true819 doubleKey = 3.14820 }821 $expected = '{"intKey":42,"strKey":"hello","boolKey":true,"doubleKey":3.14}'822 $jsonPipeline = $hash | ConvertTo-Json -Compress823 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress824 $jsonPipeline | Should -BeExactly $expected825 $jsonInputObject | Should -BeExactly $expected826 }827 }828 829 Context 'OrderedDictionary serialization' {830 It 'Should preserve order in OrderedDictionary via Pipeline and InputObject' {831 $ordered = [ordered]@{832 z = 1833 a = 2834 m = 3835 }836 $expected = '{"z":1,"a":2,"m":3}'837 $jsonPipeline = $ordered | ConvertTo-Json -Compress838 $jsonInputObject = ConvertTo-Json -InputObject $ordered -Compress839 $jsonPipeline | Should -BeExactly $expected840 $jsonInputObject | Should -BeExactly $expected841 }842 843 It 'Should serialize large OrderedDictionary preserving order via Pipeline and InputObject' {844 $ordered = [ordered]@{}845 1..5 | ForEach-Object { $ordered["key$_"] = $_ }846 $expected = '{"key1":1,"key2":2,"key3":3,"key4":4,"key5":5}'847 $jsonPipeline = $ordered | ConvertTo-Json -Compress848 $jsonInputObject = ConvertTo-Json -InputObject $ordered -Compress849 $jsonPipeline | Should -BeExactly $expected850 $jsonInputObject | Should -BeExactly $expected851 }852 }853 854 Context 'Nested dictionaries' {855 It 'Should serialize nested hashtable correctly via Pipeline and InputObject' {856 $hash = @{857 outer = @{858 inner = 'value'859 }860 }861 $jsonPipeline = $hash | ConvertTo-Json -Compress862 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress863 $jsonPipeline | Should -BeExactly '{"outer":{"inner":"value"}}'864 $jsonInputObject | Should -BeExactly '{"outer":{"inner":"value"}}'865 }866 867 It 'Should serialize deeply nested hashtable correctly via Pipeline and InputObject' {868 $hash = @{869 level1 = @{870 level2 = @{871 level3 = 'deep'872 }873 }874 }875 $jsonPipeline = $hash | ConvertTo-Json -Compress876 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress877 $jsonPipeline | Should -BeExactly '{"level1":{"level2":{"level3":"deep"}}}'878 $jsonInputObject | Should -BeExactly '{"level1":{"level2":{"level3":"deep"}}}'879 }880 881 It 'Should serialize nested hashtable with Depth limit via Pipeline and InputObject' {882 $hash = @{883 level1 = @{884 level2 = @{885 level3 = 'deep'886 }887 }888 }889 $jsonPipeline = $hash | ConvertTo-Json -Compress -Depth 1890 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress -Depth 1891 $jsonPipeline | Should -BeExactly '{"level1":{"level2":"System.Collections.Hashtable"}}'892 $jsonInputObject | Should -BeExactly '{"level1":{"level2":"System.Collections.Hashtable"}}'893 }894 895 It 'Should serialize hashtable with array value correctly via Pipeline and InputObject' {896 $hash = @{ arr = @(1, 2, 3) }897 $jsonPipeline = $hash | ConvertTo-Json -Compress898 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress899 $jsonPipeline | Should -BeExactly '{"arr":[1,2,3]}'900 $jsonInputObject | Should -BeExactly '{"arr":[1,2,3]}'901 }902 903 It 'Should serialize hashtable with nested array of hashtables correctly via Pipeline and InputObject' {904 $hash = @{905 items = @(906 @{ id = 1 },907 @{ id = 2 }908 )909 }910 $jsonPipeline = $hash | ConvertTo-Json -Compress911 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress912 $jsonPipeline | Should -BeExactly '{"items":[{"id":1},{"id":2}]}'913 $jsonInputObject | Should -BeExactly '{"items":[{"id":1},{"id":2}]}'914 }915 }916 917 Context 'Dictionary key types' {918 It 'Should serialize hashtable with string keys correctly via Pipeline and InputObject' {919 $hash = @{ 'string-key' = 'value' }920 $jsonPipeline = $hash | ConvertTo-Json -Compress921 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress922 $jsonPipeline | Should -BeExactly '{"string-key":"value"}'923 $jsonInputObject | Should -BeExactly '{"string-key":"value"}'924 }925 926 It 'Should serialize hashtable with special character keys correctly via Pipeline and InputObject' {927 $hash = [ordered]@{928 'key with space' = 1929 'key-with-dash' = 2930 'key_with_underscore' = 3931 }932 $expected = '{"key with space":1,"key-with-dash":2,"key_with_underscore":3}'933 $jsonPipeline = $hash | ConvertTo-Json -Compress934 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress935 $jsonPipeline | Should -BeExactly $expected936 $jsonInputObject | Should -BeExactly $expected937 }938 939 It 'Should serialize hashtable with unicode keys correctly via Pipeline and InputObject' {940 $hash = @{ "`u{65E5}`u{672C}`u{8A9E}" = 'Japanese' }941 $expected = "{`"`u{65E5}`u{672C}`u{8A9E}`":`"Japanese`"}"942 $jsonPipeline = $hash | ConvertTo-Json -Compress943 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress944 $jsonPipeline | Should -BeExactly $expected945 $jsonInputObject | Should -BeExactly $expected946 }947 948 It 'Should serialize hashtable with empty string key correctly via Pipeline and InputObject' {949 $hash = @{ '' = 'empty key' }950 $jsonPipeline = $hash | ConvertTo-Json -Compress951 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress952 $jsonPipeline | Should -BeExactly '{"":"empty key"}'953 $jsonInputObject | Should -BeExactly '{"":"empty key"}'954 }955 }956 957 Context 'Dictionary with complex values' {958 It 'Should serialize hashtable with DateTime value correctly via Pipeline and InputObject' {959 $hash = @{ date = [DateTime]::new(2024, 6, 15, 10, 30, 0, [DateTimeKind]::Utc) }960 $jsonPipeline = $hash | ConvertTo-Json -Compress961 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress962 $jsonPipeline | Should -BeExactly '{"date":"2024-06-15T10:30:00Z"}'963 $jsonInputObject | Should -BeExactly '{"date":"2024-06-15T10:30:00Z"}'964 }965 966 It 'Should serialize hashtable with Guid value correctly via Pipeline and InputObject' {967 $hash = @{ guid = [Guid]'12345678-1234-1234-1234-123456789abc' }968 $jsonPipeline = $hash | ConvertTo-Json -Compress969 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress970 $jsonPipeline | Should -BeExactly '{"guid":"12345678-1234-1234-1234-123456789abc"}'971 $jsonInputObject | Should -BeExactly '{"guid":"12345678-1234-1234-1234-123456789abc"}'972 }973 974 It 'Should serialize hashtable with enum value correctly via Pipeline and InputObject' {975 $hash = @{ day = [DayOfWeek]::Monday }976 $jsonPipeline = $hash | ConvertTo-Json -Compress977 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress978 $jsonPipeline | Should -BeExactly '{"day":1}'979 $jsonInputObject | Should -BeExactly '{"day":1}'980 }981 982 It 'Should serialize hashtable with enum as string correctly via Pipeline and InputObject' {983 $hash = @{ day = [DayOfWeek]::Monday }984 $jsonPipeline = $hash | ConvertTo-Json -Compress -EnumsAsStrings985 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress -EnumsAsStrings986 $jsonPipeline | Should -BeExactly '{"day":"Monday"}'987 $jsonInputObject | Should -BeExactly '{"day":"Monday"}'988 }989 990 It 'Should serialize hashtable with PSCustomObject value correctly via Pipeline and InputObject' {991 $hash = @{ obj = [PSCustomObject]@{ prop = 'value' } }992 $jsonPipeline = $hash | ConvertTo-Json -Compress993 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress994 $jsonPipeline | Should -BeExactly '{"obj":{"prop":"value"}}'995 $jsonInputObject | Should -BeExactly '{"obj":{"prop":"value"}}'996 }997 }998 999 Context 'Dictionary ETS properties' {1000 It 'Should include ETS properties on hashtable via Pipeline and InputObject' {1001 $hash = @{ a = 1 }1002 $hash = Add-Member -InputObject $hash -MemberType NoteProperty -Name ETSProp -Value 'ets' -PassThru1003 $jsonPipeline = $hash | ConvertTo-Json -Compress1004 $jsonInputObject = ConvertTo-Json -InputObject $hash -Compress1005 $jsonPipeline | Should -BeExactly '{"a":1,"ETSProp":"ets"}'1006 $jsonInputObject | Should -BeExactly '{"a":1,"ETSProp":"ets"}'1007 }1008 1009 It 'Should include ETS properties on OrderedDictionary via Pipeline and InputObject' {1010 $ordered = [ordered]@{ a = 1 }1011 $ordered = Add-Member -InputObject $ordered -MemberType NoteProperty -Name ETSProp -Value 'ets' -PassThru1012 $jsonPipeline = $ordered | ConvertTo-Json -Compress1013 $jsonInputObject = ConvertTo-Json -InputObject $ordered -Compress1014 $jsonPipeline | Should -BeExactly '{"a":1,"ETSProp":"ets"}'1015 $jsonInputObject | Should -BeExactly '{"a":1,"ETSProp":"ets"}'1016 }1017 }1018 1019 Context 'Generic Dictionary types' {1020 It 'Should serialize Generic Dictionary correctly via Pipeline and InputObject' {1021 $dict = [System.Collections.Generic.Dictionary[string,int]]::new()1022 $dict['one'] = 11023 $dict['two'] = 21024 $jsonPipeline = $dict | ConvertTo-Json -Compress1025 $jsonInputObject = ConvertTo-Json -InputObject $dict -Compress1026 $jsonPipeline | Should -Match '"one":1'1027 $jsonPipeline | Should -Match '"two":2'1028 $jsonInputObject | Should -Match '"one":1'1029 $jsonInputObject | Should -Match '"two":2'1030 }1031 1032 It 'Should serialize SortedDictionary correctly via Pipeline and InputObject' {1033 $dict = [System.Collections.Generic.SortedDictionary[string,int]]::new()1034 $dict['b'] = 21035 $dict['a'] = 11036 $dict['c'] = 31037 $jsonPipeline = $dict | ConvertTo-Json -Compress1038 $jsonInputObject = ConvertTo-Json -InputObject $dict -Compress1039 $jsonPipeline | Should -BeExactly '{"a":1,"b":2,"c":3}'1040 $jsonInputObject | Should -BeExactly '{"a":1,"b":2,"c":3}'1041 }1042 }1043 1044 #endregion Comprehensive Array and Dictionary Tests (Phase 2)1045 1046 1047 #region Comprehensive PSCustomObject Tests (Phase 3)1048 # Test coverage for ConvertTo-Json PSCustomObject serialization1049 # Covers: Pipeline vs InputObject, ETS vs no ETS, nested structures1050 1051 Context 'PSCustomObject basic serialization' {1052 It 'Should serialize PSCustomObject with single property via Pipeline and InputObject' {1053 $obj = [PSCustomObject]@{ Name = 'Test' }1054 $jsonPipeline = $obj | ConvertTo-Json -Compress1055 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1056 $jsonPipeline | Should -BeExactly '{"Name":"Test"}'1057 $jsonInputObject | Should -BeExactly '{"Name":"Test"}'1058 }1059 1060 It 'Should serialize PSCustomObject with multiple properties via Pipeline and InputObject' {1061 $obj = [PSCustomObject][ordered]@{1062 Name = 'Test'1063 Value = 421064 Active = $true1065 }1066 $expected = '{"Name":"Test","Value":42,"Active":true}'1067 $jsonPipeline = $obj | ConvertTo-Json -Compress1068 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1069 $jsonPipeline | Should -BeExactly $expected1070 $jsonInputObject | Should -BeExactly $expected1071 }1072 1073 It 'Should preserve property order in PSCustomObject via Pipeline and InputObject' {1074 $obj = [PSCustomObject][ordered]@{1075 Zebra = 11076 Alpha = 21077 Middle = 31078 }1079 $expected = '{"Zebra":1,"Alpha":2,"Middle":3}'1080 $jsonPipeline = $obj | ConvertTo-Json -Compress1081 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1082 $jsonPipeline | Should -BeExactly $expected1083 $jsonInputObject | Should -BeExactly $expected1084 }1085 1086 It 'Should serialize PSCustomObject with null property via Pipeline and InputObject' {1087 $obj = [PSCustomObject]@{ NullProp = $null }1088 $jsonPipeline = $obj | ConvertTo-Json -Compress1089 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1090 $jsonPipeline | Should -BeExactly '{"NullProp":null}'1091 $jsonInputObject | Should -BeExactly '{"NullProp":null}'1092 }1093 }1094 1095 Context 'PSCustomObject with various property types' {1096 It 'Should serialize PSCustomObject with scalar properties via Pipeline and InputObject' {1097 $obj = [PSCustomObject][ordered]@{1098 IntVal = 421099 DoubleVal = 3.141100 StringVal = 'hello'1101 BoolVal = $true1102 }1103 $expected = '{"IntVal":42,"DoubleVal":3.14,"StringVal":"hello","BoolVal":true}'1104 $jsonPipeline = $obj | ConvertTo-Json -Compress1105 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1106 $jsonPipeline | Should -BeExactly $expected1107 $jsonInputObject | Should -BeExactly $expected1108 }1109 1110 It 'Should serialize PSCustomObject with DateTime property via Pipeline and InputObject' {1111 $obj = [PSCustomObject]@{1112 Date = [DateTime]::new(2024, 6, 15, 10, 30, 0, [DateTimeKind]::Utc)1113 }1114 $expected = '{"Date":"2024-06-15T10:30:00Z"}'1115 $jsonPipeline = $obj | ConvertTo-Json -Compress1116 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1117 $jsonPipeline | Should -BeExactly $expected1118 $jsonInputObject | Should -BeExactly $expected1119 }1120 1121 It 'Should serialize PSCustomObject with Guid property via Pipeline and InputObject' {1122 $obj = [PSCustomObject]@{1123 Id = [Guid]'12345678-1234-1234-1234-123456789abc'1124 }1125 $expected = '{"Id":"12345678-1234-1234-1234-123456789abc"}'1126 $jsonPipeline = $obj | ConvertTo-Json -Compress1127 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1128 $jsonPipeline | Should -BeExactly $expected1129 $jsonInputObject | Should -BeExactly $expected1130 }1131 1132 It 'Should serialize PSCustomObject with enum property via Pipeline and InputObject' {1133 $obj = [PSCustomObject]@{ Day = [DayOfWeek]::Monday }1134 $jsonPipeline = $obj | ConvertTo-Json -Compress1135 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1136 $jsonPipeline | Should -BeExactly '{"Day":1}'1137 $jsonInputObject | Should -BeExactly '{"Day":1}'1138 }1139 1140 It 'Should serialize PSCustomObject with enum as string via Pipeline and InputObject' {1141 $obj = [PSCustomObject]@{ Day = [DayOfWeek]::Monday }1142 $jsonPipeline = $obj | ConvertTo-Json -Compress -EnumsAsStrings1143 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress -EnumsAsStrings1144 $jsonPipeline | Should -BeExactly '{"Day":"Monday"}'1145 $jsonInputObject | Should -BeExactly '{"Day":"Monday"}'1146 }1147 1148 It 'Should serialize PSCustomObject with array property via Pipeline and InputObject' {1149 $obj = [PSCustomObject]@{ Numbers = @(1, 2, 3) }1150 $expected = '{"Numbers":[1,2,3]}'1151 $jsonPipeline = $obj | ConvertTo-Json -Compress1152 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1153 $jsonPipeline | Should -BeExactly $expected1154 $jsonInputObject | Should -BeExactly $expected1155 }1156 1157 It 'Should serialize PSCustomObject with hashtable property via Pipeline and InputObject' {1158 $obj = [PSCustomObject]@{ Config = @{ Key = 'Value' } }1159 $expected = '{"Config":{"Key":"Value"}}'1160 $jsonPipeline = $obj | ConvertTo-Json -Compress1161 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1162 $jsonPipeline | Should -BeExactly $expected1163 $jsonInputObject | Should -BeExactly $expected1164 }1165 }1166 1167 Context 'Nested PSCustomObject' {1168 It 'Should serialize nested PSCustomObject via Pipeline and InputObject' {1169 $obj = [PSCustomObject]@{1170 Outer = [PSCustomObject]@{1171 Inner = 'value'1172 }1173 }1174 $expected = '{"Outer":{"Inner":"value"}}'1175 $jsonPipeline = $obj | ConvertTo-Json -Compress1176 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1177 $jsonPipeline | Should -BeExactly $expected1178 $jsonInputObject | Should -BeExactly $expected1179 }1180 1181 It 'Should serialize deeply nested PSCustomObject via Pipeline and InputObject' {1182 $obj = [PSCustomObject]@{1183 Level1 = [PSCustomObject]@{1184 Level2 = [PSCustomObject]@{1185 Level3 = 'deep'1186 }1187 }1188 }1189 $expected = '{"Level1":{"Level2":{"Level3":"deep"}}}'1190 $jsonPipeline = $obj | ConvertTo-Json -Compress1191 $jsonInputObject = ConvertTo-Json -InputObject $obj -Compress1192 $jsonPipeline | Should -BeExactly $expected1193 $jsonInputObject | Should -BeExactly $expected1194 }1195 1196 It 'Should serialize nested PSCustomObject with Depth limit via Pipeline and InputObject' {1197 $obj = [PSCustomObject]@{1198 Level1 = [PSCustomObject]@{1199 Level2 = [PSCustomObject]@{1200 Level3 = 'deep'