Skip to content

[BUG] Scan status chips show Succeeded and Partial without color on list pages #274

Open

Description

@LarsLaskowski

Summary

Several pages color scan status chips with their own private copy of GetScanStatusColor. Four of these copies map success to Completed, a status that does not exist (ScanRunStatus is NotSet, Pending, Running, Succeeded, Partial, Failed), and they have no entry for Succeeded or Partial. On the Dashboard, Scan History, Observed Images and My Images pages, the two most common outcomes, a successful run and a partially successful run, therefore appear as grey default chips that look the same as unknown states. The seven copies have also drifted apart: the detail pages already handle Succeeded/Partial.

Area

Current behavior

The scan status strings come from ScanRunStatus.ToString() (src/DockerUpdateGuard/UI/ApplicationViewService.cs: GetLatestObservedScanStatus / GetLatestRuntimeScanStatus around lines 1497-1528 return entity.Status.ToString() or "NotScanned"; scan history items use scanRun.Status.ToString(), around line 1436). Possible values are therefore Pending, Running, Succeeded, Partial, Failed, NotSet and NotScanned. Completed is never produced.

There are seven independent mappings:

File Maps Succeeded Maps Partial Maps dead Completed
src/DockerUpdateGuard/Components/Pages/Dashboard.razor.cs:79 (chip at Dashboard.razor:237, recent scans) no no yes
Components/Pages/ScanHistory.razor.cs:107 (chip at ScanHistory.razor:74) no no yes
Components/Pages/ObservedImages.razor.cs:102 (chip at ObservedImages.razor:105) no no yes
Components/Pages/MyImages.razor.cs:94 (chip at MyImages.razor:50) no no yes
Components/Pages/ObservedImageDetail.razor.cs:71 yes yes yes
Components/Pages/MyImageDetail.razor.cs:71 yes yes yes
Components/Pages/RuntimeContainerDetail.razor.cs:116 yes yes yes

The list-page variant:

// src/DockerUpdateGuard/Components/Pages/ScanHistory.razor.cs:107-117
return status.ToUpperInvariant() switch
       {
           "COMPLETED" => Color.Success,
           "FAILED" => Color.Error,
           "RUNNING" => Color.Info,
           "PENDING" => Color.Warning,
           _ => Color.Default,
       };

The existing tests pin the dead value: src/Tests/DockerUpdateGuard.Tests/MyImagesTests.cs:75-77 and MyImageDetailTests.cs:43-44 ([DataRow("Completed", Color.Success)]).

Reproduction: run any scheduled or manual scan that succeeds and open / or /scan-history. The Succeeded chip is grey (Color.Default). A vulnerability refresh where one image is Unavailable ends Partial, and its chip is grey as well. Open the observed image detail page of the same image, and the chip is green or orange.

Expected behavior

  1. Decision (product manager): Succeeded is shown in the success color and Partial in the warning color.
  2. The other colors stay as they are: Failed → error, Running → info, Pending → warning. Anything else (NotSet, NotScanned, unknown) → default. Matching is case-insensitive and null-safe.
  3. Decision (product manager): there is one shared mapping, used by every scan status chip: Dashboard, Scan History, Observed Images, My Images, Observed Image detail, My Image detail and Runtime Container detail. The page-local copies are removed.
  4. The non-existent Completed status is no longer mapped.

Acceptance criteria

  • A single internal static helper, for example ScanStatusDisplayFormatter.GetStatusColor(string? status) in src/DockerUpdateGuard/UI/ next to VulnerabilityDisplayFormatter, implements the mapping.
  • All seven pages use the shared helper, and no GetScanStatusColor method remains in Components/Pages/*.razor.cs.
  • New ScanStatusDisplayFormatterTests (MSTest [DataRow], naming per docs/UNIT_TESTS.md, for example ScanStatusDisplayFormatterGetStatusColorReturnsExpectedColor) cover:
    • Succeeded → Color.Success
    • Partial → Color.Warning
    • Failed → Color.Error
    • Running → Color.Info
    • Pending → Color.Warning
    • NotSet, NotScanned, Completed, empty and null → Color.Default
    • case-insensitivity
  • A test proves that the mapping is complete for the enum: every ScanRunStatus value except NotSet maps to a non-default color (iterate Enum.GetValues<ScanRunStatus>()), so a future status cannot silently fall back to grey.
  • The obsolete Completed data rows in MyImagesTests and MyImageDetailTests are removed or moved to the new test class. Test data using Status = "Completed" in ScanHistoryTests / ScanHistoryPersistentStateTests is changed to a real status value.
  • docs/areas/web-ui.md describes the scan status chip colors (for example in General or next to Pages and routes), and its Known gaps / open-question entry for this issue is removed.
  • Format, build, tests, analyzer gate and coverage gate pass (.squad/stack.md).

Implementation notes

  • Keep the helper internal static, as VulnerabilityDisplayFormatter is. The test project already has access to internals.
  • Prefer comparing against nameof(ScanRunStatus.Succeeded) etc., or Enum.TryParse<ScanRunStatus>(status, ignoreCase: true, …), instead of string literals, so a renamed enum member breaks the build rather than the colors.
  • The Docker Instances page shows the latest runtime scan status as plain text (DockerInstances.razor:135), not as a chip. Leave it as it is unless it becomes a chip in this change.
  • Do not change the displayed text of the chips, only their color.
  • This is purely presentational: no configuration, migration or service change.

Related


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions