Skip to content

Commit

Permalink
Merge pull request #62 from strvcom/feat/ci-workflow
Browse files Browse the repository at this point in the history
[feat]: Add CI workflow to run tests
  • Loading branch information
johnkodes authored Feb 15, 2024
2 parents e01bf1e + b2deb96 commit 3a2f7e7
Show file tree
Hide file tree
Showing 12 changed files with 391 additions and 138 deletions.
57 changes: 57 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: "Networking CI"

on:
push:
branches:
- master
pull_request:
branches:
- '*'

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
unit-tests:
name: ${{ matrix.name }}
runs-on: ${{ matrix.runsOn }}
env:
DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer"
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- name: 'iOS 17.2'
destination: 'OS=17.2,name=iPhone 15 Pro'
xcode: 'Xcode_15.2'
runsOn: macos-14
# - name: 'iOS 16.4'
# destination: 'OS=16.4,name=iPhone 14 Pro'
# xcode: 'Xcode_14.3.1'
# runsOn: macos-13
- name: 'macOS 13, Xcode 15.2'
destination: 'platform=macOS'
xcode: 'Xcode_15.2'
runsOn: macos-14
steps:
- uses: actions/checkout@v4

- name: 'Running unit tests on ${{ matrix.name }}'
run: |
set -o pipefail && \
xcodebuild clean test -resultBundlePath "TestResults-${{ matrix.name }}" -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" | tee "build-log-${{ matrix.name }}.txt" | xcpretty
- uses: kishikawakatsumi/xcresulttool@v1
with:
path: 'TestResults-${{ matrix.name }}.xcresult'
title: '${{ matrix.name }} Test Results'
if: success() || failure()

