|
10 | 10 | //
|
11 | 11 | //===----------------------------------------------------------------------===//
|
12 | 12 |
|
13 |
| -import Basics |
| 13 | +@testable import Basics |
14 | 14 | import _Concurrency
|
15 | 15 | import Foundation
|
16 | 16 | import PackageFingerprint
|
@@ -440,6 +440,126 @@ fileprivate var availabilityURL = URL("\(registryURL)/availability")
|
440 | 440 | assert(metadataSync)
|
441 | 441 | }
|
442 | 442 |
|
| 443 | + @Test func getPackageVersionMetadataInCache() async throws { |
| 444 | + let checksumAlgorithm: HashAlgorithm = MockHashAlgorithm() |
| 445 | + let expectedChecksums: [Version: String] = [ |
| 446 | + Version("1.1.1"): "a2ac54cf25fbc1ad0028f03f0aa4b96833b83bb05a14e510892bb27dea4dc812", |
| 447 | + Version("1.1.0"): checksumAlgorithm.hash(emptyZipFile).hexadecimalRepresentation |
| 448 | + ] |
| 449 | + |
| 450 | + let counter = SendableBox(0) |
| 451 | + let handler: HTTPClient.Implementation = { request, _ in |
| 452 | + await counter.increment() |
| 453 | + switch (request.method, request.url) { |
| 454 | + case (.get, releasesURL.appending(component: "1.1.1")): |
| 455 | + let expectedChecksum = expectedChecksums[Version("1.1.1")]! |
| 456 | + #expect(request.headers.get("Accept").first == "application/vnd.swift.registry.v1+json") |
| 457 | + |
| 458 | + let data = """ |
| 459 | + { |
| 460 | + "id": "mona.LinkedList", |
| 461 | + "version": "1.1.1", |
| 462 | + "resources": [ |
| 463 | + { |
| 464 | + "name": "source-archive", |
| 465 | + "type": "application/zip", |
| 466 | + "checksum": "\(expectedChecksum)" |
| 467 | + } |
| 468 | + ], |
| 469 | + "metadata": { |
| 470 | + "author": { |
| 471 | + "name": "J. Appleseed" |
| 472 | + }, |
| 473 | + "licenseURL": "https://github.com/mona/LinkedList/license", |
| 474 | + "readmeURL": "https://github.com/mona/LinkedList/readme", |
| 475 | + "repositoryURLs": [ |
| 476 | + "https://github.com/mona/LinkedList", |
| 477 | + "ssh://git@github.com:mona/LinkedList.git", |
| 478 | + "git@github.com:mona/LinkedList.git" |
| 479 | + ] |
| 480 | + } |
| 481 | + } |
| 482 | + """.data(using: .utf8)! |
| 483 | + |
| 484 | + return .init( |
| 485 | + statusCode: 200, |
| 486 | + headers: .init([ |
| 487 | + .init(name: "Content-Length", value: "\(data.count)"), |
| 488 | + .init(name: "Content-Type", value: "application/json"), |
| 489 | + .init(name: "Content-Version", value: "1"), |
| 490 | + ]), |
| 491 | + body: data |
| 492 | + ) |
| 493 | + case (.get, releasesURL.appending(component: "1.1.0")): |
| 494 | + let expectedChecksum = expectedChecksums[Version("1.1.0")]! |
| 495 | + #expect(request.headers.get("Accept").first == "application/vnd.swift.registry.v1+json") |
| 496 | + |
| 497 | + let data = """ |
| 498 | + { |
| 499 | + "id": "mona.LinkedList", |
| 500 | + "version": "1.1.0", |
| 501 | + "resources": [ |
| 502 | + { |
| 503 | + "name": "source-archive", |
| 504 | + "type": "application/zip", |
| 505 | + "checksum": "\(expectedChecksum)", |
| 506 | + } |
| 507 | + ], |
| 508 | + "metadata": { |
| 509 | + "author": { |
| 510 | + "name": "J. Appleseed" |
| 511 | + }, |
| 512 | + "licenseURL": "https://github.com/mona/LinkedList/license", |
| 513 | + "readmeURL": "https://github.com/mona/LinkedList/readme", |
| 514 | + "repositoryURLs": [ |
| 515 | + "https://github.com/mona/LinkedList", |
| 516 | + "ssh://git@github.com:mona/LinkedList.git", |
| 517 | + "git@github.com:mona/LinkedList.git" |
| 518 | + ] |
| 519 | + } |
| 520 | + } |
| 521 | + """.data(using: .utf8)! |
| 522 | + |
| 523 | + return .init( |
| 524 | + statusCode: 200, |
| 525 | + headers: .init([ |
| 526 | + .init(name: "Content-Length", value: "\(data.count)"), |
| 527 | + .init(name: "Content-Type", value: "application/json"), |
| 528 | + .init(name: "Content-Version", value: "1"), |
| 529 | + ]), |
| 530 | + body: data |
| 531 | + ) |
| 532 | + default: |
| 533 | + throw StringError("method and url should match") |
| 534 | + } |
| 535 | + } |
| 536 | + |
| 537 | + let httpClient = HTTPClient(implementation: handler) |
| 538 | + var configuration = RegistryConfiguration() |
| 539 | + configuration.defaultRegistry = Registry(url: registryURL, supportsAvailability: false) |
| 540 | + |
| 541 | + let registryClient = makeRegistryClient(configuration: configuration, httpClient: httpClient) |
| 542 | + |
| 543 | + var expectedRequestCount = 0 |
| 544 | + try await check(version: Version("1.1.1"), expectCached: false) |
| 545 | + try await check(version: Version("1.1.0"), expectCached: false) |
| 546 | + try await check(version: Version("1.1.1"), expectCached: true) |
| 547 | + try await check(version: Version("1.1.0"), expectCached: true) |
| 548 | + |
| 549 | + func check(version: Version, expectCached: Bool) async throws { |
| 550 | + let metadata = try await registryClient.getPackageVersionMetadata(package: identity, version: version) |
| 551 | + |
| 552 | + if !expectCached { |
| 553 | + expectedRequestCount += 1 |
| 554 | + } |
| 555 | + |
| 556 | + let count = await counter.value |
| 557 | + #expect(count == expectedRequestCount) |
| 558 | + #expect(metadata.author?.name == "J. Appleseed") |
| 559 | + #expect(metadata.resources[0].checksum == expectedChecksums[version]!) |
| 560 | + } |
| 561 | + } |
| 562 | + |
443 | 563 | func getPackageVersionMetadata_404() async throws {
|
444 | 564 | let serverErrorHandler = ServerErrorHandler(
|
445 | 565 | method: .get,
|
@@ -3701,6 +3821,87 @@ fileprivate var availabilityURL = URL("\(registryURL)/availability")
|
3701 | 3821 | try await registryClient.checkAvailability(registry: registry)
|
3702 | 3822 | }
|
3703 | 3823 | }
|
| 3824 | + |
| 3825 | + @Test func withAvailabilityCheck() async throws { |
| 3826 | + let handler: HTTPClient.Implementation = { request, _ in |
| 3827 | + switch (request.method, request.url) { |
| 3828 | + case (.get, availabilityURL): |
| 3829 | + return .okay() |
| 3830 | + default: |
| 3831 | + throw StringError("method and url should match") |
| 3832 | + } |
| 3833 | + } |
| 3834 | + |
| 3835 | + let httpClient = HTTPClient(implementation: handler) |
| 3836 | + let registry = Registry(url: registryURL, supportsAvailability: true) |
| 3837 | + |
| 3838 | + let registryClient = makeRegistryClient( |
| 3839 | + configuration: .init(), |
| 3840 | + httpClient: httpClient |
| 3841 | + ) |
| 3842 | + |
| 3843 | + try await registryClient.withAvailabilityCheck( |
| 3844 | + registry: registry, |
| 3845 | + observabilityScope: ObservabilitySystem.NOOP |
| 3846 | + ) |
| 3847 | + } |
| 3848 | + |
| 3849 | + @Test func withAvailabilityCheckServerError() async throws { |
| 3850 | + let handler: HTTPClient.Implementation = { request, _ in |
| 3851 | + switch (request.method, request.url) { |
| 3852 | + case (.get, availabilityURL): |
| 3853 | + return .serverError(reason: "boom") |
| 3854 | + default: |
| 3855 | + throw StringError("method and url should match") |
| 3856 | + } |
| 3857 | + } |
| 3858 | + |
| 3859 | + let httpClient = HTTPClient(implementation: handler) |
| 3860 | + let registry = Registry(url: registryURL, supportsAvailability: true) |
| 3861 | + |
| 3862 | + let registryClient = makeRegistryClient( |
| 3863 | + configuration: .init(), |
| 3864 | + httpClient: httpClient |
| 3865 | + ) |
| 3866 | + |
| 3867 | + await #expect(throws: StringError("unknown server error (500)")) { |
| 3868 | + try await registryClient.withAvailabilityCheck( |
| 3869 | + registry: registry, |
| 3870 | + observabilityScope: ObservabilitySystem.NOOP |
| 3871 | + ) |
| 3872 | + } |
| 3873 | + } |
| 3874 | + |
| 3875 | + @Test func withAvailabilityCheckInCache() async throws { |
| 3876 | + let counter = SendableBox(0) |
| 3877 | + let handler: HTTPClient.Implementation = { request, _ in |
| 3878 | + await counter.increment() |
| 3879 | + switch (request.method, request.url) { |
| 3880 | + case (.get, availabilityURL): |
| 3881 | + return .okay() |
| 3882 | + default: |
| 3883 | + throw StringError("method and url should match") |
| 3884 | + } |
| 3885 | + } |
| 3886 | + |
| 3887 | + let httpClient = HTTPClient(implementation: handler) |
| 3888 | + let registry = Registry(url: registryURL, supportsAvailability: true) |
| 3889 | + |
| 3890 | + let registryClient = makeRegistryClient( |
| 3891 | + configuration: .init(), |
| 3892 | + httpClient: httpClient |
| 3893 | + ) |
| 3894 | + |
| 3895 | + // Request count should not increase after first check |
| 3896 | + for _ in 0..<5 { |
| 3897 | + try await registryClient.withAvailabilityCheck( |
| 3898 | + registry: registry, |
| 3899 | + observabilityScope: ObservabilitySystem.NOOP |
| 3900 | + ) |
| 3901 | + let count = await counter.value |
| 3902 | + #expect(count == 1) |
| 3903 | + } |
| 3904 | + } |
3704 | 3905 | }
|
3705 | 3906 |
|
3706 | 3907 | // MARK: - Sugar
|
|
0 commit comments