Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 31 additions & 28 deletions Tests/AppTests/ErrorMiddlewareTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,60 +12,63 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import XCTest

@testable import App

import Dependencies
import Testing
import Vapor


class ErrorMiddlewareTests: AppTestCase {

override func setUpWithError() throws {
try super.setUpWithError()
@Suite struct ErrorMiddlewareTests {

func setup(_ app: Application) async throws {
// set up some test routes
app.get("ok") { _ in return "ok" }
app.get("404") { req async throws -> Response in throw Abort(.notFound) }
app.get("500") { req async throws -> Response in throw Abort(.internalServerError) }
}

func test_custom_routes() throws {
// Test to ensure the test routes we've set up in setUpWithError are in effect
try app.test(.GET, "ok", afterResponse: { response in
XCTAssertEqual(response.status, .ok)
XCTAssertEqual(response.body.asString(), "ok")
})
@Test func custom_routes() async throws {
try await withApp(setup) { app in
// Test to ensure the test routes we've set up in setUpWithError are in effect
try await app.test(.GET, "ok", afterResponse: { response async in
#expect(response.status == .ok)
#expect(response.body.asString() == "ok")
})
}
}

func test_html_error() throws {
@Test func html_error() async throws {
// Test to ensure errors are converted to html error pages via the ErrorMiddleware
try withDependencies {
try await withDependencies {
$0.environment.dbId = { nil }
} operation: {
try app.test(.GET, "404", afterResponse: { response in
XCTAssertEqual(response.content.contentType, .html)
XCTAssert(response.body.asString().contains("404 - Not Found"))
})
try await withApp(setup) { app in
try await app.test(.GET, "404", afterResponse: { response async in
#expect(response.content.contentType == .html)
#expect(response.body.asString().contains("404 - Not Found"))
})
}
}
}

func test_status_code() throws {
@Test func status_code() async throws {
// Ensure we're still reporting the actual status code even when serving html pages
// (Status is important for Google ranking, see
// https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/323)
try withDependencies {
try await withDependencies {
$0.environment.dbId = { nil }
} operation: {
try app.test(.GET, "404", afterResponse: { response in
XCTAssertEqual(response.status, .notFound)
XCTAssertEqual(response.content.contentType, .html)
})
try app.test(.GET, "500", afterResponse: { response in
XCTAssertEqual(response.status, .internalServerError)
XCTAssertEqual(response.content.contentType, .html)
})
try await withApp(setup) { app in
try await app.test(.GET, "404", afterResponse: { response async in
#expect(response.status == .notFound)
#expect(response.content.contentType == .html)
})
try await app.test(.GET, "500", afterResponse: { response async in
#expect(response.status == .internalServerError)
#expect(response.content.contentType == .html)
})
}
}
}

Expand Down
14 changes: 8 additions & 6 deletions Tests/AppTests/ErrorPageModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,32 @@

@testable import App

import XCTVapor
import Testing
import Vapor

class ErrorPageModelTests: AppTestCase {

func test_500() throws {
@Suite struct ErrorPageModelTests {

@Test func error_500() throws {
// setup
let error = Abort(.internalServerError)

// MUT
let model = ErrorPage.Model(error)

// validate
XCTAssertEqual(model.errorMessage, "500 - Internal Server Error")
#expect(model.errorMessage == "500 - Internal Server Error")
}

func test_500_with_reason() throws {
@Test func error_500_with_reason() throws {
// setup
let error = Abort(.internalServerError, reason: "Reason")

// MUT
let model = ErrorPage.Model(error)

// validate
XCTAssertEqual(model.errorMessage, "500 - Internal Server Error - Reason")
#expect(model.errorMessage == "500 - Internal Server Error - Reason")
}

}
100 changes: 55 additions & 45 deletions Tests/AppTests/ErrorReportingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,53 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation

@testable import App

import Dependencies
import XCTVapor
import Testing


@Suite struct ErrorReportingTests {

class ErrorReportingTests: AppTestCase {
let capturingLogger = CapturingLogger()

func test_Analyze_recordError() async throws {
let pkg = try await savePackage(on: app.db, "1")
try await Analyze.recordError(database: app.db,
error: AppError.cacheDirectoryDoesNotExist(pkg.id, "path"))
do {
let pkg = try await XCTUnwrapAsync(try await Package.find(pkg.id, on: app.db))
XCTAssertEqual(pkg.status, .cacheDirectoryDoesNotExist)
XCTAssertEqual(pkg.processingStage, .analysis)
@Test func Analyze_recordError() async throws {
try await withApp { app in
let pkg = try await savePackage(on: app.db, "1")
try await Analyze.recordError(database: app.db,
error: AppError.cacheDirectoryDoesNotExist(pkg.id, "path"))
do {
let pkg = try await XCTUnwrapAsync(try await Package.find(pkg.id, on: app.db))
#expect(pkg.status == .cacheDirectoryDoesNotExist)
#expect(pkg.processingStage == .analysis)
}
}
}

func test_Ingestion_error_reporting() async throws {
// setup
try await Package(id: .id0, url: "1", processingStage: .reconciliation).save(on: app.db)
@Test func Ingestion_error_reporting() async throws {
try await withApp(logHandler: capturingLogger) { app in
// setup
try await Package(id: .id0, url: "1", processingStage: .reconciliation).save(on: app.db)

try await withDependencies {
$0.date.now = .now
$0.github.fetchMetadata = { @Sendable _, _ throws(Github.Error) in throw Github.Error.invalidURL("1") }
} operation: {
// MUT
try await Ingestion.ingest(client: app.client, database: app.db, mode: .limit(10))
}
try await withDependencies {
$0.date.now = .now
$0.github.fetchMetadata = { @Sendable _, _ throws(Github.Error) in throw Github.Error.invalidURL("1") }
} operation: {
// MUT
try await Ingestion.ingest(client: app.client, database: app.db, mode: .limit(10))
}

// validation
logger.logs.withValue {
XCTAssertEqual($0, [.init(level: .warning,
message: #"Ingestion.Error(\#(UUID.id0), invalidURL(1))"#)])
// validation
capturingLogger.logs.withValue {
#expect($0 == [.init(level: .warning,
message: #"Ingestion.Error(\#(UUID.id0), invalidURL(1))"#)])
}
}
}

func test_Analyzer_error_reporting() async throws {
@Test func Analyzer_error_reporting() async throws {
try await withDependencies {
$0.fileManager.fileExists = { @Sendable _ in true }
$0.shell.run = { @Sendable cmd, _ in
Expand All @@ -60,37 +68,39 @@ class ErrorReportingTests: AppTestCase {
return "invalid"
}
} operation: {
// setup
try await Package(id: .id1, url: "1".asGithubUrl.url, processingStage: .ingestion).save(on: app.db)
try await withApp(logHandler: capturingLogger) { app in
// setup
try await Package(id: .id1, url: "1".asGithubUrl.url, processingStage: .ingestion).save(on: app.db)

// MUT
try await Analyze.analyze(client: app.client,
database: app.db,
mode: .limit(10))
// MUT
try await Analyze.analyze(client: app.client, database: app.db, mode: .limit(10))

// validation
logger.logs.withValue {
XCTAssertEqual($0, [
.init(level: .critical, message: "updatePackages: unusually high error rate: 1/1 = 100.0%"),
.init(level: .warning, message: #"App.AppError.genericError(Optional(\#(UUID.id1)), "updateRepository: no repository")"#)
])
// validation
capturingLogger.logs.withValue {
#expect($0 == [
.init(level: .critical, message: "updatePackages: unusually high error rate: 1/1 = 100.0%"),
.init(level: .warning, message: #"App.AppError.genericError(Optional(\#(UUID.id1)), "updateRepository: no repository")"#)
])
}
}
}
}

func test_invalidPackageCachePath() async throws {
@Test func invalidPackageCachePath() async throws {
try await withDependencies {
$0.fileManager.fileExists = { @Sendable _ in true }
} operation: {
// setup
try await savePackages(on: app.db, ["1", "2"], processingStage: .ingestion)
try await withApp { app in
// setup
try await savePackages(on: app.db, ["1", "2"], processingStage: .ingestion)

// MUT
try await Analyze.analyze(client: app.client, database: app.db, mode: .limit(10))
// MUT
try await Analyze.analyze(client: app.client, database: app.db, mode: .limit(10))

// validation
let packages = try await Package.query(on: app.db).sort(\.$url).all()
XCTAssertEqual(packages.map(\.status), [.invalidCachePath, .invalidCachePath])
// validation
let packages = try await Package.query(on: app.db).sort(\.$url).all()
#expect(packages.map(\.status) == [.invalidCachePath, .invalidCachePath])
}
}
}

Expand Down
17 changes: 8 additions & 9 deletions Tests/AppTests/FundingLinkTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,26 @@

@testable import App

import Vapor
import XCTest
import Testing


class FundingLinkTests: XCTestCase {
@Suite struct FundingLinkTests {

func test_fundingLink_missingSchemeFix() async throws {
@Test func fundingLink_missingSchemeFix() async throws {
// URL with both a scheme and a host.
let ghFundingLink1 = Github.Metadata.FundingLinkNode(platform: .customUrl, url: "https://example.com")
let dbFundingLink1 = try XCTUnwrap(FundingLink(from: ghFundingLink1))
XCTAssertEqual(dbFundingLink1.url, "https://example.com")
let dbFundingLink1 = try #require(FundingLink(from: ghFundingLink1))
#expect(dbFundingLink1.url == "https://example.com")

// URL with a host but no scheme.
let ghFundingLink2 = Github.Metadata.FundingLinkNode(platform: .customUrl, url: "example.com")
let dbFundingLink2 = try XCTUnwrap(FundingLink(from: ghFundingLink2))
XCTAssertEqual(dbFundingLink2.url, "https://example.com")
let dbFundingLink2 = try #require(FundingLink(from: ghFundingLink2))
#expect(dbFundingLink2.url == "https://example.com")

// URL with neither.
let ghFundingLink3 = Github.Metadata.FundingLinkNode(platform: .customUrl, url: "!@£$%")
let dbFundingLink3 = FundingLink(from: ghFundingLink3)
XCTAssertNil(dbFundingLink3)
#expect(dbFundingLink3 == nil)
}

}
Loading