Private/Test-DFCategoryDbSchema.ps1
|
#Requires -Version 7.0 function Test-DFCategoryDbSchema { <# .SYNOPSIS Validates a merged category-db document against the trifle discovery schema. Returns $true if valid; populates -Errors with any violations. .DESCRIPTION Checks structural shape only: schemaVersion, taxonomy vocabularies, and per-tool required fields / closed-vocabulary membership. Does not check cross-references (relatedTo/alternativeTo pointing at real keys) — that is intentionally out of scope, same as Test-DFToolSchema. .PARAMETER Database The parsed category-db document (schemaVersion, taxonomy, tools). .PARAMETER Errors Reference to an array populated with violation messages. Empty when validation passes. .OUTPUTS [bool] - $true if valid, $false if any violations found. #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory)][PSCustomObject]$Database, [ref]$Errors ) $errs = [System.Collections.Generic.List[string]]::new() function PSProp ([PSCustomObject]$obj, [string]$key) { if ($null -eq $obj) { return $null } $p = $obj.PSObject.Properties[$key] if ($p) { return $p.Value } else { return $null } } if (-not (PSProp $Database 'schemaVersion')) { $errs.Add('Missing required field: schemaVersion') } elseif ((PSProp $Database 'schemaVersion') -ne 1) { $errs.Add("Unsupported schemaVersion '$($Database.schemaVersion)' — expected 1") } $taxonomy = PSProp $Database 'taxonomy' $functionVocab = @(PSProp $taxonomy 'function') $worksWithVocab = @(PSProp $taxonomy 'worksWith') if (-not $functionVocab -or $functionVocab.Count -eq 0) { $errs.Add('taxonomy.function must be a non-empty array') } if (-not $worksWithVocab -or $worksWithVocab.Count -eq 0) { $errs.Add('taxonomy.worksWith must be a non-empty array') } $tools = PSProp $Database 'tools' if (-not $tools) { $errs.Add('Missing required field: tools') } else { $validInterfaces = @('cli', 'tui', 'gui') foreach ($prop in $tools.PSObject.Properties) { $key = $prop.Name $entry = $prop.Value $function = @(PSProp $entry 'function') if (-not $function -or $function.Count -eq 0) { $errs.Add("$key`: function must be a non-empty array") } else { foreach ($f in $function) { if ($f -notin $functionVocab) { $errs.Add("$key`: unknown function value '$f'") } } } # worksWith is optional; @() around a $null property value would # otherwise produce a 1-element array containing $null rather than # an empty array, so guard explicitly instead of wrapping $null. $worksWithRaw = PSProp $entry 'worksWith' $worksWith = if ($null -eq $worksWithRaw) { @() } else { @($worksWithRaw) } foreach ($w in $worksWith) { if ($w -notin $worksWithVocab) { $errs.Add("$key`: unknown worksWith value '$w'") } } $interface = PSProp $entry 'interface' if (-not $interface) { $errs.Add("$key`: missing required field interface") } elseif ($interface -notin $validInterfaces) { $errs.Add("$key`: invalid interface '$interface'. Valid: $($validInterfaces -join ', ')") } $popularity = PSProp $entry 'popularity' if ($null -ne $popularity -and ($popularity -lt 0 -or $popularity -gt 3)) { $errs.Add("$key`: popularity must be 0-3, got '$popularity'") } } } if ($Errors) { $Errors.Value = $errs.ToArray() } return $errs.Count -eq 0 } |