Skip to content

test(apiv2): cover the v2 paths the migration left untested - #401

Merged
Quick104 merged 1 commit into
mainfrom
t3code/apiv2-test-gaps
Sep 23, 2026
Merged

Quick104 merged 1 commit into
mainfrom
t3code/apiv2-test-gaps

Conversation

@Quick104

@Quick104 Quick104 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Tests now cover the v2 code paths the /api/v2 migration left untested, and two leftover v1 fallbacks are gone.

Problem

An audit of the test suite after the v2 migration (#349–#398) found tests that still exercised v1-era code while the live v2 path had no test:

  • Image size. ImageSizeCapabilityTests and ArtworkURLTests used only v1-shaped payloads (schema_version: 1, no state). The v2 branch in ImageSizeSelection.queryEntries (state == "available") had no test, so breaking it would have sent image_size=large to servers that don't offer it. v2 never sends schema_version, so no server could reach the v1 branch.
  • Item videos. ItemVideosDecodingTests decoded ItemDetail through its hand-written init(from:), which no production read calls. SiloAPI.itemDetail builds ItemDetail through ItemDetail(catalog:), and ItemVideo(catalog:) had no test. DetailVersionSelectionTests tested a legacy FileVersion.edition fallback, but the v2 projection always sets edition to nil.
  • Playback start. PlaybackProtocolV3Tests encoded PlaybackV3StartRequest with a test-owned encoder. Production sends APIv2PlaybackStartBody, so a missing or extra member in the real body would pass.
  • Pasted fixtures. AuthDeviceV2Tests, PairingDeviceAPITests and ApplePushRegistrationTests carried hand-pasted copies of server fixtures that the sync script doesn't update. The device-login capability copy had already drifted: "revision": "1", where the server sends a hash. The tests also patched these strings with replacingOccurrences, which does nothing without warning if the server reformats a fixture.
  • Pairing wire format. PairingProtocolTests only round-tripped messages through one codec, so a key renamed on both sides would pass while breaking older phones and TVs and the Android client.

Solution

  • Image size. ImageSizeCapabilityResponse drops schemaVersion, and state is now required. queryEntries accepts only state == "available". The capability is held only in memory, so no stored copy can lack state. Tests decode the vendored get_image_capabilities_ok fixture with the production decoder. They cover every server state (disabled, not_configured, unsupported, and an unknown value) and assert that a v1 document is rejected. ArtworkURLTests now expects that rejection.
  • Item videos. Tests start from the vendored get_catalog_item_ok fixture, add videos and extras, and run it through CatalogItemDetail decoding and ItemDetail(catalog:), the same path SiloAPI.itemDetail uses. A new case checks that a video without the schema-required is_official fails the read. The trailer-refresh "queued" case reads the vendored refresh_catalog_item_trailers_ok fixture.
  • Edition fallback. Removed FileVersion.edition and its fallback in editionDisplayLabel. The edition tests now use edition_raw and edition_key. OverlaySummary.edition is a different field and is unchanged.
  • Playback start. Removed the two tests that encoded the unsent type. PlaybackSessionTransportTests now checks the body startPlayback actually sends against PlaybackStartBody and ClientPlaybackContextV3 from the server's OpenAPI document at the pinned commit. Every required member must be present and no member outside the schema may appear, because both schemas set additionalProperties: false. It also checks that an explicit zero start_position is sent.
  • Fixtures. Added 9 fixtures to SELECTED in scripts/sync-apiv2-fixtures.sh and re-synced at the same server commit, 84ed9e596. The re-sync only added files and left the existing fixtures byte-identical. The 3 test files load these instead of pasted copies. The new APIv2FixtureTestSupport.text(named:bundleClass:setting:) overrides top-level members on the parsed JSON rather than editing raw text.
  • Pairing. Added literal wire frames for every message type, checked in both directions. The keys match silo-android's PairingMessageCodec.
  • Updated the vendored-fixture count in APIv2ContractTests from 34 to 43.

/api/v1 sweep: the only v1 request left is the unauthenticated GET /api/v1/health probe, which the 2026-09-07 decision recorded in silo-server's contracts/api/v2/migration.json keeps. scripts/ci/check-no-api-v1.sh passes unchanged.

Validation

  • Full iOS suite (SiloTests, iPhone 17 Pro, iOS 27.0 simulator): 1979 tests, 0 failures, 2 skipped.
  • Full tvOS suite (SiloTVTests, Apple TV 4K (3rd generation), tvOS 27.0 simulator): 1790 tests, 0 failures, 2 skipped. The SiloTV build includes the Top Shelf extension, which shares ImageSizeCapabilityResponse.
  • Both runs used CI's flags: ad-hoc simulator signing, -parallel-testing-enabled NO, and a 60 s per-test allowance.
  • I checked that the new tests catch real bugs by breaking the code on purpose, then restoring it. Forcing isOfficial to false, renaming the start body's installation_id key, and accepting any non-empty image state together caused 8 test failures across ItemVideosDecodingTests, PlaybackSessionTransportTests and ImageSizeCapabilityTests.
  • bash scripts/ci/check-no-api-v1.test.sh, bash scripts/ci/check-no-api-v1.sh and bash scripts/sync-apiv2-fixtures.test.sh pass.

Validation tasks: none

Risks and follow-ups

  • Behavior change: a server that answers /api/v2/images/capabilities without state no longer gets image_size=large. Every v2 server sends state.
  • PlaybackSessionTransportTests copies the start-body member lists from the OpenAPI document at the pinned commit. When the fixtures are re-synced at a newer server commit, these lists need a manual update.
  • The hand-written Decodable initializers for ItemDetail and FileVersion in Models.swift look unused now that reads go through the v2 projection. Removing them is a separate refactor and isn't part of this PR.

Code and description written by Claude Opus 5.5 (claude-opus-5-5) in Claude Code (T3 Code). Claude Opus 5.5 subagents audited the test suite to find these gaps.

🤖 Generated with Claude Code

Several tests still exercised v1-era code after the move to /api/v2, so
the live v2 paths had no coverage:

- Image size: tests used only v1 `schema_version` payloads; the v2
  `state` branch was untested. Drop the unreachable v1 branch and test
  the vendored v2 fixture.
- Item videos: tests decoded a path production no longer uses. Test
  `ItemDetail(catalog:)` on the vendored v2 item fixture instead, and
  drop the always-nil legacy `FileVersion.edition` fallback.
- Playback start: tests encoded `PlaybackV3StartRequest`, not the body
  production sends. Check the sent `APIv2PlaybackStartBody` members
  against the server's OpenAPI schema instead.
- Auth, pairing and push tests pasted server fixtures by hand, and one
  had drifted. Vendor them through sync-apiv2-fixtures.sh at the same
  server commit.
- Pairing: pin literal wire frames so a key renamed on both sides of
  the codec fails.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 45d8f210-2cfa-4081-bbf8-f53993c60faf

📥 Commits

Reviewing files that changed from the base of the PR and between b0a9fd5 and 4363bb6.

📒 Files selected for processing (28)
  • iosApp/Tests/APIv2ContractTests.swift
  • iosApp/Tests/APIv2FixtureTestSupport.swift
  • iosApp/Tests/ApplePushRegistrationTests.swift
  • iosApp/Tests/ArtworkURLTests.swift
  • iosApp/Tests/AuthDeviceV2Tests.swift
  • iosApp/Tests/DetailVersionSelectionTests.swift
  • iosApp/Tests/Fixtures/APIv2/SOURCE
  • iosApp/Tests/Fixtures/APIv2/get_catalog_item_ok.json
  • iosApp/Tests/Fixtures/APIv2/get_device_login_capability_ok.json
  • iosApp/Tests/Fixtures/APIv2/get_device_login_ok.json
  • iosApp/Tests/Fixtures/APIv2/get_image_capabilities_ok.json
  • iosApp/Tests/Fixtures/APIv2/index.json
  • iosApp/Tests/Fixtures/APIv2/login_ok.json
  • iosApp/Tests/Fixtures/APIv2/notification_apple_push_display.json
  • iosApp/Tests/Fixtures/APIv2/poll_device_login_ok.json
  • iosApp/Tests/Fixtures/APIv2/refresh_catalog_item_trailers_ok.json
  • iosApp/Tests/Fixtures/APIv2/start_device_login_ok.json
  • iosApp/Tests/ImageSizeCapabilityTests.swift
  • iosApp/Tests/ItemVideosDecodingTests.swift
  • iosApp/Tests/PairingDeviceAPITests.swift
  • iosApp/Tests/PairingProtocolTests.swift
  • iosApp/Tests/PlaybackProtocolV3Tests.swift
  • iosApp/Tests/PlaybackSessionTransportTests.swift
  • iosApp/iosApp/Networking/APIv2/APIv2CatalogReadProjection.swift
  • iosApp/iosApp/Networking/Models.swift
  • iosApp/iosApp/Screens/Detail/PlaybackEditions.swift
  • iosApp/iosApp/Shared/ImageSizeSelection.swift
  • scripts/sync-apiv2-fixtures.sh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick104 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@Quick104
Quick104 merged commit f4dc8de into main Sep 23, 2026
7 checks passed
@Quick104
Quick104 deleted the t3code/apiv2-test-gaps branch September 23, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant