Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/docs/maui-ci-facts.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,13 @@ modifies that visual test or the affected snapshot/platform.
The automated `/review tests` lane gathers failed UI result IDs from the public
`vstmr.dev.azure.com/.../testresults/resultsbybuild` endpoint, then reads the public
result-detail and attachment APIs. It publishes validated baseline/actual/diff PNGs to
the repository's `review-tests-assets` branch. A trusted post-step inserts as many
complete expandable comparison panels as fit inside the single test-failure analysis
comment while enforcing gh-aw's URL, mention, and character limits; excess panels are
reported as omitted rather than creating another comment. Visual publishing is
the repository's orphan, asset-only `review-tests-assets-v2` branch. The legacy
`review-tests-assets` branch remains intact so existing commit-pinned image URLs stay
reachable; it is not used for new publications because its inherited repository tree
contains workflow files that `GITHUB_TOKEN` cannot update. A trusted post-step inserts
as many complete expandable comparison panels as fit inside the single test-failure
analysis comment while enforcing gh-aw's URL, mention, and character limits; excess
panels are reported as omitted rather than creating another comment. Visual publishing is
supplementary evidence only: missing images never raise or lower the deterministic
verdict ceiling. Each panel also shows a conservative relationship label derived from
the exact test-and-platform `deterministicAttribution` plus exact changed snapshot/test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ BeforeAll {
'Get-VisualEvidenceDedupKey',
'Invoke-DownloadFile',
'Get-AssetBranchRef',
'Get-ValidatedAssetBranchState',
'Initialize-AssetBranch',
'Publish-GitAssets',
'Remove-VisualDownloadDirectory'
Expand Down Expand Up @@ -350,24 +351,221 @@ Describe 'Git publication budget enforcement' {
} | Should -Throw '*Publish budget exhausted*'
}

It 'initializes a missing branch from an asset-only root instead of the default branch tree' {
$script:refReadCount = 0
Mock Get-AssetBranchRef {
$script:refReadCount++
if ($script:refReadCount -eq 1) {
return $null
}
return [pscustomobject]@{
object = [pscustomobject]@{ sha = 'root-commit' }
}
}
Mock Invoke-GhApiJson {
if ($Method -eq 'POST' -and $Endpoint -like '*/git/blobs') {
return [pscustomobject]@{ sha = 'marker-blob' }
}
if ($Method -eq 'POST' -and $Endpoint -like '*/git/trees') {
return [pscustomobject]@{ sha = 'root-tree' }
}
if ($Method -eq 'POST' -and $Endpoint -like '*/git/commits') {
return [pscustomobject]@{ sha = 'root-commit' }
}
if ($Method -eq 'POST' -and $Endpoint -like '*/git/refs') {
return $null
}
if ($Method -eq 'GET' -and $Endpoint -like '*/git/commits/root-commit') {
return [pscustomobject]@{ tree = [pscustomobject]@{ sha = 'root-tree' } }
}
if ($Method -eq 'GET' -and $Endpoint -like '*/git/trees/root-tree') {
return [pscustomobject]@{
truncated = $false
tree = @(
[pscustomobject]@{
path = '.review-tests-assets'
mode = '100644'
type = 'blob'
}
)
}
}
throw "Unexpected API call: $Method $Endpoint"
}

$state = Initialize-AssetBranch `
-Repository 'dotnet/maui' `
-Branch 'review-tests-assets-v2'

$state.ref.object.sha | Should -Be 'root-commit'
Should -Invoke Invoke-GhApiJson -Times 1 -Exactly -ParameterFilter {
$Method -eq 'POST' -and
$Endpoint -eq 'repos/dotnet/maui/git/blobs' -and
$Body.content -eq 'Generated by /review tests. Do not edit.' -and
$Body.encoding -eq 'utf-8'
}
Should -Invoke Invoke-GhApiJson -Times 1 -Exactly -ParameterFilter {
$Method -eq 'POST' -and
$Endpoint -eq 'repos/dotnet/maui/git/trees' -and
-not $Body.ContainsKey('base_tree') -and
@($Body.tree).Count -eq 1 -and
$Body.tree[0].path -eq '.review-tests-assets' -and
$Body.tree[0].mode -eq '100644' -and
$Body.tree[0].type -eq 'blob' -and
$Body.tree[0].sha -eq 'marker-blob'
}
Should -Invoke Invoke-GhApiJson -Times 1 -Exactly -ParameterFilter {
$Method -eq 'POST' -and
$Endpoint -eq 'repos/dotnet/maui/git/commits' -and
$Body.tree -eq 'root-tree' -and
@($Body.parents).Count -eq 0
}
Should -Invoke Invoke-GhApiJson -Times 1 -Exactly -ParameterFilter {
$Method -eq 'POST' -and
$Endpoint -eq 'repos/dotnet/maui/git/refs' -and
$Body.ref -eq 'refs/heads/review-tests-assets-v2' -and
$Body.sha -eq 'root-commit'
}
Should -Invoke Invoke-GhApiJson -Times 0 -Exactly -ParameterFilter {
$Method -eq 'GET' -and $Endpoint -eq 'repos/dotnet/maui'
}
}

