MegaBites-AI/Windows-powershell
0372
1# Copyright (c) Microsoft Corporation.2# Licensed under the MIT License.3 4function New-NestedJson {5 Param(6 [ValidateRange(1, 2048)]7 [int]8 $Depth9 )10 11 $nestedJson = "true"12 13 $Depth..1 | ForEach-Object {14 $nestedJson = '{"' + $_ + '":' + $nestedJson + '}'15 }16 17 return $nestedJson18}19 20function Count-ObjectDepth {21 Param([PSCustomObject] $InputObject)22 23 for ($i=1; $i -le 2048; $i++)24 {25 $InputObject = Select-Object -InputObject $InputObject -ExpandProperty $i26 if ($InputObject -eq $true)27 {28 return $i29 }30 }31}32 33Describe 'ConvertFrom-Json Unit Tests' -tags "CI" {34 35 BeforeAll {36 $testCasesWithAndWithoutAsHashtableSwitch = @(37 @{ AsHashtable = $true }38 @{ AsHashtable = $false }39 )40 $testCasesWithAndWithoutNoEnumerateSwitch = @(41 @{ NoEnumerate = $true }42 @{ NoEnumerate = $false }43 )44 }45 46 It 'Can convert a single-line object with AsHashtable switch set to <AsHashtable>' -TestCases $testCasesWithAndWithoutAsHashtableSwitch {47 Param($AsHashtable)48 ('{"a" : "1"}' | ConvertFrom-Json -AsHashtable:$AsHashtable).a | Should -Be 149 }50 51 It 'Can convert one string-per-object with AsHashtable switch set to <AsHashtable>' -TestCases $testCasesWithAndWithoutAsHashtableSwitch {52 Param($AsHashtable)53 $json = @('{"a" : "1"}', '{"a" : "x"}') | ConvertFrom-Json -AsHashtable:$AsHashtable54 $json.Count | Should -Be 255 $json[1].a | Should -Be 'x'56 if ($AsHashtable)57 {58 $json | Should -BeOfType Hashtable59 }60 }61 62 It 'Throws an ArgumentException with an incomplete array with AsHashtable switch set to <AsHashtable>' -TestCase $testCasesWithAndWithoutAsHashtableSwitch {63 Param($AsHashtable)64 { ConvertFrom-Json '["1",' -AsHashtable:$AsHashtable } |65 Should -Throw -ErrorId "System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand"66 { ConvertFrom-Json '[' -AsHashtable:$AsHashtable } |67 Should -Throw -ErrorId "System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand"68 }69 70 It 'Can convert multi-line object with AsHashtable switch set to <AsHashtable>' -TestCases $testCasesWithAndWithoutAsHashtableSwitch {71 Param($AsHashtable)72 $json = @('{"a" :', '"x"}') | ConvertFrom-Json -AsHashtable:$AsHashtable73 $json.a | Should -Be 'x'74 if ($AsHashtable)75 {76 $json | Should -BeOfType Hashtable77 }78 }79 80 It 'Can convert an object with Newtonsoft.Json metadata properties with AsHashtable switch set to <AsHashtable>' -TestCases $testCasesWithAndWithoutAsHashtableSwitch {81 Param($AsHashtable)82 $id = 1383 $type = 'Calendar.Months.December'84 $ref = 198985 86 $json = '{"$id":' + $id + ', "$type":"' + $type + '", "$ref":' + $ref + '}' | ConvertFrom-Json -AsHashtable:$AsHashtable87 88 $json.'$id' | Should -Be $id89 $json.'$type' | Should -Be $type90 $json.'$ref' | Should -Be $ref91 92 if ($AsHashtable)93 {94 $json | Should -BeOfType Hashtable95 }96 }97 98 It 'Can convert an object of depth 1024 by default with AsHashtable switch set to <AsHashtable>' -TestCases $testCasesWithAndWithoutAsHashtableSwitch {99 Param($AsHashtable)100 $nestedJson = New-NestedJson -Depth 1024101 102 $json = $nestedJson | ConvertFrom-Json -AsHashtable:$AsHashtable103 104 if ($AsHashtable)105 {106 $json | Should -BeOfType Hashtable107 }108 else109 {110 $json | Should -BeOfType PSCustomObject111 }112 }113 114 It 'Fails to convert an object of depth higher than 1024 by default with AsHashtable switch set to <AsHashtable>' -TestCases $testCasesWithAndWithoutAsHashtableSwitch {115 Param($AsHashtable)116 $nestedJson = New-NestedJson -Depth 1025117 118 { $nestedJson | ConvertFrom-Json -AsHashtable:$AsHashtable } |119 Should -Throw -ErrorId "System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand"120 }121 122 It 'Can correctly round trip arrays with NoEnumerate switch set to <NoEnumerate>' -TestCases $testCasesWithAndWithoutNoEnumerateSwitch {123 Param($NoEnumerate)124 '[ 1, 2 ]' | ConvertFrom-Json -NoEnumerate:$NoEnumerate | ConvertTo-Json -Compress | Should -Be '[1,2]'125 }126 127 It 'Unravels array elements when NoEnumerate switch is not set' {128 ('[ 1, 2 ]' | ConvertFrom-Json | Measure-Object).Count | Should -Be 2129 }130 131 It 'Sends a Json array as a single element when NoEnumerate switch is set' {132 ('[ 1, 2 ]' | ConvertFrom-Json -NoEnumerate | Measure-Object).Count | Should -Be 1133 }134 135 It 'Cannot round trip single element arrays without NoEnumerate switch' {136 '[ 1 ]' | ConvertFrom-Json | ConvertTo-Json | Should -Be 1137 }138 139 It 'Can round trip single element arrays with NoEnumerate switch' {140 '[ 1 ]' | ConvertFrom-Json -NoEnumerate | ConvertTo-Json -Compress | Should -Be '[1]'141 }142 143 It 'Can convert null' {144 'null' | ConvertFrom-Json | Should -Be $null145 $out = '[1, null, 2]' | ConvertFrom-Json146 $out.Length | Should -Be 3147 148 # can't compare directly to array as Pester doesn't handle the $null149 $out[0] | Should -Be 1150 $out[1] | Should -Be $null151 $out[2] | Should -Be 2152 }153 154 It 'Order is preserved for a hashtable' {155 $json = '{"a":1,"b":2,"c":3}' | ConvertFrom-Json -AsHashtable156 ($json | Out-String).Trim() | Should -BeExactly @"157Name Value158---- -----159a 1160b 2161c 3162"@163 }164 165 It 'Parses DateKind Default strings for <Value>' -TestCases @(166 @{167 Value = '"2022-11-02T12:01:44.5801388+04:00"'168 Expected = ([DateTimeOffset]::new(2022, 11, 2, 12, 1, 44, 580, 138, (New-TimeSpan -Hours 4)).AddTicks(8).LocalDateTime)169 }170 @{171 Value = '"2022-11-02T12:01:44.5801388-04:00"'172 Expected = ([DateTimeOffset]::new(2022, 11, 2, 12, 1, 44, 580, 138, (New-TimeSpan -Hours -4)).AddTicks(8).LocalDateTime)173 }174 @{175 Value = '"1970-01-01T00:00:00"'176 Expected = ([DateTime]::new(1970, 1, 1, 0, 0, 0, 0, 0, 'Unspecified'))177 }178 @{179 Value = '"1970-01-01T00:00:00.0000000Z"'180 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)).UtcDateTime)181 }182 @{183 Value = '"1970-01-01T00:00:00.0000000+00:00"'184 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)).LocalDateTime)185 }186 @{187 Value = '"1970-01-01T00:00:00.0000000-00:00"'188 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)).LocalDateTime)189 }190 @{191 Value = '"1970-01-01T00:00:00.0000000+10:00"'192 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 10)).LocalDateTime)193 }194 @{195 Value = '"1970-01-01T00:00:00.0000000-10:00"'196 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours -10)).LocalDateTime)197 }198 ) {199 param ($Value, $Expected)200 201 $json = $Value | ConvertFrom-Json202 $json | Should -BeOfType ([DateTime])203 $json.Kind | Should -Be $Expected.Kind204 $json | Should -Be $Expected205 206 $json = $Value | ConvertFrom-Json -DateKind Default207 $json | Should -BeOfType ([DateTime])208 $json.Kind | Should -Be $Expected.Kind209 $json | Should -Be $Expected210 }211 212 It 'Parses DateKind Local strings for <Value>' -TestCases @(213 @{214 Value = '"2022-11-02T12:01:44.5801388+04:00"'215 Expected = ([DateTimeOffset]::new(2022, 11, 2, 12, 1, 44, 580, 138, (New-TimeSpan -Hours 4)).AddTicks(8).LocalDateTime)216 }217 @{218 Value = '"2022-11-02T12:01:44.5801388-04:00"'219 Expected = ([DateTimeOffset]::new(2022, 11, 2, 12, 1, 44, 580, 138, (New-TimeSpan -Hours -4)).AddTicks(8).LocalDateTime)220 }221 @{222 Value = '"1970-01-01T00:00:00"'223 Expected = ([DateTime]::new(1970, 1, 1, 0, 0, 0, 0, 0, 'Local'))224 }225 @{226 Value = '"1970-01-01T00:00:00.0000000Z"'227 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)).LocalDateTime)228 }229 @{230 Value = '"1970-01-01T00:00:00.0000000+00:00"'231 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)).LocalDateTime)232 }233 @{234 Value = '"1970-01-01T00:00:00.0000000-00:00"'235 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)).LocalDateTime)236 }237 @{238 Value = '"1970-01-01T00:00:00.0000000+10:00"'239 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 10)).LocalDateTime)240 }241 @{242 Value = '"1970-01-01T00:00:00.0000000-10:00"'243 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours -10)).LocalDateTime)244 }245 ) {246 param ($Value, $Expected)247 248 $json = $Value | ConvertFrom-Json -DateKind Local249 $json | Should -BeOfType ([DateTime])250 $json.Kind | Should -Be Local251 $json | Should -Be $Expected252 }253 254 It 'Parses DateKind Utc strings for <Value>' -TestCases @(255 @{256 Value = '"2022-11-02T12:01:44.5801388+04:00"'257 Expected = ([DateTimeOffset]::new(2022, 11, 2, 12, 1, 44, 580, 138, (New-TimeSpan -Hours 4)).AddTicks(8).UtcDateTime)258 }259 @{260 Value = '"2022-11-02T12:01:44.5801388-04:00"'261 Expected = ([DateTimeOffset]::new(2022, 11, 2, 12, 1, 44, 580, 138, (New-TimeSpan -Hours -4)).AddTicks(8).UtcDateTime)262 }263 @{264 Value = '"1970-01-01T00:00:00"'265 Expected = ([DateTime]::new(1970, 1, 1, 0, 0, 0, 0, 0, 'Utc'))266 }267 @{268 Value = '"1970-01-01T00:00:00.0000000Z"'269 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)).UtcDateTime)270 }271 @{272 Value = '"1970-01-01T00:00:00.0000000+00:00"'273 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)).UtcDateTime)274 }275 @{276 Value = '"1970-01-01T00:00:00.0000000-00:00"'277 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)).UtcDateTime)278 }279 @{280 Value = '"1970-01-01T00:00:00.0000000+10:00"'281 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 10)).UtcDateTime)282 }283 @{284 Value = '"1970-01-01T00:00:00.0000000-10:00"'285 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours -10)).UtcDateTime)286 }287 ) {288 param ($Value, $Expected)289 290 $json = $Value | ConvertFrom-Json -DateKind Utc291 $json | Should -BeOfType ([DateTime])292 $json.Kind | Should -Be Utc293 $json | Should -Be $Expected294 }295 296 It 'Parses DateKind Offset strings for <Value>' -TestCases @(297 @{298 Value = '"2022-11-02T12:01:44.5801388+04:00"'299 Expected = ([DateTimeOffset]::new(2022, 11, 2, 12, 1, 44, 580, 138, (New-TimeSpan -Hours 4)).AddTicks(8))300 }301 @{302 Value = '"2022-11-02T12:01:44.5801388-04:00"'303 Expected = ([DateTimeOffset]::new(2022, 11, 2, 12, 1, 44, 580, 138, (New-TimeSpan -Hours -4)).AddTicks(8))304 }305 @{306 Value = '"1970-01-01T00:00:00"'307 Expected = ([DateTimeOffset]::new([DateTime]::new(1970, 1, 1, 0, 0, 0, 0, 0, 'Local')))308 }309 @{310 Value = '"1970-01-01T00:00:00.0000000Z"'311 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)))312 }313 @{314 Value = '"1970-01-01T00:00:00.0000000+00:00"'315 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)))316 }317 @{318 Value = '"1970-01-01T00:00:00.0000000-00:00"'319 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 0)))320 }321 @{322 Value = '"1970-01-01T00:00:00.0000000+10:00"'323 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours 10)))324 }325 @{326 Value = '"1970-01-01T00:00:00.0000000-10:00"'327 Expected = ([DateTimeOffset]::new(1970, 1, 1, 0, 0, 0, 0, 0, (New-TimeSpan -Hours -10)))328 }329 ) {330 param ($Value, $Expected)331 332 $json = $Value | ConvertFrom-Json -DateKind Offset333 $json | Should -BeOfType ([DateTimeOffset])334 $json.EqualsExact($Expected) | Should -BeTrue335 }336 337 It 'Parses DateKind String strings for <Value>' -TestCases @(338 @{ Value = '"2022-11-02T12:01:44.5801388+04:00"' }339 @{ Value = '"2022-11-02T12:01:44.5801388-04:00"' }340 @{ Value = '"1970-01-01T00:00:00"' }341 @{ Value = '"1970-01-01T00:00:00.0000000Z"' }342 @{ Value = '"1970-01-01T00:00:00.0000000+00:00"' }343 @{ Value = '"1970-01-01T00:00:00.0000000-00:00"' }344 @{ Value = '"1970-01-01T00:00:00.0000000+10:00"' }345 @{ Value = '"1970-01-01T00:00:00.0000000-10:00"' }346 ) {347 param ($Value)348 349 $json = $Value | ConvertFrom-Json -DateKind String350 $json | Should -BeOfType ([string])351 $json | Should -Be $Value.Substring(1, $Value.Length - 2)352 }353 354 It 'Ignores comments in arrays' -TestCase $testCasesWithAndWithoutAsHashtableSwitch {355 param($AsHashtable)356 357 # https://github.com/powerShell/powerShell/issues/14553358 '[359 // comment360 100,361 /* comment */362 200363 ]' | ConvertFrom-Json -AsHashtable:$AsHashtable | Should -Be @(100, 200)364 }365 366 It 'Ignores comments in dictionaries' -TestCase $testCasesWithAndWithoutAsHashtableSwitch {367 param($AsHashtable)368 369 $json = '{370 // comment371 "a": 100,372 /* comment */373 "b": 200374 }' | ConvertFrom-Json -AsHashtable:$AsHashtable375 376 if ($AsHashtable) {377 $json.Keys | Should -Be @("a", "b")378 } else {379 $json.psobject.Properties | Should -HaveCount 2380 }381 382 $json.a | Should -Be 100383 $json.b | Should -Be 200384 }385}386 387Describe 'ConvertFrom-Json -Depth Tests' -tags "Feature" {388 389 BeforeAll {390 $testCasesJsonDepthWithAndWithoutAsHashtableSwitch = @(391 @{ Depth = 2; AsHashtable = $true }392 @{ Depth = 2; AsHashtable = $false }393 @{ Depth = 200; AsHashtable = $true }394 @{ Depth = 200; AsHashtable = $false }395 @{ Depth = 2000; AsHashtable = $true }396 @{ Depth = 2000; AsHashtable = $false }397 )398 }399 400 It 'Can convert an object with depth less than Depth param set to <Depth> and AsHashtable switch set to <AsHashtable>' -TestCases $testCasesJsonDepthWithAndWithoutAsHashtableSwitch {401 Param($AsHashtable, $Depth)402 $nestedJson = New-NestedJson -Depth ($Depth - 1)403 404 $json = $nestedJson | ConvertFrom-Json -AsHashtable:$AsHashtable -Depth $Depth405 406 if ($AsHashtable)407 {408 $json | Should -BeOfType Hashtable409 }410 else411 {412 $json | Should -BeOfType PSCustomObject413 }414 415 (Count-ObjectDepth -InputObject $json) | Should -Be ($Depth - 1)416 }417 418 It 'Can convert an object with depth equal to Depth param set to <Depth> and AsHashtable switch set to <AsHashtable>' -TestCases $testCasesJsonDepthWithAndWithoutAsHashtableSwitch {419 Param($AsHashtable, $Depth)420 $nestedJson = New-NestedJson -Depth:$Depth421 422 $json = $nestedJson | ConvertFrom-Json -AsHashtable:$AsHashtable -Depth $Depth423 424 if ($AsHashtable)425 {426 $json | Should -BeOfType Hashtable427 }428 else429 {430 $json | Should -BeOfType PSCustomObject431 }432 433 (Count-ObjectDepth -InputObject $json) | Should -Be $Depth434 }435 436 It 'Fails to convert an object with greater depth than Depth param set to <Depth> and AsHashtable switch set to <AsHashtable>' -TestCases $testCasesJsonDepthWithAndWithoutAsHashtableSwitch {437 Param($AsHashtable, $Depth)438 $nestedJson = New-NestedJson -Depth ($Depth + 1)439 440 { $nestedJson | ConvertFrom-Json -AsHashtable:$AsHashtable -Depth $Depth } |441 Should -Throw -ErrorId "System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand"442 }443}444 