← Files Power BI DesktopARCHIVED FILE

scripts/Invoke-PowerBISemanticTestRunner.ps1

5.24 KB · Oct 2, 2026 · 00:32 UTC

↓ Download file

param(
    [string]$Path = ".",
    [string]$ExpectationsPath,
    [string]$Server,
    [string]$OutputPath,
    [switch]$FailOnPending,
    [switch]$Json
)

$ErrorActionPreference = 'Stop'
$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$catalog = & (Join-Path $scriptRoot 'New-PowerBIMetricCatalog.ps1') -Path $Path -Json | ConvertFrom-Json

if (-not $ExpectationsPath) {
    $ExpectationsPath = Join-Path (Resolve-Path -LiteralPath $Path).Path 'measure-expectations.json'
}

$expectations = @()
if (Test-Path -LiteralPath $ExpectationsPath) {
    $loaded = Get-Content -Raw -LiteralPath $ExpectationsPath | ConvertFrom-Json
    if ($loaded.PSObject.Properties.Name -contains 'tests') { $expectations = @($loaded.tests) }
    elseif ($loaded.PSObject.Properties.Name -contains 'expectations') { $expectations = @($loaded.expectations) }
}

$expectationFile = $null
if ($expectations.Count -gt 0) {
    $expectationFile = [System.IO.Path]::GetTempFileName()
    try {
        [pscustomobject]@{
            schema = 'codex.powerbi.measureExpectations.v1'
            expectations = @($expectations | ForEach-Object {
                $name = if ($_.PSObject.Properties.Name -contains 'measure') { $_.measure } else { $_.name }
                $filters = if ($_.PSObject.Properties.Name -contains 'filters') { $_.filters } elseif ($_.PSObject.Properties.Name -contains 'filterContext' -and $_.filterContext -isnot [string]) { $_.filterContext } else { [pscustomobject]@{} }
                [pscustomobject]@{
                    measure = $name
                    expected = $_.expected
                    tolerance = if ($_.PSObject.Properties.Name -contains 'tolerance') { $_.tolerance } else { 0 }
                    filters = $filters
                    existsOnly = (($null -eq $_.expected) -or ($_.PSObject.Properties.Name -contains 'existsOnly' -and $_.existsOnly))
                }
            })
        } | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $expectationFile -Encoding UTF8

        $argsHash = @{
            Path = $Path
            ExpectationsPath = $expectationFile
            Json = $true
        }
        if ($Server) { $argsHash.Server = $Server }
        if ($FailOnPending) { $argsHash.FailOnPending = $true }
        $expectationResult = & (Join-Path $scriptRoot 'Test-PowerBIMeasureExpectations.ps1') @argsHash | ConvertFrom-Json
        $tests = @($expectationResult.checks | ForEach-Object {
            [pscustomobject]@{
                measure = $_.measure
                status = 'Configured'
                filterContext = $_.filters
                expected = $_.expected
                actual = $_.actual
                tolerance = $_.tolerance
                result = $_.status
                detail = $_.detail
                daxQuery = $_.daxQuery
                error = $_.error
            }
        })
    }
    finally {
        if ($expectationFile -and (Test-Path -LiteralPath $expectationFile)) {
            Remove-Item -LiteralPath $expectationFile -Force
        }
    }
}
else {
    $tests = @($catalog.metrics | ForEach-Object {
        [pscustomobject]@{
            measure = $_.name
            status = 'Generated'
            filterContext = [pscustomobject]@{}
            expected = '[TODO]'
            actual = $null
            tolerance = 0
            result = 'QueryGenerated'
            detail = 'No expectation supplied. Generated semantic test placeholder.'
            daxQuery = ('EVALUATE ROW("{0}", [{1}])' -f ($_.name -replace '"', '""'), ($_.name -replace ']', ']]'))
            error = $null
        }
    })
}

$failed = @($tests | Where-Object { $_.result -eq 'Failed' -or ($FailOnPending -and $_.result -eq 'PendingLiveDax') })
$pending = @($tests | Where-Object { $_.result -eq 'PendingLiveDax' })
$passed = @($tests | Where-Object { $_.result -in @('Passed', 'ExistsOnly', 'QueryGenerated') })
$result = [pscustomobject]@{
    schema = 'codex.powerbi.semanticTestRunner.v2'
    generated = (Get-Date).ToString('s')
    source = (Resolve-Path -LiteralPath $Path).Path
    expectationsPath = if (Test-Path -LiteralPath $ExpectationsPath) { (Resolve-Path -LiteralPath $ExpectationsPath).Path } else { $ExpectationsPath }
    status = if ($failed.Count -gt 0) { 'Failed' } elseif ($pending.Count -gt 0) { 'PendingLiveDax' } else { 'Passed' }
    testCount = @($tests).Count
    passedCount = $passed.Count
    failedCount = $failed.Count
    pendingCount = $pending.Count
    failOnPending = [bool]$FailOnPending
    tests = @($tests)
}

if ($Json) {
    $text = $result | ConvertTo-Json -Depth 12
    if ($OutputPath) { Set-Content -LiteralPath $OutputPath -Value $text -Encoding UTF8 }
    $text
    return
}
$lines = @(
    '# Semantic Test Runner',
    '',
    "Status: $($result.status)",
    "Tests: $($result.testCount)",
    "Passed: $($result.passedCount)",
    "Failed: $($result.failedCount)",
    "Pending: $($result.pendingCount)",
    '',
    '## Tests'
) + @($tests | ForEach-Object { "- [$($_.result)] $($_.measure): $($_.detail)" })
$content = ($lines -join [Environment]::NewLine) + [Environment]::NewLine
if ($OutputPath) { Set-Content -LiteralPath $OutputPath -Value $content -Encoding UTF8 }
$content

SHA-256: 5504a0261b28fcda8c83ec68bb941111038661d1e8212123c145223dfa16a887