← Files Power BI DesktopARCHIVED FILE

scripts/Test-PowerBISemanticContract.ps1

3.19 KB · Oct 5, 2026 · 18:32 UTC

↓ Download file

param(
    [string]$Path = ".",
    [string]$ContractPath,
    [string]$OutputPath,
    [switch]$Json
)

$ErrorActionPreference = 'Stop'
$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$contract = if ($ContractPath -and (Test-Path -LiteralPath $ContractPath)) {
    Get-Content -Raw -LiteralPath $ContractPath | ConvertFrom-Json
}
else {
    & (Join-Path $scriptRoot 'New-PowerBIKpiTrustContract.ps1') -Path $Path -Json | ConvertFrom-Json
}
$tests = & (Join-Path $scriptRoot 'Invoke-PowerBISemanticTestRunner.ps1') -Path $Path -Json | ConvertFrom-Json
$catalog = & (Join-Path $scriptRoot 'New-PowerBIMetricCatalog.ps1') -Path $Path -Json | ConvertFrom-Json

$findings = New-Object System.Collections.Generic.List[object]
foreach ($metric in @($catalog.metrics)) {
    if ($metric.owner -match '\[TODO' -or [string]::IsNullOrWhiteSpace($metric.owner)) {
        $findings.Add([pscustomobject]@{ severity = 'High'; metric = $metric.name; contractArea = 'Owner'; message = 'Metric owner is missing.' }) | Out-Null
    }
    if ($metric.businessDefinition -match '\[TODO' -or [string]::IsNullOrWhiteSpace($metric.businessDefinition)) {
        $findings.Add([pscustomobject]@{ severity = 'High'; metric = $metric.name; contractArea = 'BusinessDefinition'; message = 'Business definition is missing.' }) | Out-Null
    }
}
foreach ($test in @($tests.tests | Where-Object { $_.result -in @('PendingLiveDax','NotRun') -or $_.status -eq 'Generated' })) {
    $measureCandidates = @($test.measure, $test.measureName, $test.metricName) | Where-Object { $_ }
    $measure = if ($measureCandidates.Count -gt 0) { [string]$measureCandidates[0] } else { 'Unknown metric' }
    $findings.Add([pscustomobject]@{ severity = 'Medium'; metric = $measure; contractArea = 'ExecutableExpectation'; message = 'Semantic expectation is not backed by executed evidence.' }) | Out-Null
}

$high = @($findings.ToArray() | Where-Object { $_.severity -eq 'High' }).Count
$medium = @($findings.ToArray() | Where-Object { $_.severity -eq 'Medium' }).Count
$result = [pscustomobject]@{
    schema = 'codex.powerbi.semanticContractTest.v1'
    root = $catalog.root
    generated = (Get-Date).ToString('s')
    contractSource = if ($ContractPath) { $ContractPath } else { 'GeneratedFromModel' }
    metricCount = $catalog.metricCount
    testCount = $tests.testCount
    findingCount = $findings.Count
    highCount = $high
    mediumCount = $medium
    status = if ($high -gt 0) { 'ContractFailed' } elseif ($medium -gt 0) { 'ContractWithCaveats' } else { 'ContractPassed' }
    contract = $contract
    findings = @($findings.ToArray())
}

if ($Json) { $text = $result | ConvertTo-Json -Depth 10; if ($OutputPath) { Set-Content -LiteralPath $OutputPath -Value $text -Encoding UTF8 }; $text; return }
$md = @('# Power BI Semantic Contract Test', '', ('Status: **{0}**' -f $result.status), ('Findings: {0}' -f $result.findingCount), '') + @($result.findings | ForEach-Object { '- [{0}] `{1}` {2}: {3}' -f $_.severity, $_.metric, $_.contractArea, $_.message })
$content = ($md -join [Environment]::NewLine) + [Environment]::NewLine
if ($OutputPath) { Set-Content -LiteralPath $OutputPath -Value $content -Encoding UTF8 }
$content

SHA-256: 74464f862df4e6168b6b5b2abc31fed575e3908bfb781875c1083fd2f8c44705