From 1cb633234774fe3eca075daa017804f460083cc7 Mon Sep 17 00:00:00 2001 From: slamhan Date: Wed, 23 Sep 2026 11:44:51 +0800 Subject: [PATCH] fix(lists): follow every page in list commands that read only the first reviews, devices, bundle-ids, profiles (both paths), users, versions, build uploads, Xcode Cloud build runs and diagnostic signatures called Apple without a limit and read one page, so anything past Apple's default page size (50 for most endpoints, 20 for bundle IDs) was dropped. They now request limit=200 and follow every page via APIClient.requestAllPages. Verified live: builds uploads list 50 -> 122, bundle-ids list 20 -> 85; every changed endpoint accepts limit=200. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 1 + .../Builds/SDKBuildUploadRepository.swift | 8 +++-- .../SDKDiagnosticsRepository.swift | 13 ++++--- .../Reviews/SDKCustomerReviewRepository.swift | 10 +++--- .../Apps/Versions/SDKVersionRepository.swift | 8 +++-- .../CodeSigning/SDKBundleIDRepository.swift | 15 ++++---- .../CodeSigning/SDKDeviceRepository.swift | 13 ++++--- .../CodeSigning/SDKProfileRepository.swift | 20 ++++++----- .../Users/SDKUserRepository.swift | 13 ++++--- .../SDKXcodeCloudBuildRunRepository.swift | 8 +++-- .../SDKBuildUploadRepositoryTests.swift | 18 ++++++++++ .../SDKDiagnosticsRepositoryTests.swift | 18 ++++++++++ .../SDKCustomerReviewRepositoryTests.swift | 18 ++++++++++ .../Versions/SDKVersionRepositoryTests.swift | 18 ++++++++++ .../SDKBundleIDRepositoryTests.swift | 18 ++++++++++ .../SDKDeviceRepositoryTests.swift | 18 ++++++++++ .../SDKProfileRepositoryTests.swift | 36 +++++++++++++++++++ .../Users/SDKUserRepositoryTests.swift | 18 ++++++++++ ...SDKXcodeCloudBuildRunRepositoryTests.swift | 18 ++++++++++ 19 files changed, 247 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77274f7f..1898a5f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Subscription price schedules no longer drop manual prices after the first 50** — `subscription-price-schedule get` (and the read-back after `set-batch`) fetched `/v1/subscriptions/{id}/prices` without a `limit`, so only Apple's default first page came back and the other territories were filled with the first price's equalizations, showing wrong prices where custom per-territory prices were set. It now requests `limit=200` and follows the pagination cursor. - **The other per-territory price lists no longer stop at 50** — the same truncation affected `iap-price-schedule get` (manual prices), `iap-offer-codes prices list`, `subscription-offer-codes prices list`, `subscription-promotional-offers prices list` and `win-back-offers prices list`. They called Apple without a `limit` and read one page, so any territory past Apple's default 50 was missing. They now request `limit=200` and follow every page through a shared `APIClient.requestAllPages(_:nextCursor:)` helper, which `subscription-price-schedule get` uses too. - **Offer price lists now show each price's territory and price point** — `iap-offer-codes prices list`, `subscription-offer-codes prices list`, `subscription-promotional-offers prices list` and `win-back-offers prices list` returned entries with only `id` and the offer ID, because Apple sends a price's territory and price point only when the request asks for them with `include=`. The requests now include both, so `territory` and the price-point ID are filled in, over CLI and REST. +- **More list commands no longer stop at Apple's first page** — `reviews list`, `devices list`, `bundle-ids list`, `profiles list` (with and without `--bundle-id-id`), `users list`, `versions list`, `builds uploads list`, `xcode-cloud builds list` and `diagnostics list` read a single page with no `limit`, so anything past Apple's default page size (50 for most endpoints, 20 for bundle IDs) was silently dropped. They now request `limit=200` and follow every page through `APIClient.requestAllPages`. On a real account, `builds uploads list` went from 50 to 122 entries and `bundle-ids list` from 20 to 85. --- diff --git a/Sources/Infrastructure/Apps/Builds/SDKBuildUploadRepository.swift b/Sources/Infrastructure/Apps/Builds/SDKBuildUploadRepository.swift index 18153455..c3c62e42 100644 --- a/Sources/Infrastructure/Apps/Builds/SDKBuildUploadRepository.swift +++ b/Sources/Infrastructure/Apps/Builds/SDKBuildUploadRepository.swift @@ -90,9 +90,11 @@ public struct SDKBuildUploadRepository: BuildUploadRepository, @unchecked Sendab } public func listBuildUploads(appId: String) async throws -> [Domain.BuildUpload] { - let request = APIEndpoint.v1.apps.id(appId).buildUploads.get() - let response = try await client.request(request) - return response.data.map { mapBuildUpload($0, appId: appId) } + let pages = try await client.requestAllPages( + APIEndpoint.v1.apps.id(appId).buildUploads.get(parameters: .init(limit: 200)), + nextCursor: { $0.meta?.paging.nextCursor } + ) + return pages.flatMap(\.data).map { mapBuildUpload($0, appId: appId) } } public func getBuildUpload(id: String) async throws -> Domain.BuildUpload { diff --git a/Sources/Infrastructure/Apps/Performance/SDKDiagnosticsRepository.swift b/Sources/Infrastructure/Apps/Performance/SDKDiagnosticsRepository.swift index 3ffa642c..180f63da 100644 --- a/Sources/Infrastructure/Apps/Performance/SDKDiagnosticsRepository.swift +++ b/Sources/Infrastructure/Apps/Performance/SDKDiagnosticsRepository.swift @@ -10,11 +10,14 @@ public struct SDKDiagnosticsRepository: DiagnosticsRepository, @unchecked Sendab public func listSignatures(buildId: String, diagnosticType: DiagnosticType?) async throws -> [DiagnosticSignatureInfo] { let filterType = diagnosticType.flatMap { mapDiagnosticTypeFilter($0) }.map { [$0] } - let request = APIEndpoint.v1.builds.id(buildId).diagnosticSignatures.get(parameters: .init( - filterDiagnosticType: filterType - )) - let response = try await client.request(request) - return response.data.map { mapSignature($0, buildId: buildId) } + let pages = try await client.requestAllPages( + APIEndpoint.v1.builds.id(buildId).diagnosticSignatures.get(parameters: .init( + filterDiagnosticType: filterType, + limit: 200 + )), + nextCursor: { $0.meta?.paging.nextCursor } + ) + return pages.flatMap(\.data).map { mapSignature($0, buildId: buildId) } } public func listLogs(signatureId: String) async throws -> [DiagnosticLogEntry] { diff --git a/Sources/Infrastructure/Apps/Reviews/SDKCustomerReviewRepository.swift b/Sources/Infrastructure/Apps/Reviews/SDKCustomerReviewRepository.swift index 8df2592b..63025a66 100644 --- a/Sources/Infrastructure/Apps/Reviews/SDKCustomerReviewRepository.swift +++ b/Sources/Infrastructure/Apps/Reviews/SDKCustomerReviewRepository.swift @@ -9,11 +9,13 @@ public struct SDKCustomerReviewRepository: CustomerReviewRepository, @unchecked } public func listReviews(appId: String) async throws -> [Domain.CustomerReview] { - let request = APIEndpoint.v1.apps.id(appId).customerReviews.get( - parameters: .init(sort: [.minuscreatedDate]) + let pages = try await client.requestAllPages( + APIEndpoint.v1.apps.id(appId).customerReviews.get( + parameters: .init(sort: [.minuscreatedDate], limit: 200) + ), + nextCursor: { $0.meta?.paging.nextCursor } ) - let response = try await client.request(request) - return response.data.map { mapReview($0, appId: appId) } + return pages.flatMap(\.data).map { mapReview($0, appId: appId) } } public func getReview(reviewId: String) async throws -> Domain.CustomerReview { diff --git a/Sources/Infrastructure/Apps/Versions/SDKVersionRepository.swift b/Sources/Infrastructure/Apps/Versions/SDKVersionRepository.swift index 82e9212a..aeee8e52 100644 --- a/Sources/Infrastructure/Apps/Versions/SDKVersionRepository.swift +++ b/Sources/Infrastructure/Apps/Versions/SDKVersionRepository.swift @@ -10,9 +10,11 @@ public struct SDKVersionRepository: VersionRepository, @unchecked Sendable { } public func listVersions(appId: String) async throws -> [Domain.AppStoreVersion] { - let request = APIEndpoint.v1.apps.id(appId).appStoreVersions.get() - let response = try await client.request(request) - return response.data.compactMap { mapVersion($0, appId: appId) } + let pages = try await client.requestAllPages( + APIEndpoint.v1.apps.id(appId).appStoreVersions.get(parameters: .init(limit: 200)), + nextCursor: { $0.meta?.paging.nextCursor } + ) + return pages.flatMap(\.data).compactMap { mapVersion($0, appId: appId) } } public func getVersion(id: String) async throws -> Domain.AppStoreVersion { diff --git a/Sources/Infrastructure/CodeSigning/SDKBundleIDRepository.swift b/Sources/Infrastructure/CodeSigning/SDKBundleIDRepository.swift index 87810ac3..b848f8b9 100644 --- a/Sources/Infrastructure/CodeSigning/SDKBundleIDRepository.swift +++ b/Sources/Infrastructure/CodeSigning/SDKBundleIDRepository.swift @@ -12,12 +12,15 @@ public struct SDKBundleIDRepository: BundleIDRepository, @unchecked Sendable { let filterPlatform = platform.flatMap { APIEndpoint.V1.BundleIDs.GetParameters.FilterPlatform(rawValue: $0.rawValue) } - let request = APIEndpoint.v1.bundleIDs.get(parameters: .init( - filterPlatform: filterPlatform.map { [$0] }, - filterIdentifier: identifier.map { [$0] } - )) - let response = try await client.request(request) - return response.data.map(mapBundleID) + let pages = try await client.requestAllPages( + APIEndpoint.v1.bundleIDs.get(parameters: .init( + filterPlatform: filterPlatform.map { [$0] }, + filterIdentifier: identifier.map { [$0] }, + limit: 200 + )), + nextCursor: { $0.meta?.paging.nextCursor } + ) + return pages.flatMap(\.data).map(mapBundleID) } public func createBundleID(name: String, identifier: String, platform: Domain.BundleIDPlatform) async throws -> Domain.BundleID { diff --git a/Sources/Infrastructure/CodeSigning/SDKDeviceRepository.swift b/Sources/Infrastructure/CodeSigning/SDKDeviceRepository.swift index 350a1782..936d37da 100644 --- a/Sources/Infrastructure/CodeSigning/SDKDeviceRepository.swift +++ b/Sources/Infrastructure/CodeSigning/SDKDeviceRepository.swift @@ -12,11 +12,14 @@ public struct SDKDeviceRepository: DeviceRepository, @unchecked Sendable { let filterPlatform = platform.flatMap { APIEndpoint.V1.Devices.GetParameters.FilterPlatform(rawValue: $0.rawValue) } - let request = APIEndpoint.v1.devices.get(parameters: .init( - filterPlatform: filterPlatform.map { [$0] } - )) - let response = try await client.request(request) - return response.data.map(mapDevice) + let pages = try await client.requestAllPages( + APIEndpoint.v1.devices.get(parameters: .init( + filterPlatform: filterPlatform.map { [$0] }, + limit: 200 + )), + nextCursor: { $0.meta?.paging.nextCursor } + ) + return pages.flatMap(\.data).map(mapDevice) } public func registerDevice(name: String, udid: String, platform: Domain.BundleIDPlatform) async throws -> Domain.Device { diff --git a/Sources/Infrastructure/CodeSigning/SDKProfileRepository.swift b/Sources/Infrastructure/CodeSigning/SDKProfileRepository.swift index 89570500..de162b00 100644 --- a/Sources/Infrastructure/CodeSigning/SDKProfileRepository.swift +++ b/Sources/Infrastructure/CodeSigning/SDKProfileRepository.swift @@ -11,19 +11,23 @@ public struct SDKProfileRepository: ProfileRepository, @unchecked Sendable { public func listProfiles(bundleIdId: String?, profileType: Domain.ProfileType?) async throws -> [Domain.Profile] { if let bundleIdId { // Use the bundle ID relationship endpoint for server-side filtering - let response = try await client.request( - APIEndpoint.v1.bundleIDs.id(bundleIdId).profiles.get() + let pages = try await client.requestAllPages( + APIEndpoint.v1.bundleIDs.id(bundleIdId).profiles.get(limit: 200), + nextCursor: { $0.meta?.paging.nextCursor } ) - return response.data.map { mapProfile($0, bundleIdId: bundleIdId) } + return pages.flatMap(\.data).map { mapProfile($0, bundleIdId: bundleIdId) } } else { let filterType = profileType.flatMap { APIEndpoint.V1.Profiles.GetParameters.FilterProfileType(rawValue: $0.rawValue) } - let request = APIEndpoint.v1.profiles.get(parameters: .init( - filterProfileType: filterType.map { [$0] } - )) - let response = try await client.request(request) - return response.data.map { sdkProfile in + let pages = try await client.requestAllPages( + APIEndpoint.v1.profiles.get(parameters: .init( + filterProfileType: filterType.map { [$0] }, + limit: 200 + )), + nextCursor: { $0.meta?.paging.nextCursor } + ) + return pages.flatMap(\.data).map { sdkProfile in let parentId = sdkProfile.relationships?.bundleID?.data?.id ?? "" return mapProfile(sdkProfile, bundleIdId: parentId) } diff --git a/Sources/Infrastructure/Users/SDKUserRepository.swift b/Sources/Infrastructure/Users/SDKUserRepository.swift index 6aea29ad..2ca1edd2 100644 --- a/Sources/Infrastructure/Users/SDKUserRepository.swift +++ b/Sources/Infrastructure/Users/SDKUserRepository.swift @@ -13,11 +13,14 @@ public struct SDKUserRepository: UserRepository, @unchecked Sendable { let filterRole = role.flatMap { APIEndpoint.V1.Users.GetParameters.FilterRoles(rawValue: $0.rawValue) } - let request = APIEndpoint.v1.users.get(parameters: .init( - filterRoles: filterRole.map { [$0] } - )) - let response = try await client.request(request) - return response.data.map(mapTeamMember) + let pages = try await client.requestAllPages( + APIEndpoint.v1.users.get(parameters: .init( + filterRoles: filterRole.map { [$0] }, + limit: 200 + )), + nextCursor: { $0.meta?.paging.nextCursor } + ) + return pages.flatMap(\.data).map(mapTeamMember) } public func updateUser(id: String, roles: [Domain.UserRole]) async throws -> Domain.TeamMember { diff --git a/Sources/Infrastructure/XcodeCloud/SDKXcodeCloudBuildRunRepository.swift b/Sources/Infrastructure/XcodeCloud/SDKXcodeCloudBuildRunRepository.swift index e2509669..f7cd9f03 100644 --- a/Sources/Infrastructure/XcodeCloud/SDKXcodeCloudBuildRunRepository.swift +++ b/Sources/Infrastructure/XcodeCloud/SDKXcodeCloudBuildRunRepository.swift @@ -9,9 +9,11 @@ public struct SDKXcodeCloudBuildRunRepository: XcodeCloudBuildRunRepository, @un } public func listBuildRuns(workflowId: String) async throws -> [XcodeCloudBuildRun] { - let request = APIEndpoint.v1.ciWorkflows.id(workflowId).buildRuns.get() - let response = try await client.request(request) - return response.data.map { mapBuildRun($0, workflowId: workflowId) } + let pages = try await client.requestAllPages( + APIEndpoint.v1.ciWorkflows.id(workflowId).buildRuns.get(parameters: .init(limit: 200)), + nextCursor: { $0.meta?.paging.nextCursor } + ) + return pages.flatMap(\.data).map { mapBuildRun($0, workflowId: workflowId) } } public func getBuildRun(id: String) async throws -> XcodeCloudBuildRun { diff --git a/Tests/InfrastructureTests/Apps/Builds/SDKBuildUploadRepositoryTests.swift b/Tests/InfrastructureTests/Apps/Builds/SDKBuildUploadRepositoryTests.swift index edd2dbfe..8fd20364 100644 --- a/Tests/InfrastructureTests/Apps/Builds/SDKBuildUploadRepositoryTests.swift +++ b/Tests/InfrastructureTests/Apps/Builds/SDKBuildUploadRepositoryTests.swift @@ -126,4 +126,22 @@ struct SDKBuildUploadRepositoryTests { ) ) } + + @Test func `build uploads list includes every upload beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> BuildUploadsResponse { + BuildUploadsResponse( + data: range.map { i in makeSdkBuildUpload(id: "item-\(i)", state: .complete) }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKBuildUploadRepository(client: stub) + let result = try await repo.listBuildUploads(appId: "app-1") + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } } diff --git a/Tests/InfrastructureTests/Apps/Performance/SDKDiagnosticsRepositoryTests.swift b/Tests/InfrastructureTests/Apps/Performance/SDKDiagnosticsRepositoryTests.swift index b29110a8..730fa25d 100644 --- a/Tests/InfrastructureTests/Apps/Performance/SDKDiagnosticsRepositoryTests.swift +++ b/Tests/InfrastructureTests/Apps/Performance/SDKDiagnosticsRepositoryTests.swift @@ -257,4 +257,22 @@ struct SDKDiagnosticsRepositoryTests { #expect(result.isEmpty) } + + @Test func `diagnostic signatures list includes every signature beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> DiagnosticSignaturesResponse { + DiagnosticSignaturesResponse( + data: range.map { i in makeSDKSignature(id: "item-\(i)") }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKDiagnosticsRepository(client: stub) + let result = try await repo.listSignatures(buildId: "build-1", diagnosticType: nil) + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } } diff --git a/Tests/InfrastructureTests/Apps/Reviews/SDKCustomerReviewRepositoryTests.swift b/Tests/InfrastructureTests/Apps/Reviews/SDKCustomerReviewRepositoryTests.swift index 76d1a8b7..a93711ef 100644 --- a/Tests/InfrastructureTests/Apps/Reviews/SDKCustomerReviewRepositoryTests.swift +++ b/Tests/InfrastructureTests/Apps/Reviews/SDKCustomerReviewRepositoryTests.swift @@ -185,4 +185,22 @@ struct SDKCustomerReviewRepositoryDeleteResponseTests { #expect(stub.voidRequestCalled) } + + @Test func `reviews list includes every review beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> CustomerReviewsResponse { + CustomerReviewsResponse( + data: range.map { i in CustomerReview(type: .customerReviews, id: "item-\(i)", attributes: .init(rating: 5)) }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKCustomerReviewRepository(client: stub) + let result = try await repo.listReviews(appId: "app-1") + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } } diff --git a/Tests/InfrastructureTests/Apps/Versions/SDKVersionRepositoryTests.swift b/Tests/InfrastructureTests/Apps/Versions/SDKVersionRepositoryTests.swift index 4abd3d84..07019ba7 100644 --- a/Tests/InfrastructureTests/Apps/Versions/SDKVersionRepositoryTests.swift +++ b/Tests/InfrastructureTests/Apps/Versions/SDKVersionRepositoryTests.swift @@ -129,4 +129,22 @@ struct SDKVersionRepositoryTests { try await repo.setBuild(versionId: "v-1", buildId: "build-42") // No error thrown = success } + + @Test func `versions list includes every version beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> AppStoreVersionsResponse { + AppStoreVersionsResponse( + data: range.map { i in AppStoreVersion(type: .appStoreVersions, id: "item-\(i)", attributes: .init(platform: .ios, versionString: "1.\(i)", appStoreState: .readyForSale)) }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKVersionRepository(client: stub) + let result = try await repo.listVersions(appId: "app-1") + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } } diff --git a/Tests/InfrastructureTests/CodeSigning/SDKBundleIDRepositoryTests.swift b/Tests/InfrastructureTests/CodeSigning/SDKBundleIDRepositoryTests.swift index 80284e9b..405d0c4c 100644 --- a/Tests/InfrastructureTests/CodeSigning/SDKBundleIDRepositoryTests.swift +++ b/Tests/InfrastructureTests/CodeSigning/SDKBundleIDRepositoryTests.swift @@ -55,4 +55,22 @@ struct SDKBundleIDRepositoryTests { #expect(stub.voidRequestCalled == true) } + + @Test func `bundle ids list includes every bundle id beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> BundleIDsResponse { + BundleIDsResponse( + data: range.map { i in AppStoreConnect_Swift_SDK.BundleID(type: .bundleIDs, id: "item-\(i)", attributes: .init(name: "B\(i)", platform: .ios, identifier: "com.example.b\(i)")) }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKBundleIDRepository(client: stub) + let result = try await repo.listBundleIDs(platform: nil, identifier: nil) + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } } diff --git a/Tests/InfrastructureTests/CodeSigning/SDKDeviceRepositoryTests.swift b/Tests/InfrastructureTests/CodeSigning/SDKDeviceRepositoryTests.swift index 0f032ffe..6b5842f0 100644 --- a/Tests/InfrastructureTests/CodeSigning/SDKDeviceRepositoryTests.swift +++ b/Tests/InfrastructureTests/CodeSigning/SDKDeviceRepositoryTests.swift @@ -56,4 +56,22 @@ struct SDKDeviceRepositoryTests { #expect(result[0].isEnabled == false) } + + @Test func `devices list includes every device beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> DevicesResponse { + DevicesResponse( + data: range.map { i in AppStoreConnect_Swift_SDK.Device(type: .devices, id: "item-\(i)", attributes: .init(name: "D\(i)", platform: .ios, udid: "U\(i)", deviceClass: .iphone, status: .enabled)) }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKDeviceRepository(client: stub) + let result = try await repo.listDevices(platform: nil) + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } } diff --git a/Tests/InfrastructureTests/CodeSigning/SDKProfileRepositoryTests.swift b/Tests/InfrastructureTests/CodeSigning/SDKProfileRepositoryTests.swift index c7852456..004c00b3 100644 --- a/Tests/InfrastructureTests/CodeSigning/SDKProfileRepositoryTests.swift +++ b/Tests/InfrastructureTests/CodeSigning/SDKProfileRepositoryTests.swift @@ -59,4 +59,40 @@ struct SDKProfileRepositoryTests { #expect(stub.voidRequestCalled == true) } + + @Test func `profiles list for a bundle id includes every profile beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> ProfilesWithoutIncludesResponse { + ProfilesWithoutIncludesResponse( + data: range.map { i in AppStoreConnect_Swift_SDK.Profile(type: .profiles, id: "item-\(i)", attributes: .init(name: "P\(i)", profileType: .iosAppStore, profileState: .active)) }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKProfileRepository(client: stub) + let result = try await repo.listProfiles(bundleIdId: "bid-1", profileType: nil) + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } + + @Test func `profiles list includes every profile beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> ProfilesResponse { + ProfilesResponse( + data: range.map { i in AppStoreConnect_Swift_SDK.Profile(type: .profiles, id: "item-\(i)", attributes: .init(name: "P\(i)", profileType: .iosAppStore, profileState: .active)) }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKProfileRepository(client: stub) + let result = try await repo.listProfiles(bundleIdId: nil, profileType: nil) + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } } diff --git a/Tests/InfrastructureTests/Users/SDKUserRepositoryTests.swift b/Tests/InfrastructureTests/Users/SDKUserRepositoryTests.swift index fd5ce689..f33f7cde 100644 --- a/Tests/InfrastructureTests/Users/SDKUserRepositoryTests.swift +++ b/Tests/InfrastructureTests/Users/SDKUserRepositoryTests.swift @@ -191,4 +191,22 @@ struct SDKUserRepositoryTests { #expect(result[0].roles == [.admin]) } + + @Test func `users list includes every team member beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> UsersResponse { + UsersResponse( + data: range.map { i in AppStoreConnect_Swift_SDK.User(type: .users, id: "item-\(i)", attributes: .init(username: "u\(i)@example.com", roles: [.developer])) }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKUserRepository(client: stub) + let result = try await repo.listUsers(role: nil) + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } } diff --git a/Tests/InfrastructureTests/XcodeCloud/SDKXcodeCloudBuildRunRepositoryTests.swift b/Tests/InfrastructureTests/XcodeCloud/SDKXcodeCloudBuildRunRepositoryTests.swift index 15e03f00..82a8321b 100644 --- a/Tests/InfrastructureTests/XcodeCloud/SDKXcodeCloudBuildRunRepositoryTests.swift +++ b/Tests/InfrastructureTests/XcodeCloud/SDKXcodeCloudBuildRunRepositoryTests.swift @@ -84,4 +84,22 @@ struct SDKXcodeCloudBuildRunRepositoryTests { #expect(result.executionProgress == .pending) #expect(result.workflowId == "wf-1") } + + @Test func `build runs list includes every run beyond the first page`() async throws { + func page(_ range: Range, nextCursor: String?) -> CiBuildRunsResponse { + CiBuildRunsResponse( + data: range.map { i in makeSDKBuildRun(id: "item-\(i)") }, + links: .init(this: ""), + meta: .init(paging: .init(total: 75, limit: 50, nextCursor: nextCursor)) + ) + } + let stub = StubAPIClient() + stub.willReturnPages([page(0..<50, nextCursor: "page-2"), page(50..<75, nextCursor: nil)]) + + let repo = SDKXcodeCloudBuildRunRepository(client: stub) + let result = try await repo.listBuildRuns(workflowId: "wf-1") + + #expect(result.count == 75) + #expect(result.last?.id == "item-74") + } }