diff --git a/api/Sources/Api/Routes/Appcast.swift b/api/Sources/Api/Routes/Appcast.swift index 516e7d0e..51a9df0f 100644 --- a/api/Sources/Api/Routes/Appcast.swift +++ b/api/Sources/Api/Routes/Appcast.swift @@ -6,10 +6,9 @@ enum AppcastRoute { static func handler(_ request: Request) async throws -> Response { let query = try request.query.decode(AppcastQuery.self) let releases = try await Current.db.query(Release.self) - .where(query.version.map { _ in .always } ?? (.channel == query.channel ?? .stable)) - .where(query.version.map { .semver == $0 } ?? .always) .orderBy(.createdAt, .desc) .all() + .filter { $0.channel.isAtLeastAsStable(as: query.channel ?? .stable) } return Response( headers: ["Content-Type": "application/xml"], diff --git a/api/Tests/ApiTests/AppcastTests.swift b/api/Tests/ApiTests/AppcastTests.swift new file mode 100644 index 00000000..c4597481 --- /dev/null +++ b/api/Tests/ApiTests/AppcastTests.swift @@ -0,0 +1,39 @@ +import XCTest +import XCTVapor +import XExpect + +@testable import Api + +final class AppcastTests: ApiTestCase { + func testAppcastVersions() async throws { + try await replaceAllReleases(with: [ + Release("2.0.0", channel: .stable, pace: nil, createdAt: .epoch), + Release("2.1.0", channel: .canary, pace: nil, createdAt: .epoch.advanced(by: .days(10))), + Release("2.1.1", channel: .stable, pace: nil, createdAt: .epoch.advanced(by: .days(20))), + ]) + + try app.test(.GET, "appcast.xml", afterResponse: { res in + expect(filenames(from: res)).toEqual([ + "Gertrude.2.1.1.zip", + "Gertrude.2.0.0.zip", + ]) + }) + + // canary is one behind stable, so... + try app.test(.GET, "appcast.xml?channel=canary", afterResponse: { res in + expect(filenames(from: res)).toEqual([ + "Gertrude.2.1.1.zip", // <-- ...includes stable + "Gertrude.2.1.0.zip", + "Gertrude.2.0.0.zip", + ]) + }) + } + + func filenames(from response: XCTHTTPResponse) -> [String] { + response.body.string.split(separator: "\n") + .filter { $0.contains("url=") } + .map { $0.trimmingCharacters(in: .whitespaces) } + .compactMap { $0.split(separator: "/").last } + .map { String($0.replacingOccurrences(of: "\"", with: "")) } + } +} diff --git a/api/Tests/ApiTests/MacappPairResolvers/CheckInLatestReleaseTests.swift b/api/Tests/ApiTests/MacappPairResolvers/CheckInLatestReleaseTests.swift index e7be87fd..d392b317 100644 --- a/api/Tests/ApiTests/MacappPairResolvers/CheckInLatestReleaseTests.swift +++ b/api/Tests/ApiTests/MacappPairResolvers/CheckInLatestReleaseTests.swift @@ -20,7 +20,7 @@ final class CheckInLatestReleaseTests: ApiTestCase { } func testAppOnLatestVersionHappyPath() async throws { - try await createReleases([ + try await replaceAllReleases(with: [ Release("1.0.0", pace: 10), Release("1.1.0", pace: 10), ]) @@ -31,7 +31,7 @@ final class CheckInLatestReleaseTests: ApiTestCase { } func testAppBehindOne() async throws { - try await createReleases([ + try await replaceAllReleases(with: [ Release("1.0.0", pace: 10, createdAt: .epoch), Release("1.1.0", pace: 10, createdAt: .epoch.advanced(by: .days(10))), Release("1.2.0", pace: 10, createdAt: .epoch.advanced(by: .days(20))), @@ -49,7 +49,7 @@ final class CheckInLatestReleaseTests: ApiTestCase { } func testAppBehindTwoUsesFirstPace() async throws { - try await createReleases([ + try await replaceAllReleases(with: [ Release("1.0.0", pace: 10, createdAt: .epoch), Release("1.1.0", pace: 10, createdAt: .epoch.advanced(by: .days(10))), Release("1.2.0", pace: 10, createdAt: .epoch.advanced(by: .days(20))), @@ -68,7 +68,7 @@ final class CheckInLatestReleaseTests: ApiTestCase { } func testOnBetaAheadOfStable() async throws { - try await createReleases([ + try await replaceAllReleases(with: [ Release("1.0.0", pace: 10, createdAt: .epoch), Release("1.1.0", pace: 10, createdAt: .epoch.advanced(by: .days(10))), Release("2.0.0", channel: .beta, pace: 10, createdAt: .epoch.advanced(by: .days(20))), @@ -88,7 +88,7 @@ final class CheckInLatestReleaseTests: ApiTestCase { } func testOnCanaryBehindStable() async throws { - try await createReleases([ + try await replaceAllReleases(with: [ Release("2.0.0", channel: .stable, pace: nil, createdAt: .epoch), Release("2.1.0", channel: .canary, pace: nil, createdAt: .epoch.advanced(by: .days(10))), Release("2.1.1", channel: .stable, pace: nil, createdAt: .epoch.advanced(by: .days(20))), @@ -110,7 +110,7 @@ final class CheckInLatestReleaseTests: ApiTestCase { // extensions, helpers -func createReleases(_ releases: [Release]) async throws { +func replaceAllReleases(with releases: [Release]) async throws { try await Current.db.deleteAll(Release.self) try await Current.db.create(releases) for var release in releases { diff --git a/api/Tests/ApiTests/MacappPairResolvers/CheckInResolverTests.swift b/api/Tests/ApiTests/MacappPairResolvers/CheckInResolverTests.swift index 9921873b..cc02a71a 100644 --- a/api/Tests/ApiTests/MacappPairResolvers/CheckInResolverTests.swift +++ b/api/Tests/ApiTests/MacappPairResolvers/CheckInResolverTests.swift @@ -30,7 +30,7 @@ final class CheckInResolverTests: ApiTestCase { } func testCheckIn_OtherProps() async throws { - try await createReleases([ + try await replaceAllReleases(with: [ Release("2.0.3", channel: .stable), Release("2.0.4", channel: .stable), Release("3.0.0", channel: .beta),