Private/Document/Tests/ConvertTo-OpenApiOperation.Tests.ps1
|
BeforeAll { Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '../../../tcs.openapi.psd1') -Force } Describe 'ConvertTo-OpenApiOperation' { BeforeAll { $script:build = InModuleScope tcs.openapi { { param([string]$PathItem, [string]$Method = 'get', [string]$Components = '{}') $root = ConvertFrom-OpenApiJson -Text ('{"components":' + $Components + '}') $context = Get-OpenApiNormalizationContext -Root $root -SourceVersion '3.0.3' $item = ConvertFrom-OpenApiJson -Text $PathItem $operation = ConvertTo-OpenApiOperation -Context $context -OperationId 'op' -Method $Method -Path '/things/{id}' -Operation $item[$Method] -PathItem $item -PathItemPointer '/paths/~1things~1{id}' [pscustomobject]@{ Operation = $operation; Context = $context } } } } It 'has the documented properties and PSTypeName' { InModuleScope tcs.openapi -Parameters @{ Build = $script:build } { param($Build) $operation = (& $Build '{"get":{"summary":"s","description":"d","tags":["a","b"],"externalDocs":{"url":"https://docs"},"responses":{"200":{"description":"ok"}}}}').Operation $operation.PSObject.TypeNames | Should -Contain 'Tcs.OpenApi.Operation' foreach ($name in 'OperationId', 'Method', 'Path', 'Tags', 'Summary', 'Description', 'Deprecated', 'ExternalDocsUrl', 'Parameters', 'RequestBody', 'Responses', 'Security', 'Paging', 'Extensions') { $operation.PSObject.Properties.Name | Should -Contain $name } $operation.OperationId | Should -Be 'op' $operation.Method | Should -BeExactly 'GET' $operation.Path | Should -Be '/things/{id}' $operation.Tags | Should -Be @('a', 'b') $operation.Summary | Should -Be 's' $operation.Description | Should -Be 'd' $operation.ExternalDocsUrl | Should -Be 'https://docs' $operation.Deprecated | Should -BeFalse $operation.RequestBody | Should -BeNullOrEmpty $operation.Unsupported | Should -BeFalse $operation.Responses[0].StatusCode | Should -Be '200' } } It 'merges path-level parameters; the operation wins on name and location' { InModuleScope tcs.openapi -Parameters @{ Build = $script:build } { param($Build) $operation = (& $Build '{"parameters":[{"name":"id","in":"path","description":"path level"},{"name":"X-Trace","in":"header"},{"name":"id","in":"query"}],"get":{"parameters":[{"name":"id","in":"path","description":"operation level"},{"name":"x-trace","in":"header","required":true},{"name":"extra","in":"query"}]}}').Operation @($operation.Parameters | ForEach-Object { '{0}:{1}' -f $_.In, $_.Name }) | Should -Be @('path:id', 'header:x-trace', 'query:id', 'query:extra') $operation.Parameters[0].Description | Should -Be 'operation level' $operation.Parameters[1].Required | Should -BeTrue } } It 'resolves parameter and request body refs' { InModuleScope tcs.openapi -Parameters @{ Build = $script:build } { param($Build) $components = '{"parameters":{"limit":{"name":"limit","in":"query","schema":{"type":"integer"}}},"requestBodies":{"Thing":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}}' $operation = (& $Build '{"post":{"parameters":[{"$ref":"#/components/parameters/limit"}],"requestBody":{"$ref":"#/components/requestBodies/Thing"}}}' 'post' $components).Operation $operation.Parameters[0].Name | Should -Be 'limit' $operation.Parameters[0].Schema.Type | Should -Be 'integer' $operation.RequestBody.Required | Should -BeTrue $operation.RequestBody.Content[0].ContentType | Should -Be 'application/json' $operation.Method | Should -Be 'POST' } } It 'keeps Security null when absent, empty for [] and a list otherwise' { InModuleScope tcs.openapi -Parameters @{ Build = $script:build } { param($Build) $components = '{"securitySchemes":{"key":{"type":"apiKey","name":"k","in":"header"}}}' (& $Build '{"get":{}}' 'get' $components).Operation.Security | Should -BeNullOrEmpty $none = (& $Build '{"get":{"security":[]}}' 'get' $components).Operation.Security $null -eq $none | Should -BeFalse , $none | Should -BeOfType [object[]] $none.Count | Should -Be 0 $list = (& $Build '{"get":{"security":[{"key":[]}]}}' 'get' $components).Operation.Security $list.Count | Should -Be 1 $list[0].Contains('key') | Should -BeTrue } } It 'keeps x- extensions, including the x-ps overrides' { InModuleScope tcs.openapi -Parameters @{ Build = $script:build } { param($Build) $extensions = (& $Build '{"get":{"x-ps-name":"Get-Thing","x-ps-verb":"Get","x-ps-noun":"Thing","x-other":{"a":1},"summary":"s"}}').Operation.Extensions @($extensions.Keys) | Should -Be @('x-ps-name', 'x-ps-verb', 'x-ps-noun', 'x-other') $extensions['x-other']['a'] | Should -Be 1 } } It 'reports deprecated operations as OA060' { InModuleScope tcs.openapi -Parameters @{ Build = $script:build } { param($Build) $result = & $Build '{"get":{"deprecated":true}}' $result.Operation.Deprecated | Should -BeTrue $result.Context.Findings[0].Code | Should -Be 'OA060' $result.Context.Findings[0].Severity | Should -Be 'Information' $result.Context.Findings[0].Operation | Should -Be 'op' $result.Context.Findings[0].Pointer | Should -BeExactly '/paths/~1things~1{id}/get/deprecated' } } It 'flags operations with an external $ref as Unsupported' { InModuleScope tcs.openapi -Parameters @{ Build = $script:build } { param($Build) $result = & $Build '{"get":{"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"other.json#/Thing"}}}}}}}' $result.Operation.Unsupported | Should -BeTrue @($result.Context.Findings | Where-Object Code -EQ 'OA020').Count | Should -Be 1 $result.Context.Findings[0].Operation | Should -Be 'op' } } It 'flags operations that reuse a cached schema with an external $ref, with a finding of their own' { InModuleScope tcs.openapi { $root = ConvertFrom-OpenApiJson -Text '{"components":{"schemas":{"W":{"properties":{"x":{"$ref":"other.json#/X"}}}}}}' $context = Get-OpenApiNormalizationContext -Root $root $null = Resolve-OpenApiSchemaReference -Context $context -Reference '#/components/schemas/W' -Pointer '/components/schemas/W' $item = ConvertFrom-OpenApiJson -Text '{"get":{"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/W"}}}}}}}' $operation = ConvertTo-OpenApiOperation -Context $context -OperationId 'getW' -Method 'get' -Path '/w' -Operation $item['get'] -PathItem $item -PathItemPointer '/paths/~1w' $operation.Unsupported | Should -BeTrue $own = @($context.Findings | Where-Object { $_.Code -eq 'OA020' -and $_.Operation -eq 'getW' }) $own.Count | Should -Be 1 $own[0].Pointer | Should -BeExactly '/paths/~1w/get' $context.CurrentOperation | Should -BeNullOrEmpty } } It 'detects paging' { InModuleScope tcs.openapi -Parameters @{ Build = $script:build } { param($Build) (& $Build '{"get":{"x-ms-pageable":{"nextLinkName":"nextLink"}}}').Operation.Paging.Kind | Should -Be 'nextLink' } } } |