- name: 'Upload Build Log'
uses: actions/upload-artifact@v4
with:
name: 'build-log-${{ matrix.name }}'
path: 'build-log-${{ matrix.name }}.txt'
if: success() || failure()
16 changes: 14 additions & 2 deletions .swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,22 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Networking"
BuildableName = "Networking"
BlueprintName = "Networking"
ReferencedContainer = "container:">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
skipped = "NO"
testExecutionOrdering = "random">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NetworkingTests"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ final class SampleAuthorizationManager: AuthorizationManaging {
/// We use mock data to simulate real API requests here
private let apiManager: APIManager = {
APIManager(
responseProvider: MockResponseProvider(with: Bundle.main, sessionId: "2023-01-31T15:08:08Z"),
responseProvider: StoredResponseProvider(with: Bundle.main, sessionId: "2023-01-31T15:08:08Z"),
requestAdapters: [LoggingInterceptor.shared],
responseProcessors: [
LoggingInterceptor.shared,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class AuthorizationViewModel: ObservableObject {
#endif

return APIManager(
responseProvider: MockResponseProvider(with: Bundle.main, sessionId: "2023-01-31T15:08:08Z"),
responseProvider: StoredResponseProvider(with: Bundle.main, sessionId: "2023-01-31T15:08:08Z"),
requestAdapters: [
LoggingInterceptor.shared,
authorizationInterceptor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// MockResponseProvider.swift
// StoredResponseProvider.swift
//
//
// Created by Matej Molnár on 04.01.2023.
Expand All @@ -14,10 +14,8 @@ import Foundation
import UIKit
#endif

// MARK: - MockResponseProvider definition

/// A response provider which creates responses for requests from corresponding data files stored in Assets.
open class MockResponseProvider: ResponseProviding {
open class StoredResponseProvider: ResponseProviding {
private let bundle: Bundle
private let sessionId: String
private let requestCounter = Counter()
Expand All @@ -36,7 +34,7 @@ open class MockResponseProvider: ResponseProviding {
/// - Parameter request: URL request.
public func response(for request: URLRequest) async throws -> Response {
guard let model = try? await loadModel(for: request) else {
throw NetworkError.underlying(error: MockResponseProviderError.unableToLoadAssetData)
throw NetworkError.underlying(error: StoredResponseProviderError.unableToLoadAssetData)
}

guard
Expand All @@ -49,7 +47,7 @@ open class MockResponseProvider: ResponseProviding {
headerFields: model.responseHeaders
)
else {
throw NetworkError.underlying(error: MockResponseProviderError.unableToConstructResponse)
throw NetworkError.underlying(error: StoredResponseProviderError.unableToConstructResponse)
}

return Response(model.responseBody ?? Data(), httpResponse)
Expand All @@ -58,7 +56,7 @@ open class MockResponseProvider: ResponseProviding {

// MARK: Private helper functions

private extension MockResponseProvider {
private extension StoredResponseProvider {
/// Loads a corresponding file from Assets for a given ``URLRequest`` and decodes the data to `EndpointRequestStorageModel`.
func loadModel(for request: URLRequest) async throws -> EndpointRequestStorageModel? {
// counting from 0, check storage request processing
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//
// MockResponseProviderError.swift
// StoredResponseProvider.swift
//
//
// Created by Matej Molnár on 04.01.2023.
//

import Foundation

/// An error that occurs during loading a ``Response`` from assets by `MockResponseProvider`.
enum MockResponseProviderError: Error {
/// An error that occurs during loading a ``Response`` from assets by `StoredResponseProvider`.
enum StoredResponseProviderError: Error {
/// An indication that there was a problem with loading or decoding data from assets.
case unableToLoadAssetData
/// An indication that it was not possible to construct a `Response` from the loaded data.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing
// MARK: Private variables
private let fileManager: FileManager
private let jsonEncoder: JSONEncoder
private let fileDataWriter: FileDataWriting
private let config: Config

private lazy var responsesDirectory = fileManager.temporaryDirectory.appendingPathComponent("responses")
Expand Down Expand Up @@ -51,13 +52,15 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing

public init(
fileManager: FileManager = .default,
fileDataWriter: FileDataWriting = FileDataWriter(),
jsonEncoder: JSONEncoder? = nil,
config: Config = .default
) {
self.fileManager = fileManager
self.fileDataWriter = fileDataWriter
self.jsonEncoder = jsonEncoder ?? .default
self.config = config

deleteStoredSessionsExceedingLimit()
}

Expand Down Expand Up @@ -138,7 +141,7 @@ private extension EndpointRequestStorageProcessor {
endpointRequest: EndpointRequest,
urlRequest: URLRequest
) {
Task(priority: .background) { [weak self] in
Task.detached(priority: .utility) { [weak self] in
guard let self else {
return
}
Expand Down Expand Up @@ -214,7 +217,7 @@ private extension EndpointRequestStorageProcessor {
func store(_ model: EndpointRequestStorageModel, fileUrl: URL) {
do {
let jsonData = try jsonEncoder.encode(model)
try jsonData.write(to: fileUrl)
try fileDataWriter.write(jsonData, to: fileUrl)
os_log("🎈 Response saved %{public}@ bytes at %{public}@", type: .info, "\(jsonData.count)", fileUrl.path)
} catch {
os_log("❌ Can't store response %{public}@ %{public}@ %{public}@", type: .error, model.method, model.path, error.localizedDescription)
Expand Down
29 changes: 29 additions & 0 deletions Sources/Networking/Utils/FileDataWriter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// FileDataWriter.swift
//
//
// Created by Jan Kodeš on 24.01.2024.
//

import Foundation

/// A protocol defining an interface for writing data to a file.
public protocol FileDataWriting {
/// Writes the given data to the specified URL.
///
/// - Parameters:
/// - data: The `Data` object that needs to be written to the file.
/// - url: The destination `URL` where the data should be written.
/// - Throws: An error if the data cannot be written to the URL.
func write(_ data: Data, to url: URL) throws
}


/// A class that implements data writing functionality.
public class FileDataWriter: FileDataWriting {
public init() {}

public func write(_ data: Data, to url: URL) throws {
try data.write(to: url)
}
}
Loading

0 comments on commit 3a2f7e7

Please sign in to comment.