@@ -317,8 +317,15 @@ def test_windows_native_acceptance_source_setup_uses_standalone_manifest_online_
317317
318318 source = re .search (r'if \(\$InstallMode -eq "source"\) \{(?P<body>.*?)\n \}' , setup , re .DOTALL )
319319 assert source is not None
320- assert "& brigade setup --manifest-source standalone" in source .group ("body" )
321- assert "& brigade setup --offline --manifest-source standalone" in source .group ("body" )
320+ body = source .group ("body" )
321+ # Empty-asset ids are derived before either setup invocation so online and
322+ # offline share the published-only expectation set.
323+ unpublished_at = body .index ("$unpublishedIds = @(Get-UnpublishedComponentIds -ManifestPath $bundledManifestPath)" )
324+ online_at = body .index ('Write-Step "brigade setup (online)"' )
325+ offline_at = body .index ('Write-Step "brigade setup --offline"' )
326+ assert unpublished_at < online_at < offline_at
327+ assert "& brigade setup --manifest-source standalone" in body
328+ assert "& brigade setup --offline --manifest-source standalone" in body
322329
323330
324331def test_windows_native_acceptance_pypi_setup_keeps_exact_manifest_default_and_digest_check ():
@@ -541,7 +548,10 @@ def test_windows_native_acceptance_source_mode_skips_only_bundled_unpublished_co
541548 unpublished_fn = _extract_powershell_function (script , "Get-UnpublishedComponentIds" )
542549 assert "function Get-UnpublishedComponentIds" in unpublished_fn
543550 assert "ConvertFrom-Json" in unpublished_fn
544- assert "PSObject.Properties.Count -eq 0" in unpublished_fn
551+ # Force Properties through @(...) so Windows PowerShell 5.1 reports Count 0
552+ # for empty "assets": {} maps (bare .Count is unreliable and dropped agent-notify).
553+ assert "@($assets.PSObject.Properties).Count -eq 0" in unpublished_fn
554+ assert "Write-Output -NoEnumerate" in unpublished_fn
545555 # The skip set is read from the bundled manifest on disk, not from the
546556 # component report, so an unsupported component with declared assets is
547557 # never treated as skippable.
@@ -550,8 +560,20 @@ def test_windows_native_acceptance_source_mode_skips_only_bundled_unpublished_co
550560 main = script [script .index ("$acceptRoot = $null" ) :]
551561 source_block = main [main .index ('if ($InstallMode -eq "source") {' ) : main .index ("$report = Get-ComponentReport" )]
552562 assert 'Join-Path $RepoRoot "src\\ brigade\\ templates\\ components\\ manifest-v1.json"' in source_block
553- assert "$unpublishedIds = Get-UnpublishedComponentIds -ManifestPath $bundledManifestPath" in source_block
554- assert "$unpublishedIds = @" in main
563+ assert (
564+ "[string[]]$unpublishedIds = @(Get-UnpublishedComponentIds -ManifestPath $bundledManifestPath)" in source_block
565+ )
566+ # Empty-asset omission applies to both setup requests: ids are computed
567+ # before online and offline standalone setup, then reused for health/smoke.
568+ assert source_block .index (
569+ "[string[]]$unpublishedIds = @(Get-UnpublishedComponentIds -ManifestPath $bundledManifestPath)"
570+ ) < source_block .index ('Write-Step "brigade setup (online)"' )
571+ assert source_block .index ('Write-Step "brigade setup (online)"' ) < source_block .index (
572+ 'Write-Step "brigade setup --offline"'
573+ )
574+ assert "& brigade setup --manifest-source standalone" in source_block
575+ assert "& brigade setup --offline --manifest-source standalone" in source_block
576+ assert "[string[]]$unpublishedIds = @()" in main
555577 assert "Assert-AllComponentsHealthy -Report $report -Skippable $unpublishedIds" in main
556578
557579 healthy_fn = _extract_powershell_function (script , "Assert-AllComponentsHealthy" )
@@ -573,6 +595,52 @@ def test_windows_native_acceptance_source_mode_skips_only_bundled_unpublished_co
573595 assert "& $agentNotifyExe version --json" in agent_block
574596
575597
598+ def test_windows_native_acceptance_source_setup_command_contract_omits_empty_asset_components ():
599+ """Source-mode setup must request only published components: both online and
600+ offline use standalone manifest selection (which omits empty-asset entries
601+ via published_component_ids) after deriving that same empty-asset set for
602+ post-setup assertions. Nonempty published assets stay required."""
603+ script = (ROOT / "scripts/windows-native-acceptance.ps1" ).read_text ()
604+ manifest = json .loads ((ROOT / "src/brigade/templates/components/manifest-v1.json" ).read_text ())
605+ unpublished = [component_id for component_id , record in manifest ["components" ].items () if not record .get ("assets" )]
606+ published = [component_id for component_id , record in manifest ["components" ].items () if record .get ("assets" )]
607+ assert unpublished == ["agent-notify" ]
608+ assert "graphtrail" in published
609+
610+ main = script [script .index ("$acceptRoot = $null" ) :]
611+ source_block = main [main .index ('if ($InstallMode -eq "source") {' ) : main .index ("$report = Get-ComponentReport" )]
612+ online_cmd = "& brigade setup --manifest-source standalone"
613+ offline_cmd = "& brigade setup --offline --manifest-source standalone"
614+ assert source_block .count (online_cmd ) == 1
615+ assert source_block .count (offline_cmd ) == 1
616+ # No alternate manifest path or component-selection flag that could request
617+ # unpublished agent-notify or hide a published component failure.
618+ assert "--manifest " not in source_block
619+ assert "Get-UnpublishedComponentIds" in source_block
620+ assert "@($assets.PSObject.Properties).Count -eq 0" in _extract_powershell_function (
621+ script , "Get-UnpublishedComponentIds"
622+ )
623+
624+
625+ def test_windows_native_acceptance_pypi_setup_command_contract_is_strict ():
626+ """PyPI/released mode must issue bare setup online and offline (every
627+ published component) and never populate the empty-asset skip set."""
628+ script = (ROOT / "scripts/windows-native-acceptance.ps1" ).read_text ()
629+ main = script [script .index ("$acceptRoot = $null" ) :]
630+ setup = main [main .index ("[string[]]$unpublishedIds = @()" ) : main .index ("$report = Get-ComponentReport" )]
631+ published = re .search (r"else \{(?P<body>.*?)\n \}" , setup , re .DOTALL )
632+ assert published is not None
633+ body = published .group ("body" )
634+ assert re .search (r"(?m)^ & brigade setup$" , body )
635+ assert re .search (r"(?m)^ & brigade setup --offline$" , body )
636+ assert "--manifest-source" not in body
637+ assert "Get-UnpublishedComponentIds" not in body
638+ # Default empty skip set is only replaced inside the source branch.
639+ assert setup .index ("[string[]]$unpublishedIds = @()" ) < setup .index ('if ($InstallMode -eq "source") {' )
640+ source_block = setup [setup .index ('if ($InstallMode -eq "source") {' ) : setup .index ("else {" )]
641+ assert "Get-UnpublishedComponentIds" in source_block
642+
643+
576644def test_windows_native_acceptance_pypi_mode_never_skips_agent_notify ():
577645 """Published/release acceptance keeps the strict five-component contract:
578646 no skip set, agent-notify must install, digest-check, and smoke by absolute
@@ -581,9 +649,12 @@ def test_windows_native_acceptance_pypi_mode_never_skips_agent_notify():
581649 main = script [script .index ("$acceptRoot = $null" ) :]
582650
583651 # The skip set is only populated in source mode; pypi mode leaves it empty.
584- source_block = main [main .index ('if ($InstallMode -eq "source") {' ) : main .index ("$report = Get-ComponentReport" )]
585- assert "$unpublishedIds = @()" in source_block
652+ setup = main [main .index ("[string[]]$unpublishedIds = @()" ) : main .index ("$report = Get-ComponentReport" )]
653+ source_block = setup [setup .index ('if ($InstallMode -eq "source") {' ) : setup .index ("else {" )]
654+ pypi_block = setup [setup .index ("else {" ) :]
655+ assert "[string[]]$unpublishedIds = @()" in setup
586656 assert "Get-UnpublishedComponentIds" in source_block
657+ assert "Get-UnpublishedComponentIds" not in pypi_block
587658
588659 assert "Assert-ManagedComponentDigests -Manifest $releaseManifest -Report $report -ManagedBin $managedBin" in main
589660 digest_fn = _extract_powershell_function (script , "Assert-ManagedComponentDigests" )
@@ -626,5 +697,7 @@ def test_windows_native_acceptance_pypi_mode_rejects_bare_agent_notify_metadata(
626697 # Source-mode skip behavior is preserved: the unpublished-ids derivation and
627698 # the required-ids gating are unchanged.
628699 source_block = main [main .index ('if ($InstallMode -eq "source") {' ) : main .index ("$report = Get-ComponentReport" )]
629- assert "$unpublishedIds = Get-UnpublishedComponentIds -ManifestPath $bundledManifestPath" in source_block
700+ assert (
701+ "[string[]]$unpublishedIds = @(Get-UnpublishedComponentIds -ManifestPath $bundledManifestPath)" in source_block
702+ )
630703 assert "Where-Object { $unpublishedIds -notcontains $_ }" in main
0 commit comments