It 'uses an existing asset-only branch without creating Git objects' {
Mock Get-AssetBranchRef {
return [pscustomobject]@{
object = [pscustomobject]@{ sha = 'existing-commit' }
}
}
Mock Invoke-GhApiJson {
if ($Method -eq 'GET' -and $Endpoint -like '*/git/commits/existing-commit') {
return [pscustomobject]@{ tree = [pscustomobject]@{ sha = 'asset-tree' } }
}
if ($Method -eq 'GET' -and $Endpoint -like '*/git/trees/asset-tree') {
return [pscustomobject]@{
truncated = $false
tree = @(
[pscustomobject]@{
path = '.review-tests-assets'
mode = '100644'
type = 'blob'
},
[pscustomobject]@{ path = 'pr-123'; type = 'tree' }
)
}
}
throw "Unexpected API call: $Method $Endpoint"
}

$state = Initialize-AssetBranch `
-Repository 'dotnet/maui' `
-Branch 'review-tests-assets-v2'

$state.ref.object.sha | Should -Be 'existing-commit'
Should -Invoke Invoke-GhApiJson -Times 0 -Exactly -ParameterFilter {
$Method -eq 'POST'
}
}

It 'rejects an existing branch that contains repository files' {
Mock Get-AssetBranchRef {
return [pscustomobject]@{
object = [pscustomobject]@{ sha = 'unsafe-commit' }
}
}
Mock Invoke-GhApiJson {
if ($Endpoint -like '*/git/commits/unsafe-commit') {
return [pscustomobject]@{ tree = [pscustomobject]@{ sha = 'unsafe-tree' } }
}
if ($Endpoint -like '*/git/trees/unsafe-tree') {
return [pscustomobject]@{
truncated = $false
tree = @(
[pscustomobject]@{ path = '.github'; type = 'tree' }
)
}
}
throw "Unexpected API call: $Method $Endpoint"
}

{
Initialize-AssetBranch `
-Repository 'dotnet/maui' `
-Branch 'review-tests-assets-v2'
} | Should -Throw "*unexpected top-level path(s): .github*"
Comment thread
kubaflo marked this conversation as resolved.
Outdated
}

It 'accepts a concurrently-created branch only after validating its tree' {
$script:refReadCount = 0
Mock Get-AssetBranchRef {
$script:refReadCount++
if ($script:refReadCount -eq 1) {
return $null
}
return [pscustomobject]@{
object = [pscustomobject]@{ sha = 'winner-commit' }
}
}
Mock Invoke-GhApiJson {
if ($Method -eq 'POST' -and $Endpoint -like '*/git/blobs') {
return [pscustomobject]@{ sha = 'marker-blob' }
}
if ($Method -eq 'POST' -and $Endpoint -like '*/git/trees') {
return [pscustomobject]@{ sha = 'loser-tree' }
}
if ($Method -eq 'POST' -and $Endpoint -like '*/git/commits') {
return [pscustomobject]@{ sha = 'loser-commit' }
}
if ($Method -eq 'POST' -and $Endpoint -like '*/git/refs') {
throw 'HTTP 422 Reference already exists'
}
if ($Method -eq 'GET' -and $Endpoint -like '*/git/commits/winner-commit') {
return [pscustomobject]@{ tree = [pscustomobject]@{ sha = 'winner-tree' } }
}
if ($Method -eq 'GET' -and $Endpoint -like '*/git/trees/winner-tree') {
return [pscustomobject]@{
truncated = $false
tree = @(
[pscustomobject]@{
path = '.review-tests-assets'
mode = '100644'
type = 'blob'
}
)
}
}
throw "Unexpected API call: $Method $Endpoint"
}

$state = Initialize-AssetBranch `
-Repository 'dotnet/maui' `
-Branch 'review-tests-assets-v2'

$state.ref.object.sha | Should -Be 'winner-commit'
Should -Invoke Invoke-GhApiJson -Times 1 -Exactly -ParameterFilter {
$Method -eq 'GET' -and $Endpoint -like '*/git/trees/winner-tree'
}
}

It 'passes the shared deadline to every publication API call' {
$assetPath = Join-Path $TestDrive 'asset.png'
[System.IO.File]::WriteAllBytes($assetPath, [byte[]](1, 2, 3))
$deadline = (Get-Date).AddMinutes(5)

Mock Initialize-AssetBranch {}
Mock Get-AssetBranchRef {
Mock Initialize-AssetBranch {
return [pscustomobject]@{
object = [pscustomobject]@{ sha = 'parent-sha' }
ref = [pscustomobject]@{
object = [pscustomobject]@{ sha = 'parent-sha' }
}
commit = [pscustomobject]@{
tree = [pscustomobject]@{ sha = 'parent-tree' }
}
}
}
Mock Invoke-GhApiJson {
if ($Method -eq 'POST' -and $Endpoint -like '*/git/blobs') {
return [pscustomobject]@{ sha = 'blob-sha' }
}
if ($Method -eq 'GET' -and $Endpoint -like '*/git/commits/*') {
return [pscustomobject]@{ tree = [pscustomobject]@{ sha = 'parent-tree' } }
}
if ($Method -eq 'POST' -and $Endpoint -like '*/git/trees') {
return [pscustomobject]@{ sha = 'new-tree' }
}
Expand All @@ -379,13 +577,13 @@ Describe 'Git publication budget enforcement' {

$result = Publish-GitAssets `
-Repository 'dotnet/maui' `
-Branch 'review-tests-assets' `
-Branch 'review-tests-assets-v2' `
-Assets @([pscustomobject]@{ localPath = $assetPath; assetPath = 'pr-123/asset.png' }) `
-CommitMessage 'test' `
-Deadline $deadline

$result | Should -Be 'new-commit'
Should -Invoke Invoke-GhApiJson -Times 5 -Exactly -ParameterFilter {
Should -Invoke Invoke-GhApiJson -Times 4 -Exactly -ParameterFilter {
$Deadline -eq $deadline
}
}
Expand Down
Loading
Loading