vignette/scripts/test_public_runtime_task_definition_cutover.py
2026-08-29 23:58:33 +09:00

285 lines
11 KiB
Python

from __future__ import annotations
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path
SCRIPTS = Path(__file__).resolve().parent
CONTRACT = SCRIPTS / "public-runtime-task-definition-cutover.ps1"
REPO_ROOT = SCRIPTS.parent
class PublicRuntimeTaskDefinitionCutoverTest(unittest.TestCase):
def _run_powershell(self, body: str) -> subprocess.CompletedProcess[str]:
powershell = shutil.which("powershell.exe")
if powershell is None:
self.skipTest("Windows PowerShell 5.1 is not available")
with tempfile.TemporaryDirectory(prefix="vignette-task-cutover-") as raw:
harness = Path(raw) / "task-cutover.ps1"
harness.write_text(body, encoding="utf-8-sig")
return subprocess.run(
[
powershell,
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy",
"Bypass",
"-File",
str(harness),
],
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
timeout=30,
check=False,
)
def _assert_harness_passes(self, body: str) -> None:
completed = self._run_powershell(body)
self.assertEqual(
0,
completed.returncode,
msg=f"stdout={completed.stdout}\nstderr={completed.stderr}",
)
def test_contract_digest_ignores_only_enabled_state(self) -> None:
contract = str(CONTRACT).replace("'", "''")
self._assert_harness_passes(
f"""
. '{contract}'
$enabled = '<Task><Settings><Enabled>true</Enabled></Settings><Actions><Exec><Command>one</Command></Exec></Actions></Task>'
$disabled = '<Task><Settings><Enabled>false</Enabled></Settings><Actions><Exec><Command>one</Command></Exec></Actions></Task>'
$drifted = '<Task><Settings><Enabled>false</Enabled></Settings><Actions><Exec><Command>two</Command></Exec></Actions></Task>'
$enabledDigest = Get-PublicRuntimeTaskXmlContractSha256 -Xml $enabled
$disabledDigest = Get-PublicRuntimeTaskXmlContractSha256 -Xml $disabled
$driftedDigest = Get-PublicRuntimeTaskXmlContractSha256 -Xml $drifted
if ($enabledDigest -cne $disabledDigest) {{ exit 2 }}
if ($enabledDigest -ceq $driftedDigest) {{ exit 3 }}
exit 0
"""
)
def test_exact_action_contract_accepts_disabled_and_operational_snapshots(self) -> None:
contract = str(CONTRACT).replace("'", "''")
root = str(REPO_ROOT).replace("'", "''")
self._assert_harness_passes(
f"""
. '{contract}'
$contracts = @(Get-PublicRuntimeExpectedTaskActionContracts `
-StableSourceRoot '{root}' `
-ExpectedSourceCommit ('a' * 40) `
-ExpectedSourceTree ('b' * 40) `
-PythonPath 'C:\\Python311\\python.exe' `
-UserUploadDir 'C:\\Runtime\\uploads' `
-UserUploadManifestPath 'C:\\RuntimeState\\manifest.json' `
-ExpectedUserUploadManifestSha256 ('c' * 64) `
-UserUploadWriteFreezePath 'C:\\RuntimeState\\freeze.json' `
-CloudflaredPath 'C:\\Tools\\cloudflared.exe' `
-CloudflaredConfigPath 'C:\\RuntimeState\\cloudflared.yml' `
-PublicHealthUrl 'https://api-vignette.chanpaca.net/health')
$entries = @()
foreach ($item in $contracts) {{
$entries += [pscustomobject]@{{
role = $item.role
enabled = $false
action_execute = $item.execute
action_arguments = $item.arguments
action_working_directory = $item.working_directory
}}
}}
$snapshot = [pscustomobject]@{{ entries = $entries }}
$null = Assert-NewPublicRuntimeTaskDefinitionsPinned `
-Snapshot $snapshot `
-StableSourceRoot '{root}' `
-ExpectedSourceCommit ('a' * 40) `
-ExpectedSourceTree ('b' * 40) `
-PythonPath 'C:\\Python311\\python.exe' `
-UserUploadDir 'C:\\Runtime\\uploads' `
-UserUploadManifestPath 'C:\\RuntimeState\\manifest.json' `
-ExpectedUserUploadManifestSha256 ('c' * 64) `
-UserUploadWriteFreezePath 'C:\\RuntimeState\\freeze.json' `
-CloudflaredPath 'C:\\Tools\\cloudflared.exe' `
-CloudflaredConfigPath 'C:\\RuntimeState\\cloudflared.yml' `
-PublicHealthUrl 'https://api-vignette.chanpaca.net/health'
foreach ($entry in $entries) {{ $entry.enabled = $true }}
$null = Assert-NewPublicRuntimeTaskDefinitionsPinned `
-Snapshot $snapshot `
-StableSourceRoot '{root}' `
-ExpectedSourceCommit ('a' * 40) `
-ExpectedSourceTree ('b' * 40) `
-PythonPath 'C:\\Python311\\python.exe' `
-UserUploadDir 'C:\\Runtime\\uploads' `
-UserUploadManifestPath 'C:\\RuntimeState\\manifest.json' `
-ExpectedUserUploadManifestSha256 ('c' * 64) `
-UserUploadWriteFreezePath 'C:\\RuntimeState\\freeze.json' `
-CloudflaredPath 'C:\\Tools\\cloudflared.exe' `
-CloudflaredConfigPath 'C:\\RuntimeState\\cloudflared.yml' `
-PublicHealthUrl 'https://api-vignette.chanpaca.net/health' `
-AllowEnabled
$entries[0].action_arguments += ' -InjectedDrift'
try {{
$null = Assert-NewPublicRuntimeTaskDefinitionsPinned `
-Snapshot $snapshot `
-StableSourceRoot '{root}' `
-ExpectedSourceCommit ('a' * 40) `
-ExpectedSourceTree ('b' * 40) `
-PythonPath 'C:\\Python311\\python.exe' `
-UserUploadDir 'C:\\Runtime\\uploads' `
-UserUploadManifestPath 'C:\\RuntimeState\\manifest.json' `
-ExpectedUserUploadManifestSha256 ('c' * 64) `
-UserUploadWriteFreezePath 'C:\\RuntimeState\\freeze.json' `
-CloudflaredPath 'C:\\Tools\\cloudflared.exe' `
-CloudflaredConfigPath 'C:\\RuntimeState\\cloudflared.yml' `
-PublicHealthUrl 'https://api-vignette.chanpaca.net/health' `
-AllowEnabled
exit 2
}} catch {{
exit 0
}}
"""
)
def test_second_installer_failure_compensates_both_tasks_to_disabled(self) -> None:
contract = str(CONTRACT).replace("'", "''")
self._assert_harness_passes(
f"""
. '{contract}'
$script:states = @{{ Boot = $false; Watchdog = $false }}
$script:bootCalls = 0
$script:watchdogCalls = 0
$script:suspendCalls = 0
$script:assertCalls = 0
function Suspend-PublicRuntimeTasks {{
param($Snapshot, $TimeoutSec)
$script:suspendCalls += 1
$script:states.Boot = $false
$script:states.Watchdog = $false
}}
function Assert-PublicRuntimeTasksDisabledAndIdle {{
param($Snapshot, $TimeoutSec)
$script:assertCalls += 1
if ($script:states.Boot -or $script:states.Watchdog) {{
throw 'task was not compensated to disabled'
}}
}}
$bootInstaller = {{
$script:bootCalls += 1
$script:states.Boot = $true
}}
$watchdogInstaller = {{
$script:watchdogCalls += 1
throw 'synthetic second installer failure'
}}
$maintenance = @(
[pscustomobject]@{{ role='boot'; task_name='Boot' }},
[pscustomobject]@{{ role='watchdog'; task_name='Watchdog' }}
)
try {{
Invoke-PublicRuntimeTaskDefinitionInstallerPairDisabled `
-BootInstaller $bootInstaller `
-WatchdogInstaller $watchdogInstaller `
-MaintenanceSnapshot $maintenance `
-TimeoutSec 3
exit 2
}} catch {{
if ($script:bootCalls -ne 1 -or $script:watchdogCalls -ne 1) {{ exit 3 }}
if ($script:suspendCalls -ne 1 -or $script:assertCalls -ne 1) {{ exit 4 }}
if ($script:states.Boot -or $script:states.Watchdog) {{ exit 5 }}
exit 0
}}
"""
)
def test_second_enable_failure_compensates_both_tasks_to_disabled(self) -> None:
contract = str(CONTRACT).replace("'", "''")
self._assert_harness_passes(
f"""
. '{contract}'
$script:states = @{{ Boot = $false; Watchdog = $false }}
$script:taskPaths = @()
function Assert-PublicRuntimeTaskDefinitionSnapshotCurrent {{
param($ExpectedSnapshot)
return $ExpectedSnapshot
}}
function Enable-ScheduledTask {{
param($TaskName, $TaskPath, $ErrorAction)
$script:taskPaths += $TaskPath
if ($TaskPath -cne '\\') {{ throw 'wrong task path' }}
if ($TaskName -eq 'Watchdog') {{ throw 'synthetic second enable failure' }}
$script:states[$TaskName] = $true
}}
function Disable-ScheduledTask {{
param($TaskName, $TaskPath, $ErrorAction)
$script:taskPaths += $TaskPath
if ($TaskPath -cne '\\') {{ throw 'wrong task path' }}
$script:states[$TaskName] = $false
}}
function Get-PublicRuntimeTaskDefinitionSnapshot {{
param($BootTaskName, $WatchdogTaskName, [switch]$RequireEnabled)
return [pscustomobject]@{{ entries = @(
[pscustomobject]@{{ role='boot'; task_name='Boot'; enabled=$script:states.Boot }},
[pscustomobject]@{{ role='watchdog'; task_name='Watchdog'; enabled=$script:states.Watchdog }}
) }}
}}
$snapshot = [pscustomobject]@{{ entries = @(
[pscustomobject]@{{ role='boot'; task_name='Boot'; contract_sha256=('a' * 64); action_execute='one'; action_arguments='one'; action_working_directory='one' }},
[pscustomobject]@{{ role='watchdog'; task_name='Watchdog'; contract_sha256=('b' * 64); action_execute='two'; action_arguments='two'; action_working_directory='two' }}
) }}
try {{
Enable-NewPublicRuntimeTaskDefinitions -DisabledSnapshot $snapshot | Out-Null
exit 2
}} catch {{
if ($script:states.Boot -or $script:states.Watchdog) {{ exit 3 }}
if ($script:taskPaths.Count -lt 3) {{ exit 4 }}
if (@($script:taskPaths | Where-Object {{ $_ -cne '\\' }}).Count -ne 0) {{ exit 5 }}
exit 0
}}
"""
)
def test_operational_definition_drift_disables_both_tasks(self) -> None:
contract = str(CONTRACT).replace("'", "''")
self._assert_harness_passes(
f"""
. '{contract}'
$script:states = @{{ Boot = $false; Watchdog = $false }}
function Assert-PublicRuntimeTaskDefinitionSnapshotCurrent {{ param($ExpectedSnapshot); return $ExpectedSnapshot }}
function Enable-ScheduledTask {{
param($TaskName, $TaskPath, $ErrorAction)
if ($TaskPath -cne '\\') {{ throw 'wrong task path' }}
$script:states[$TaskName] = $true
}}
function Disable-ScheduledTask {{
param($TaskName, $TaskPath, $ErrorAction)
if ($TaskPath -cne '\\') {{ throw 'wrong task path' }}
$script:states[$TaskName] = $false
}}
function Get-PublicRuntimeTaskDefinitionSnapshot {{
param($BootTaskName, $WatchdogTaskName, [switch]$RequireEnabled)
return [pscustomobject]@{{ entries = @(
[pscustomobject]@{{ role='boot'; task_name='Boot'; enabled=$true; contract_sha256=('a' * 64); action_execute='one'; action_arguments='one'; action_working_directory='one' }},
[pscustomobject]@{{ role='watchdog'; task_name='Watchdog'; enabled=$true; contract_sha256=('c' * 64); action_execute='two'; action_arguments='two'; action_working_directory='two' }}
); set_sha256=('d' * 64) }}
}}
$snapshot = [pscustomobject]@{{ entries = @(
[pscustomobject]@{{ role='boot'; task_name='Boot'; contract_sha256=('a' * 64); action_execute='one'; action_arguments='one'; action_working_directory='one' }},
[pscustomobject]@{{ role='watchdog'; task_name='Watchdog'; contract_sha256=('b' * 64); action_execute='two'; action_arguments='two'; action_working_directory='two' }}
) }}
try {{
Enable-NewPublicRuntimeTaskDefinitions -DisabledSnapshot $snapshot | Out-Null
exit 2
}} catch {{
if ($script:states.Boot -or $script:states.Watchdog) {{ exit 3 }}
exit 0
}}
"""
)
if __name__ == "__main__":
unittest.main()