完成40%
This commit is contained in:
@@ -7,7 +7,6 @@ $issues = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
$createPath = '/genealogy/app/genealogies'
|
||||
$statusPath = '/genealogy/app/genealogy-bootstrap-operations/{operationKey}'
|
||||
$settingsPath = '/genealogy/app/genealogies/{genealogyId}'
|
||||
$regionSearchPath = '/genealogy/app/region/search'
|
||||
$personPath = '/genealogy/app/genealogies/{genealogyId}/lineage/persons'
|
||||
$personDetailPath = '/genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}'
|
||||
@@ -30,6 +29,35 @@ function Test-IsJsonBoolean {
|
||||
return $Value -is [System.Boolean] -and $Value -eq $Expected
|
||||
}
|
||||
|
||||
function Test-IsNonNullable {
|
||||
param([object]$Schema)
|
||||
if (-not $Schema) { return $false }
|
||||
$nullable = $Schema.PSObject.Properties['nullable']
|
||||
return -not $nullable -or (Test-IsJsonBoolean $nullable.Value $false)
|
||||
}
|
||||
|
||||
function Assert-AllowedSchemaKeywords {
|
||||
param([object]$Schema, [string]$Label, [string[]]$Allowed)
|
||||
if (-not $Schema) { return }
|
||||
$annotations = @('title', 'description', 'example', 'examples', 'deprecated')
|
||||
foreach ($property in @($Schema.PSObject.Properties)) {
|
||||
if ($property.Name -like 'x-*' -or $property.Name -in $annotations -or $property.Name -in $Allowed) { continue }
|
||||
Add-Issue "JSON $Label contains an unowned schema keyword: $($property.Name)"
|
||||
}
|
||||
}
|
||||
|
||||
function Test-IsPureSchemaRef {
|
||||
param([object]$Schema, [string]$ExpectedRef, [string]$Label)
|
||||
if (-not $Schema) { return $false }
|
||||
$properties = @($Schema.PSObject.Properties.Name)
|
||||
$actualRef = [string]$Schema.'$ref'
|
||||
if ($properties.Count -ne 1 -or $properties[0] -cne '$ref' -or $actualRef -cne $ExpectedRef) {
|
||||
Add-Issue "JSON $Label must be the sole exact local schema ref $ExpectedRef; actual: $actualRef"
|
||||
return $false
|
||||
}
|
||||
return $true
|
||||
}
|
||||
|
||||
$parityScript = Join-Path $PSScriptRoot 'openapi-yaml-json-parity-runtime-smoke.js'
|
||||
$parityOutput = @(& node $parityScript 2>&1)
|
||||
if ($LASTEXITCODE -ne 0 -or 'OPENAPI-YAML-JSON-PARITY PASS' -notin $parityOutput) {
|
||||
@@ -180,8 +208,8 @@ function Assert-RequestBodyRef {
|
||||
$media = $Operation.requestBody.content.PSObject.Properties['application/json']
|
||||
if (-not (Test-IsJsonBoolean $Operation.requestBody.required $true) -or -not $media) {
|
||||
Add-Issue "JSON $Label must require an application/json body"
|
||||
} elseif ([string]$media.Value.schema.'$ref' -ne $ExpectedRef) {
|
||||
Add-Issue "JSON $Label request body must use $ExpectedRef"
|
||||
} else {
|
||||
[void](Test-IsPureSchemaRef $media.Value.schema $ExpectedRef "$Label request body")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +233,8 @@ function Assert-ExactObject {
|
||||
} elseif ($requiredProperty) {
|
||||
Test-IsJsonArray $Schema.required
|
||||
} else { $true }
|
||||
if ($Schema.type -ne 'object' -or -not (Test-IsJsonBoolean $Schema.additionalProperties $false) -or
|
||||
if ($Schema.type -ne 'object' -or -not (Test-IsNonNullable $Schema) -or
|
||||
-not (Test-IsJsonBoolean $Schema.additionalProperties $false) -or
|
||||
-not $requiredShapeValid -or
|
||||
($actualFields -join ',') -ne ($expectedFields -join ',') -or
|
||||
($actualRequired -join ',') -ne ($expectedRequired -join ',')) {
|
||||
@@ -217,16 +246,18 @@ function Assert-ExactObject {
|
||||
if ($MaxProperties -ge 0 -and [int]$Schema.maxProperties -ne $MaxProperties) {
|
||||
Add-Issue "JSON $Label maxProperties must be $MaxProperties"
|
||||
}
|
||||
$allowedKeywords = @('type', 'properties', 'required', 'additionalProperties', 'nullable')
|
||||
if ($MinProperties -ge 0) { $allowedKeywords += 'minProperties' }
|
||||
if ($MaxProperties -ge 0) { $allowedKeywords += 'maxProperties' }
|
||||
Assert-AllowedSchemaKeywords $Schema $Label $allowedKeywords
|
||||
}
|
||||
|
||||
function Assert-PropertyRef {
|
||||
param([object]$Schema, [string]$SchemaName, [string]$Field, [string]$ExpectedRef)
|
||||
if (-not $Schema) { return }
|
||||
$property = $Schema.properties.PSObject.Properties[$Field]
|
||||
$actual = if ($property) { [string]$property.Value.'$ref' } else { '' }
|
||||
if ($actual -ne $ExpectedRef) {
|
||||
Add-Issue "JSON $SchemaName.$Field must use $ExpectedRef; actual: $actual"
|
||||
}
|
||||
if (-not $property) { Add-Issue "JSON $SchemaName.$Field must use $ExpectedRef"; return }
|
||||
[void](Test-IsPureSchemaRef $property.Value $ExpectedRef "$SchemaName.$Field")
|
||||
}
|
||||
|
||||
function Assert-ErrorSchema {
|
||||
@@ -364,7 +395,6 @@ function Assert-AllDeclaredResponsesPrivateJson {
|
||||
|
||||
$createOperation = Get-Operation $createPath 'post'
|
||||
$statusOperation = Get-Operation $statusPath 'get'
|
||||
$settingsOperation = Get-Operation $settingsPath 'put'
|
||||
$regionOperation = Get-Operation $regionSearchPath 'get'
|
||||
$personOperation = Get-Operation $personPath 'post'
|
||||
$personUpdateOperation = Get-Operation $personDetailPath 'put'
|
||||
@@ -379,7 +409,6 @@ if ($document.paths.PSObject.Properties['/genealogy/region/search']) {
|
||||
foreach ($entry in @(
|
||||
[pscustomobject]@{ Operation = $createOperation; Label = "POST $createPath"; Path = $createPath },
|
||||
[pscustomobject]@{ Operation = $statusOperation; Label = "GET $statusPath"; Path = $statusPath },
|
||||
[pscustomobject]@{ Operation = $settingsOperation; Label = "PUT $settingsPath"; Path = $settingsPath },
|
||||
[pscustomobject]@{ Operation = $regionOperation; Label = "GET $regionSearchPath"; Path = $regionSearchPath },
|
||||
[pscustomobject]@{ Operation = $personOperation; Label = "POST $personPath"; Path = $personPath },
|
||||
[pscustomobject]@{ Operation = $personUpdateOperation; Label = "PUT $personDetailPath"; Path = $personDetailPath },
|
||||
@@ -391,7 +420,6 @@ foreach ($entry in @(
|
||||
}
|
||||
|
||||
Assert-RequestBodyRef $createOperation "POST $createPath" '#/components/schemas/AppGenealogyBootstrapBody'
|
||||
Assert-RequestBodyRef $settingsOperation "PUT $settingsPath" '#/components/schemas/AppGenealogySettingsUpdateBody'
|
||||
|
||||
$idempotencyHeader = Get-Parameter $createOperation 'Idempotency-Key' 'header' $createPath
|
||||
if ($idempotencyHeader) {
|
||||
@@ -556,7 +584,6 @@ $rootPersonBody = Get-Schema 'AppGenealogyRootPersonBody'
|
||||
$operationKey = Get-Schema 'GenealogyBootstrapOperationKey'
|
||||
$regionCode = Get-Schema 'GenealogyRegionCode'
|
||||
$accessPreset = Get-Schema 'GenealogyAccessPreset'
|
||||
$settingsBody = Get-Schema 'AppGenealogySettingsUpdateBody'
|
||||
$bootstrapResult = Get-Schema 'GenealogyBootstrapResult'
|
||||
$operationStatus = Get-Schema 'GenealogyBootstrapOperationStatus'
|
||||
$pendingStatus = Get-Schema 'GenealogyBootstrapPendingStatus'
|
||||
@@ -573,7 +600,7 @@ Assert-ExactObject $bootstrapBody 'AppGenealogyBootstrapBody' @(
|
||||
Assert-PropertyRef $bootstrapBody 'AppGenealogyBootstrapBody' 'accessPreset' '#/components/schemas/GenealogyAccessPreset'
|
||||
Assert-PropertyRef $bootstrapBody 'AppGenealogyBootstrapBody' 'rootPerson' '#/components/schemas/AppGenealogyRootPersonBody'
|
||||
Assert-PropertyRef $bootstrapBody 'AppGenealogyBootstrapBody' 'regionCode' '#/components/schemas/GenealogyRegionCode'
|
||||
Assert-StringField $bootstrapBody 'AppGenealogyBootstrapBody' 'genealogyName' 1 24
|
||||
Assert-PropertyRef $bootstrapBody 'AppGenealogyBootstrapBody' 'genealogyName' '#/components/schemas/GenealogyName'
|
||||
Assert-StringField $bootstrapBody 'AppGenealogyBootstrapBody' 'surname' 1 4
|
||||
Assert-StringField $bootstrapBody 'AppGenealogyBootstrapBody' 'ancestralHall' 1 12
|
||||
|
||||
@@ -631,17 +658,20 @@ if ($regionCode) {
|
||||
if ($accessPreset) {
|
||||
$values = @($accessPreset.enum | Sort-Object)
|
||||
$expected = @('MEMBER_ONLY', 'PUBLIC_APPLY') | Sort-Object
|
||||
$nullable = $accessPreset.PSObject.Properties['nullable']
|
||||
if ($accessPreset.type -ne 'string' -or -not (Test-IsJsonArray $accessPreset.enum) -or
|
||||
($values -join ',') -ne ($expected -join ',')) {
|
||||
Add-Issue 'JSON GenealogyAccessPreset must contain only MEMBER_ONLY/PUBLIC_APPLY'
|
||||
($values -join ',') -ne ($expected -join ',') -or
|
||||
($nullable -and -not (Test-IsJsonBoolean $nullable.Value $false))) {
|
||||
Add-Issue 'JSON GenealogyAccessPreset must be non-null and contain only MEMBER_ONLY/PUBLIC_APPLY'
|
||||
}
|
||||
foreach ($keyword in @('not', 'allOf', 'anyOf', 'oneOf', 'const')) {
|
||||
if ($accessPreset.PSObject.Properties[$keyword]) {
|
||||
Add-Issue "JSON GenealogyAccessPreset must not define conflicting schema keyword: $keyword"
|
||||
}
|
||||
}
|
||||
Assert-AllowedSchemaKeywords $accessPreset 'GenealogyAccessPreset' @('type', 'enum', 'nullable')
|
||||
}
|
||||
|
||||
Assert-ExactObject $settingsBody 'AppGenealogySettingsUpdateBody' @(
|
||||
'genealogyName', 'intro', 'accessPreset'
|
||||
) @() 1 3
|
||||
Assert-PropertyRef $settingsBody 'AppGenealogySettingsUpdateBody' 'accessPreset' '#/components/schemas/GenealogyAccessPreset'
|
||||
|
||||
foreach ($legacySchema in @('GenealogyCreateBody', 'AppGenealogyCreateBody', 'GenealogyUpdateBody', 'AppGenealogyUpdateBody')) {
|
||||
if ($document.components.schemas.PSObject.Properties[$legacySchema]) {
|
||||
Add-Issue "JSON legacy schema must be removed in the same migration: $legacySchema"
|
||||
@@ -673,9 +703,7 @@ foreach ($schemaProperty in $document.components.schemas.PSObject.Properties) {
|
||||
}
|
||||
}
|
||||
foreach ($presetDefinition in @(Get-ComposedPropertyDefinitions $schemaProperty.Value 'accessPreset' @{})) {
|
||||
if ([string]$presetDefinition.'$ref' -ne '#/components/schemas/GenealogyAccessPreset') {
|
||||
Add-Issue "JSON $($schemaProperty.Name).accessPreset must reference GenealogyAccessPreset"
|
||||
}
|
||||
[void](Test-IsPureSchemaRef $presetDefinition '#/components/schemas/GenealogyAccessPreset' "$($schemaProperty.Name).accessPreset")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -967,7 +995,6 @@ foreach ($status in $statusErrorRefs.Keys) {
|
||||
Assert-RetryAfter $statusResponses['404'] "GET $statusPath" '404'
|
||||
Assert-RetryAfter $statusResponses['429'] "GET $statusPath" '429'
|
||||
|
||||
Assert-AllDeclaredResponsesPrivateJson $settingsOperation "PUT $settingsPath"
|
||||
Assert-AllDeclaredResponsesPrivateJson $personOperation "POST $personPath"
|
||||
Assert-AllDeclaredResponsesPrivateJson $personUpdateOperation "PUT $personDetailPath"
|
||||
Assert-AllDeclaredResponsesPrivateJson $personDeleteOperation "DELETE $personDetailPath"
|
||||
|
||||
Reference in New Issue
Block a user