# Azure Backup (MARS agent) collector, against collectors/mars/tests/MockMars.ps1 (shapes from a # real agent, 2.0.9274: job times are UTC values with no UTC marking, ErrorInfo is always present). BeforeAll { Import-Module (Join-Path $PSScriptRoot 'TestHelpers.psm1') -Force $now = Set-MockNow $config = @{ ApiUrl = 'http://localhost:1'; ApiKey = 'bdk_x'; Tenant = 'Acme' } # MARS reports job times in UTC; the API should get them with +00:00 function Utc([datetime]$Local) { ConvertTo-ApiTime $Local.ToUniversalTime() } } Describe 'MARS collector' { Context 'what it reports (dry run)' { BeforeAll { $r = Invoke-Collector mars -Config $config -DryRun $runs = @($r.Reports | Where-Object run | ForEach-Object run) $byStart = @{} foreach ($run in $runs) { $byStart[$run.started_at] = $run } } It 'runs cleanly' { $r.ExitCode | Should -Be 0 $r.Log[-1] | Should -Be '[INFO] Reported 4 runs, 0 failures' } It 'reports the server as one job, named after it' { $r.Reports.job.external_id | Select-Object -Unique | Should -Be 'mars:srv01' $r.Reports.job.name | Select-Object -Unique | Should -Be 'SRV01 - Azure Backup' $r.Reports.source_system | Select-Object -Unique | Should -Be 'Azure Backup (MARS)' } It 'computes the next run from the schedule (daily at 21:00)' { $next = (Get-Date).Date.AddHours(21) if ((Get-Date) -ge $next) { $next = $next.AddDays(1) } $r.Reports[0].job.next_run_at | Should -Be (ConvertTo-ApiTime $next) } It 'treats job times as UTC (MARS gives UTC values without marking them)' { $byStart.Keys | Should -Contain (Utc $now.AddHours(-12)) $byStart[(Utc $now.AddHours(-12))].finished_at | Should -Be (Utc $now.AddHours(-11.5)) } It 'reports a completed backup as success, despite its (always present) ErrorInfo with code 0' { $run = $byStart[(Utc $now.AddHours(-12))] $run.status | Should -Be 'success' $run.message | Should -Be 'Backup completed successfully.' $run.size_bytes | Should -Be 523407872 $run.details.datasources | Should -Be @('E:\') } It 'reports a completed backup with failed files as a warning, with the count and log' { $run = $byStart[(Utc $now.AddHours(-36))] $run.status | Should -Be 'warning' $run.message | Should -BeLike '*E:\: 12 file(s) failed to back up*' $run.message | Should -BeLike '*Failed file log: C:\Program Files\Microsoft Azure Recovery Services Agent\Temp\Errors.log*' $run.details.failed_files | Should -Be 12 $run.details.failed_bytes | Should -Be 40MB } It 'reports a failed backup with its error code and parameters' { $run = $byStart[(Utc $now.AddHours(-30))] $run.status | Should -Be 'failed' $run.message | Should -BeLike '*Error code 100050 (Volume=E:)*' } It 'reports a backup in progress as running, without a finish time' { $run = $byStart[(Utc $now.AddMinutes(-10))] $run.status | Should -Be 'running' $run.PSObject.Properties.Name | Should -Not -Contain 'finished_at' } It 'ignores restores and backups outside the lookback window' { $runs.Count | Should -Be 4 } It 'uses JobName from the config when set' { $named = Invoke-Collector mars -Config ($config + @{ JobName = 'File Server' }) -DryRun $named.Reports.job.name | Select-Object -Unique | Should -Be 'File Server' $named.Reports.job.external_id | Select-Object -Unique | Should -Be 'mars:srv01' } } Context 'sending to the API' { BeforeAll { $stub = Start-ApiStub $live = $config.Clone(); $live.ApiUrl = $stub.Url $first = Invoke-Collector mars -Config $live $firstRequests = Get-StubRequests $stub $second = Invoke-Collector mars -Config $live -WorkDir $first.WorkDir $secondRequests = Get-StubRequests $stub } AfterAll { Stop-ApiStub $stub } It 'sends every run on the first run' { $first.ExitCode | Should -Be 0 $firstRequests.Count | Should -Be 4 } It 'on the next run, resends only the backup in progress' { $first.State.Count | Should -Be 3 $secondRequests.Count | Should -Be 1 $secondRequests[0].Payload.run.status | Should -Be 'running' } } }