fix(api): isUpdateAvailable returns null for all containers when cache has an undef entry#2030
Conversation
Walkthrough
ChangesDocker Cache Parsing Robustness
OIDC Integration Test Timeout
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
isUpdateAvailable resolved to null for every container while
containerUpdateStatuses (a separate code path) reported correct values.
The webgui writes null digests and status:'undef' (surfaced as
UNKNOWN) for stopped or locally-built containers in
/var/lib/docker/unraid-update-status.json. The API validated the whole
file with z.record, so a single such entry failed validation for the
entire file, making readCachedUpdateStatus return {} and every
container's isUpdateAvailable resolve to null.
Loosen the entry schema to the shapes the webgui actually writes and
parse per-entry, skipping only malformed entries instead of discarding
the whole cache.
FeatureOS: https://product.unraid.net/p/docker-graphql-isupdateavailable-always-returns-null
29bda93 to
f285873
Compare
The test hits a real network endpoint (expired-cert.badssl.com) and can exceed the 5s default in CI. Bump to 20s for headroom.
A different network-dependent test in the suite timed out at 5s in CI after the per-test bump. These integration tests all make real outbound calls (discovery, SSL/DNS probes), so raise the timeout for the whole suite instead of one test.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
api/src/unraid-api/graph/resolvers/docker/docker-php.service.spec.ts (1)
14-14: ⚡ Quick winRemove unnecessary type assertion.
The
as nevercast suppresses all type checking. SinceJSON.stringify(data)returns astringandmockResolvedValueacceptsstring | Buffer, the cast should be unnecessary. As per coding guidelines, avoid using casting whenever possible—prefer proper typing from the start.♻️ Proposed fix
-const writeCache = (data: unknown) => mockReadFile.mockResolvedValue(JSON.stringify(data) as never); +const writeCache = (data: unknown) => mockReadFile.mockResolvedValue(JSON.stringify(data));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@api/src/unraid-api/graph/resolvers/docker/docker-php.service.spec.ts` at line 14, In the writeCache function, remove the `as never` type assertion from the `mockReadFile.mockResolvedValue()` call. Since `JSON.stringify(data)` already returns a string and `mockResolvedValue` is typed to accept string or Buffer, the unnecessary cast is suppressing valid type checking. Delete the `as never` portion so the function relies on proper typing without explicit type assertions.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@api/src/unraid-api/graph/resolvers/docker/docker-php.service.spec.ts`:
- Line 14: In the writeCache function, remove the `as never` type assertion from
the `mockReadFile.mockResolvedValue()` call. Since `JSON.stringify(data)`
already returns a string and `mockResolvedValue` is typed to accept string or
Buffer, the unnecessary cast is suppressing valid type checking. Delete the `as
never` portion so the function relies on proper typing without explicit type
assertions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: c157c0ff-c130-4f6b-83d1-3565e183da75
📒 Files selected for processing (3)
api/src/unraid-api/graph/resolvers/docker/docker-php.service.spec.tsapi/src/unraid-api/graph/resolvers/docker/docker-php.service.tsapi/src/unraid-api/graph/resolvers/sso/core/oidc.service.integration.test.ts
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2030 +/- ##
==========================================
+ Coverage 52.63% 52.69% +0.05%
==========================================
Files 1035 1035
Lines 72034 72044 +10
Branches 8248 8276 +28
==========================================
+ Hits 37917 37965 +48
+ Misses 33991 33953 -38
Partials 126 126 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
This plugin has been deployed to Cloudflare R2 and is available for testing. |
🔄 PR Merged - Plugin Redirected to StagingThis PR has been merged and the preview plugin has been updated to redirect to the staging version. For users testing this PR:
Staging URL: Thank you for testing! 🚀 |
🤖 I have created a release *beep* *boop* --- ## [4.35.1](v4.35.0...v4.35.1) (2026-06-18) ### Bug Fixes * **api:** isUpdateAvailable returns null for all containers when cache has an undef entry ([#2030](#2030)) ([b166407](b166407)) * **api:** keep plugin config loads side-effect free ([#2025](#2025)) ([3c84326](3c84326)) * **plugin:** preserve Connect during install start ([#2027](#2027)) ([6f94aa1](6f94aa1)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
docker.containers[].isUpdateAvailableresolves tonullfor every container, even thoughcontainerUpdateStatuses(a separate code path) reports the correctUP_TO_DATE/UPDATE_AVAILABLE/UNKNOWNvalues for the same containers.Root cause
isUpdateAvailablereads/var/lib/docker/unraid-update-status.json, which the webgui (DockerClient.php) writes — not the API. For stopped or locally-built containers the webgui legitimately writeslocal/remoteasnullandstatusas the string'undef'(surfaced elsewhere asUNKNOWN).The API validated the whole file with
z.record(...), which fails wholesale if any single value fails. The entry schema required non-nulllocal/remotestrings andstatusof exactly'true' | 'false', so a singleundef/nullentry rejected the entire file →readCachedUpdateStatus()returned{}→ every container reportednull, including ones that actually had updates.This matches the report exactly: the failing response contained several
UNKNOWN(undef) entries, which poisoned the parse for all rows.Fix
CachedStatusEntrySchemato match what the webgui actually writes (local/remotenullable+optional,statusan optional nullable string so'undef'is accepted).Downstream logic in
isUpdateAvailableCachedalready handled these shapes (it filtersundefdigests and treats any non-true/falsestatus as unknown), so it just needed to actually receive the data.Tests
Added
docker-php.service.spec.tscovering the regression (a valid entry survives a malformed sibling), per-entry dropping of bad entries, and the manifest behavior. Full API suite + type-check pass.Links
Fixes OS-381
Summary by CodeRabbit
Bug Fixes
Tests