From 5f82211c19d815fb9e2b4482e760a567dbb14747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Wed, 17 Jan 2024 17:22:24 +0100 Subject: [PATCH 01/52] [feat]: Add CI workflow to run tests --- .github/workflows/ci.yml | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..3ab3d080 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +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: + macOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + include: + - name: 'MacOS 13, Xcode 15.1, Swift 5.9.0' + xcode: 'Xcode_15.1' + runsOn: 'macos-13' + - name: 'MacOS 12, Xcode 14.3.1, Swift 5.8.0' + xcode: 'Xcode_14.3.1' + runsOn: 'macos-12' + steps: + - uses: actions/checkout@v4 + + - name: ${{ matrix.name }} + run: swift test + + iOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + include: + - name: 'iOS 17.0' + destination: 'OS=17.0,name=iPhone 15 Pro' + xcode: 'Xcode_15.1' + runsOn: macos-13 + - name: 'iOS 16.4' + destination: 'OS=16.4,name=iPhone 14 Pro' + xcode: 'Xcode_14.3.1' + runsOn: macos-12 + steps: + - uses: actions/checkout@v4 + + - name: ${{ matrix.name }} + run: set -o pipefail && xcodebuild -scheme "Networking" -destination "${{ matrix.destination }}" clean test 2>&1 | xcpretty From b8f60c0d5a845086c8372c262bdd3d94ea279cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Wed, 17 Jan 2024 17:38:57 +0100 Subject: [PATCH 02/52] [feat]: Fix macOS versions --- .github/workflows/ci.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ab3d080..1d218292 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,9 +26,9 @@ jobs: - name: 'MacOS 13, Xcode 15.1, Swift 5.9.0' xcode: 'Xcode_15.1' runsOn: 'macos-13' - - name: 'MacOS 12, Xcode 14.3.1, Swift 5.8.0' + - name: 'MacOS 13, Xcode 14.3.1, Swift 5.8.0' xcode: 'Xcode_14.3.1' - runsOn: 'macos-12' + runsOn: 'macos-13' steps: - uses: actions/checkout@v4 @@ -52,9 +52,12 @@ jobs: - name: 'iOS 16.4' destination: 'OS=16.4,name=iPhone 14 Pro' xcode: 'Xcode_14.3.1' - runsOn: macos-12 + runsOn: macos-13 steps: - uses: actions/checkout@v4 - name: ${{ matrix.name }} - run: set -o pipefail && xcodebuild -scheme "Networking" -destination "${{ matrix.destination }}" clean test 2>&1 | xcpretty + run: | + set -o pipefail && \ + xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean test | xcpretty + From 1cae19b080dff8af4713c01c7d70a6d1c0c3bfea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Wed, 17 Jan 2024 17:46:51 +0100 Subject: [PATCH 03/52] [feat]: Update versions --- .github/workflows/ci.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d218292..6dae311c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,12 +23,12 @@ jobs: fail-fast: false matrix: include: - - name: 'MacOS 13, Xcode 15.1, Swift 5.9.0' - xcode: 'Xcode_15.1' - runsOn: 'macos-13' - - name: 'MacOS 13, Xcode 14.3.1, Swift 5.8.0' - xcode: 'Xcode_14.3.1' + - name: 'MacOS 13, Xcode 15.2, Swift 5.9.0' + xcode: 'Xcode_15.2' runsOn: 'macos-13' + #- name: 'MacOS 13, Xcode 14.3.1, Swift 5.8.0' + # xcode: 'Xcode_14.3.1' + # runsOn: 'macos-13' steps: - uses: actions/checkout@v4 @@ -47,12 +47,16 @@ jobs: include: - name: 'iOS 17.0' destination: 'OS=17.0,name=iPhone 15 Pro' - xcode: 'Xcode_15.1' + xcode: 'Xcode_15.2' runsOn: macos-13 - name: 'iOS 16.4' destination: 'OS=16.4,name=iPhone 14 Pro' xcode: 'Xcode_14.3.1' runsOn: macos-13 + - name: 'iOS 16.4 (Xcode 15)' + destination: 'OS=16.4,name=iPhone 14 Pro' + xcode: 'Xcode_15.2' + runsOn: macos-13 steps: - uses: actions/checkout@v4 From 9516ed2a89c1a7f32562146809ea315dfe397453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Wed, 17 Jan 2024 17:55:45 +0100 Subject: [PATCH 04/52] [feat]: Test formatting tool --- .github/workflows/ci.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6dae311c..ad1bfe1b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,11 +49,11 @@ jobs: destination: 'OS=17.0,name=iPhone 15 Pro' xcode: 'Xcode_15.2' runsOn: macos-13 + # - name: 'iOS 16.4' + # destination: 'OS=16.4,name=iPhone 14 Pro' + # xcode: 'Xcode_14.3.1' + # runsOn: macos-13 - name: 'iOS 16.4' - destination: 'OS=16.4,name=iPhone 14 Pro' - xcode: 'Xcode_14.3.1' - runsOn: macos-13 - - name: 'iOS 16.4 (Xcode 15)' destination: 'OS=16.4,name=iPhone 14 Pro' xcode: 'Xcode_15.2' runsOn: macos-13 @@ -63,5 +63,9 @@ jobs: - name: ${{ matrix.name }} run: | set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean test | xcpretty + xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath TestResults test | xcpretty + - uses: kishikawakatsumi/xcresulttool@v1 + with: + path: TestResults.xcresult + if: success() || failure() From b8f6ca4422971acb234b1a24c457b6955abcad8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 18 Jan 2024 08:40:41 +0100 Subject: [PATCH 05/52] [feat]: Split reports --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad1bfe1b..6631f73b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,9 +63,11 @@ jobs: - name: ${{ matrix.name }} run: | set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath TestResults test | xcpretty + xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | xcpretty - uses: kishikawakatsumi/xcresulttool@v1 with: - path: TestResults.xcresult + path: 'TestResults-${{ matrix.name }}.xcresult' + title: '${{ matrix.name }} Test Results' + upload-bundles: 'failure' if: success() || failure() From 71fe137f5a9f855342735bdc00b3de8800b3f8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 18 Jan 2024 13:05:02 +0100 Subject: [PATCH 06/52] [feat]: Turn on test coverage --- .github/workflows/ci.yml | 2 +- .../xcshareddata/xcschemes/Networking.xcscheme | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6631f73b..2082e89c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@v4 - name: ${{ matrix.name }} - run: swift test + run: swift test --parallel --xunit-output "TestResults-${{ matrix.name }}" iOS: name: ${{ matrix.name }} diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme index fb598c66..ed488045 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme @@ -26,7 +26,18 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES" + onlyGenerateCoverageForSpecifiedTargets = "YES"> + + + + @@ -37,6 +48,11 @@ BlueprintName = "NetworkingTests" ReferencedContainer = "container:"> + + + + From 3a7381390ceb7b106f74a22cf374b636fcc0abda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 18 Jan 2024 17:43:03 +0100 Subject: [PATCH 07/52] [feat]: Test speed of macOS tests via xcodebuild --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2082e89c..5fb0c902 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,10 +57,14 @@ jobs: destination: 'OS=16.4,name=iPhone 14 Pro' xcode: 'Xcode_15.2' runsOn: macos-13 + - name: 'MacOS' + destination: 'OS=macOS' + xcode: 'Xcode_15.2' + runsOn: macos-13 steps: - uses: actions/checkout@v4 - - name: ${{ matrix.name }} + - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | xcpretty From a23e8a61fdbd8eb812cb1920779d1656a27e50dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 18 Jan 2024 17:54:04 +0100 Subject: [PATCH 08/52] =?UTF-8?q?[feat]:=20Try=20different=20Xcode=20to=20?= =?UTF-8?q?solve=20=E2=80=9CSegmentation=20fault=E2=80=9D=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5fb0c902..101cc169 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: runsOn: macos-13 - name: 'MacOS' destination: 'OS=macOS' - xcode: 'Xcode_15.2' + xcode: 'Xcode_15.1' runsOn: macos-13 steps: - uses: actions/checkout@v4 From 180e9c4cc643786028d26d79670011d7532a2fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 10:29:34 +0100 Subject: [PATCH 09/52] =?UTF-8?q?[feat]:=20try=20xcode=2015.2=20again=20fo?= =?UTF-8?q?r=20macOS=C4=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 101cc169..041150d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,9 +57,9 @@ jobs: destination: 'OS=16.4,name=iPhone 14 Pro' xcode: 'Xcode_15.2' runsOn: macos-13 - - name: 'MacOS' + - name: 'MacOS 13, Xcode 15.2' destination: 'OS=macOS' - xcode: 'Xcode_15.1' + xcode: 'Xcode_15.2' runsOn: macos-13 steps: - uses: actions/checkout@v4 From 3ee87c01a3131caa6928523dc0ccd06c926135e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 11:43:24 +0100 Subject: [PATCH 10/52] [feat]: test xcresults without matrix --- .github/workflows/ci.yml | 51 ++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 041150d2..2972e06c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,29 +13,7 @@ concurrency: cancel-in-progress: true jobs: - macOS: - name: ${{ matrix.name }} - runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" - timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - include: - - name: 'MacOS 13, Xcode 15.2, Swift 5.9.0' - xcode: 'Xcode_15.2' - runsOn: 'macos-13' - #- name: 'MacOS 13, Xcode 14.3.1, Swift 5.8.0' - # xcode: 'Xcode_14.3.1' - # runsOn: 'macos-13' - steps: - - uses: actions/checkout@v4 - - - name: ${{ matrix.name }} - run: swift test --parallel --xunit-output "TestResults-${{ matrix.name }}" - - iOS: + unit-tests: name: ${{ matrix.name }} runs-on: ${{ matrix.runsOn }} env: @@ -49,15 +27,11 @@ jobs: destination: 'OS=17.0,name=iPhone 15 Pro' xcode: 'Xcode_15.2' runsOn: macos-13 - # - name: 'iOS 16.4' - # destination: 'OS=16.4,name=iPhone 14 Pro' - # xcode: 'Xcode_14.3.1' - # runsOn: macos-13 - name: 'iOS 16.4' destination: 'OS=16.4,name=iPhone 14 Pro' xcode: 'Xcode_15.2' runsOn: macos-13 - - name: 'MacOS 13, Xcode 15.2' + - name: 'macOS 13, Xcode 15.2' destination: 'OS=macOS' xcode: 'Xcode_15.2' runsOn: macos-13 @@ -75,3 +49,24 @@ jobs: title: '${{ matrix.name }} Test Results' upload-bundles: 'failure' if: success() || failure() + + ios-tests: + name: "iOS 17.0 without matrix" + runs-on: macos-13 + env: + DEVELOPER_DIR: "/Applications/Xcode_15.2.app/Contents/Developer" + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: 'Running unit tests on iOS 17 without matrix' + run: | + set -o pipefail && \ + xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "OS=17.0,name=iPhone 15 Pro" clean -resultBundlePath "TestResults" test | xcpretty + + - uses: kishikawakatsumi/xcresulttool@v1 + with: + path: 'TestResults.xcresult' + title: 'iOS 17 without matrix Test Results' + upload-bundles: 'failure' + if: success() || failure() From 93547a6e2e6e2ffcfb05c102907259de5e3b5631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 12:17:32 +0100 Subject: [PATCH 11/52] [feat]: Set correct simulators --- .github/workflows/ci.yml | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2972e06c..6f5ff9d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,20 +24,18 @@ jobs: matrix: include: - name: 'iOS 17.0' - destination: 'OS=17.0,name=iPhone 15 Pro' - xcode: 'Xcode_15.2' - runsOn: macos-13 - - name: 'iOS 16.4' - destination: 'OS=16.4,name=iPhone 14 Pro' + destination: 'OS=17.2,name=iPhone 15 Pro' xcode: 'Xcode_15.2' runsOn: macos-13 +# - 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: 'OS=macOS' xcode: 'Xcode_15.2' runsOn: macos-13 steps: - - uses: actions/checkout@v4 - - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ @@ -49,24 +47,3 @@ jobs: title: '${{ matrix.name }} Test Results' upload-bundles: 'failure' if: success() || failure() - - ios-tests: - name: "iOS 17.0 without matrix" - runs-on: macos-13 - env: - DEVELOPER_DIR: "/Applications/Xcode_15.2.app/Contents/Developer" - timeout-minutes: 10 - steps: - - uses: actions/checkout@v4 - - - name: 'Running unit tests on iOS 17 without matrix' - run: | - set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "OS=17.0,name=iPhone 15 Pro" clean -resultBundlePath "TestResults" test | xcpretty - - - uses: kishikawakatsumi/xcresulttool@v1 - with: - path: 'TestResults.xcresult' - title: 'iOS 17 without matrix Test Results' - upload-bundles: 'failure' - if: success() || failure() From 2fbdef3a756463d6f877b88f5584e7a713f0517d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 12:27:35 +0100 Subject: [PATCH 12/52] [feat]: Change name --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f5ff9d2..8d07323b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: fail-fast: false matrix: include: - - name: 'iOS 17.0' + - name: 'iOS 17.2' destination: 'OS=17.2,name=iPhone 15 Pro' xcode: 'Xcode_15.2' runsOn: macos-13 @@ -39,7 +39,7 @@ jobs: - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | xcpretty + xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test - uses: kishikawakatsumi/xcresulttool@v1 with: From c6f02357222ae1c53ac7410423c6a26a74373e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 12:31:15 +0100 Subject: [PATCH 13/52] [feat]: Split xcodebuild to build + test --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d07323b..c86ec85d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,8 @@ jobs: - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test + xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" -resultBundlePath "TestResults-${{ matrix.name }}" clean build-for-testing + xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" -resultBundlePath "TestResults-${{ matrix.name }}" test - uses: kishikawakatsumi/xcresulttool@v1 with: From 6b00693057725e4628198758c2907972dc54895e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 12:35:09 +0100 Subject: [PATCH 14/52] [feat]: change results bundle --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c86ec85d..f334136f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,7 @@ jobs: - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" -resultBundlePath "TestResults-${{ matrix.name }}" clean build-for-testing - xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" -resultBundlePath "TestResults-${{ matrix.name }}" test + xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" -resultBundlePath "TestResults-ios" test - uses: kishikawakatsumi/xcresulttool@v1 with: From 2813225b128521ce8d69df6ce0c9032f25b23bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 12:37:16 +0100 Subject: [PATCH 15/52] [feat]: Trying to fix the missing package error --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f334136f..a2194b3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,8 @@ jobs: - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" -resultBundlePath "TestResults-ios" test + xcodebuild -list + xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" test - uses: kishikawakatsumi/xcresulttool@v1 with: From a609ac52bb4b02ec949930b78c04f514e8db0969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 12:39:03 +0100 Subject: [PATCH 16/52] [feat]: Change Xcode version --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2194b3f..bc2d7d34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,9 +31,9 @@ jobs: # destination: 'OS=16.4,name=iPhone 14 Pro' # xcode: 'Xcode_14.3.1' # runsOn: macos-13 - - name: 'macOS 13, Xcode 15.2' + - name: 'macOS 13, Xcode 15.1' destination: 'OS=macOS' - xcode: 'Xcode_15.2' + xcode: 'Xcode_15.1' runsOn: macos-13 steps: - name: 'Running unit tests on ${{ matrix.name }}' From e4cd6449d230b0a49d0a17d01150fe499b7f7493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 12:40:58 +0100 Subject: [PATCH 17/52] [feat]: Fix checkout --- .github/workflows/ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc2d7d34..dd2493e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,16 +31,17 @@ jobs: # destination: 'OS=16.4,name=iPhone 14 Pro' # xcode: 'Xcode_14.3.1' # runsOn: macos-13 - - name: 'macOS 13, Xcode 15.1' + - name: 'macOS 13, Xcode 15.2' destination: 'OS=macOS' - xcode: 'Xcode_15.1' + xcode: 'Xcode_15.2' runsOn: macos-13 steps: + - uses: actions/checkout@v4 + - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ - xcodebuild -list - xcodebuild -skipPackagePluginValidation -scheme Networking -destination "${{ matrix.destination }}" test + xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | xcpretty - uses: kishikawakatsumi/xcresulttool@v1 with: From c0659817a164a35b380cb98320825dd3ad48cf3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Fri, 19 Jan 2024 13:00:37 +0100 Subject: [PATCH 18/52] [feat]: Use lower Xcode for macOS --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd2493e0..5a234f88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,9 +31,9 @@ jobs: # destination: 'OS=16.4,name=iPhone 14 Pro' # xcode: 'Xcode_14.3.1' # runsOn: macos-13 - - name: 'macOS 13, Xcode 15.2' + - name: 'macOS 13, Xcode 15.1' destination: 'OS=macOS' - xcode: 'Xcode_15.2' + xcode: 'Xcode_15.1' # runsOn: macos-13 steps: - uses: actions/checkout@v4 From 4501d9c566a15bf0fda0cfc8c9de83f6a08b9fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sat, 20 Jan 2024 13:50:21 +0100 Subject: [PATCH 19/52] [feat]: try something else --- .github/workflows/ci.yml | 101 ++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a234f88..a99a8896 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,39 +13,76 @@ concurrency: 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: 10 +# unit-tests: +# name: ${{ matrix.name }} +# runs-on: ${{ matrix.runsOn }} +# env: +# DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" +# timeout-minutes: 10 +# strategy: +# fail-fast: false +# matrix: +# include: +# - name: 'iOS 17.2' +# destination: 'OS=17.2,name=iPhone 15 Pro' +# xcode: 'Xcode_15.2' +# runsOn: macos-13 +## - 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.1' +# destination: 'OS=macOS' +# xcode: 'Xcode_15.1' # +# runsOn: macos-13 +# steps: +# - uses: actions/checkout@v4 +# +# - name: 'Running unit tests on ${{ matrix.name }}' +# run: | +# set -o pipefail && \ +# xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | xcpretty +# +# - uses: kishikawakatsumi/xcresulttool@v1 +# with: +# path: 'TestResults-${{ matrix.name }}.xcresult' +# title: '${{ matrix.name }} Test Results' +# upload-bundles: 'failure' +# if: success() || failure() + + build_and_test: + name: ${{ matrix.command }} on  ${{ matrix.platform }} (xcode ${{ matrix.xcode }}, ${{ matrix.macos }}) + runs-on: ${{ matrix.macos }} strategy: fail-fast: false matrix: - include: - - name: 'iOS 17.2' - destination: 'OS=17.2,name=iPhone 15 Pro' - xcode: 'Xcode_15.2' - runsOn: macos-13 -# - 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.1' - destination: 'OS=macOS' - xcode: 'Xcode_15.1' # - runsOn: macos-13 + xcode: ['15.2'] + macos: ['macos-13'] + scheme: ['Networking'] + command: ['test'] + platform: ['macOS', 'iOS'] steps: - - uses: actions/checkout@v4 - - - name: 'Running unit tests on ${{ matrix.name }}' - run: | - set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | xcpretty - - - uses: kishikawakatsumi/xcresulttool@v1 - with: - path: 'TestResults-${{ matrix.name }}.xcresult' - title: '${{ matrix.name }} Test Results' - upload-bundles: 'failure' - if: success() || failure() + - name: Switch xcode to ${{ matrix.xcode }} + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: ${{ matrix.xcode }} + - name: Double-check macOS version (${{ matrix.macos }}) + run: sw_vers + - name: Code Checkout + uses: actions/checkout@v2 + - name: Check xcodebuild version + run: xcodebuild -version + - name: Check xcode embedded SDKs + run: xcodebuild -showsdks + - name: Show buildable schemes + run: xcodebuild -list + - name: Show eligible build destinations for ${{ matrix.scheme }} + run: xcodebuild -showdestinations -scheme ${{ matrix.scheme }} + - uses: mxcl/xcodebuild@v1 + with: + platform: ${{ matrix.platform }} + scheme: ${{ matrix.scheme }} + action: ${{ matrix.command }} + code-coverage: true + verbosity: xcpretty + upload-logs: always From a74d496cbf7a5db5a7fc14d8c74c6bbfc1c041ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sat, 20 Jan 2024 17:16:42 +0100 Subject: [PATCH 20/52] [feat]: Revert back --- .github/workflows/ci.yml | 97 +++++++++++++--------------------------- 1 file changed, 30 insertions(+), 67 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a99a8896..1ab09d27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,76 +13,39 @@ concurrency: 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: 10 -# strategy: -# fail-fast: false -# matrix: -# include: + unit-tests: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + include: # - name: 'iOS 17.2' # destination: 'OS=17.2,name=iPhone 15 Pro' # xcode: 'Xcode_15.2' # runsOn: macos-13 -## - 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.1' -# destination: 'OS=macOS' -# xcode: 'Xcode_15.1' # +# - name: 'iOS 16.4' +# destination: 'OS=16.4,name=iPhone 14 Pro' +# xcode: 'Xcode_14.3.1' # runsOn: macos-13 -# steps: -# - uses: actions/checkout@v4 -# -# - name: 'Running unit tests on ${{ matrix.name }}' -# run: | -# set -o pipefail && \ -# xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | xcpretty -# -# - uses: kishikawakatsumi/xcresulttool@v1 -# with: -# path: 'TestResults-${{ matrix.name }}.xcresult' -# title: '${{ matrix.name }} Test Results' -# upload-bundles: 'failure' -# if: success() || failure() - - build_and_test: - name: ${{ matrix.command }} on  ${{ matrix.platform }} (xcode ${{ matrix.xcode }}, ${{ matrix.macos }}) - runs-on: ${{ matrix.macos }} - strategy: - fail-fast: false - matrix: - xcode: ['15.2'] - macos: ['macos-13'] - scheme: ['Networking'] - command: ['test'] - platform: ['macOS', 'iOS'] + - name: 'macOS 13, Xcode 15.1' + destination: 'OS=macOS' + xcode: 'Xcode_15.1' # + runsOn: macos-13 steps: - - name: Switch xcode to ${{ matrix.xcode }} - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: ${{ matrix.xcode }} - - name: Double-check macOS version (${{ matrix.macos }}) - run: sw_vers - - name: Code Checkout - uses: actions/checkout@v2 - - name: Check xcodebuild version - run: xcodebuild -version - - name: Check xcode embedded SDKs - run: xcodebuild -showsdks - - name: Show buildable schemes - run: xcodebuild -list - - name: Show eligible build destinations for ${{ matrix.scheme }} - run: xcodebuild -showdestinations -scheme ${{ matrix.scheme }} - - uses: mxcl/xcodebuild@v1 - with: - platform: ${{ matrix.platform }} - scheme: ${{ matrix.scheme }} - action: ${{ matrix.command }} - code-coverage: true - verbosity: xcpretty - upload-logs: always + - uses: actions/checkout@v4 + + - name: 'Running unit tests on ${{ matrix.name }}' + run: | + set -o pipefail && \ + xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | xcpretty + + - uses: kishikawakatsumi/xcresulttool@v1 + with: + path: 'TestResults-${{ matrix.name }}.xcresult' + title: '${{ matrix.name }} Test Results' + upload-bundles: 'failure' + if: success() || failure() From 4f577de42493dae66a86683554e32aca839294f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 15:13:40 +0100 Subject: [PATCH 21/52] [feat]: Upload logs --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ab09d27..0de7704c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,3 +49,9 @@ jobs: title: '${{ matrix.name }} Test Results' upload-bundles: 'failure' if: success() || failure() + + - name: 'Upload xcresult Artifact' + uses: actions/upload-artifact@v3 + with: + name: 'xcresult-${{ matrix.name }}' + path: 'TestResults-${{ matrix.name }}.xcresult' From ffeb9f72d6ecb8b9f81579870589b94630846261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 15:19:49 +0100 Subject: [PATCH 22/52] [feat]: Upload logs --- .github/workflows/ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0de7704c..34bd50f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,17 +41,16 @@ jobs: - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | xcpretty + xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | tee "build-log-${{ matrix.name }}.txt" | xcpretty - uses: kishikawakatsumi/xcresulttool@v1 with: path: 'TestResults-${{ matrix.name }}.xcresult' title: '${{ matrix.name }} Test Results' - upload-bundles: 'failure' if: success() || failure() - - name: 'Upload xcresult Artifact' + - name: 'Upload Build Log' uses: actions/upload-artifact@v3 with: - name: 'xcresult-${{ matrix.name }}' - path: 'TestResults-${{ matrix.name }}.xcresult' + name: 'build-log-${{ matrix.name }}' + path: 'build-log-${{ matrix.name }}.txt' From b53904c7c2997c697ed57cd152f6629abba3bf96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 15:22:58 +0100 Subject: [PATCH 23/52] [feat]: If failure or success --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34bd50f3..aa644520 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,3 +54,4 @@ jobs: with: name: 'build-log-${{ matrix.name }}' path: 'build-log-${{ matrix.name }}.txt' + if: success() || failure() From de9e9e705a619bb83bba382c1fc0b9b7cdbba0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 16:45:01 +0100 Subject: [PATCH 24/52] [feat]: Remove result bundle --- .github/workflows/ci.yml | 2 +- Package.resolved | 86 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 Package.resolved diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa644520..c3a32f84 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean -resultBundlePath "TestResults-${{ matrix.name }}" test | tee "build-log-${{ matrix.name }}.txt" | xcpretty + xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean test | tee "build-log-${{ matrix.name }}.txt" | xcpretty - uses: kishikawakatsumi/xcresulttool@v1 with: diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 00000000..63931882 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,86 @@ +{ + "pins" : [ + { + "identity" : "collectionconcurrencykit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git", + "state" : { + "revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95", + "version" : "0.2.0" + } + }, + { + "identity" : "cryptoswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", + "state" : { + "revision" : "32f641cf24fc7abc1c591a2025e9f2f572648b0f", + "version" : "1.7.2" + } + }, + { + "identity" : "sourcekitten", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/SourceKitten.git", + "state" : { + "revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56", + "version" : "0.34.1" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", + "version" : "1.2.3" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax.git", + "state" : { + "revision" : "74203046135342e4a4a627476dd6caf8b28fe11b", + "version" : "509.0.0" + } + }, + { + "identity" : "swiftlint", + "kind" : "remoteSourceControl", + "location" : "https://github.com/realm/SwiftLint.git", + "state" : { + "revision" : "6d2e58271ebc14c37bf76d7c9f4082cc15bad718", + "version" : "0.53.0" + } + }, + { + "identity" : "swiftytexttable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scottrhoyt/SwiftyTextTable.git", + "state" : { + "revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", + "version" : "0.9.0" + } + }, + { + "identity" : "swxmlhash", + "kind" : "remoteSourceControl", + "location" : "https://github.com/drmohundro/SWXMLHash.git", + "state" : { + "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f", + "version" : "7.0.2" + } + }, + { + "identity" : "yams", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/Yams.git", + "state" : { + "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", + "version" : "5.0.6" + } + } + ], + "version" : 2 +} From 70e9b99772f5a3e7b041577e188769e2bdb5390a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 17:08:10 +0100 Subject: [PATCH 25/52] [feat]: disable swiftlint --- Package.resolved | 86 ------------------------------------------------ Package.swift | 11 ++++--- 2 files changed, 6 insertions(+), 91 deletions(-) delete mode 100644 Package.resolved diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 63931882..00000000 --- a/Package.resolved +++ /dev/null @@ -1,86 +0,0 @@ -{ - "pins" : [ - { - "identity" : "collectionconcurrencykit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git", - "state" : { - "revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95", - "version" : "0.2.0" - } - }, - { - "identity" : "cryptoswift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", - "state" : { - "revision" : "32f641cf24fc7abc1c591a2025e9f2f572648b0f", - "version" : "1.7.2" - } - }, - { - "identity" : "sourcekitten", - "kind" : "remoteSourceControl", - "location" : "https://github.com/jpsim/SourceKitten.git", - "state" : { - "revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56", - "version" : "0.34.1" - } - }, - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser.git", - "state" : { - "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", - "version" : "1.2.3" - } - }, - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", - "state" : { - "revision" : "74203046135342e4a4a627476dd6caf8b28fe11b", - "version" : "509.0.0" - } - }, - { - "identity" : "swiftlint", - "kind" : "remoteSourceControl", - "location" : "https://github.com/realm/SwiftLint.git", - "state" : { - "revision" : "6d2e58271ebc14c37bf76d7c9f4082cc15bad718", - "version" : "0.53.0" - } - }, - { - "identity" : "swiftytexttable", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scottrhoyt/SwiftyTextTable.git", - "state" : { - "revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", - "version" : "0.9.0" - } - }, - { - "identity" : "swxmlhash", - "kind" : "remoteSourceControl", - "location" : "https://github.com/drmohundro/SWXMLHash.git", - "state" : { - "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f", - "version" : "7.0.2" - } - }, - { - "identity" : "yams", - "kind" : "remoteSourceControl", - "location" : "https://github.com/jpsim/Yams.git", - "state" : { - "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", - "version" : "5.0.6" - } - } - ], - "version" : 2 -} diff --git a/Package.swift b/Package.swift index 152471e9..99027a47 100644 --- a/Package.swift +++ b/Package.swift @@ -17,19 +17,20 @@ let package = Package( targets: ["Networking"] ) ], - dependencies: [.package(url: "https://github.com/realm/SwiftLint.git", exact: "0.53.0")], + // dependencies: [.package(url: "https://github.com/realm/SwiftLint.git", exact: "0.53.0")], + dependencies: [], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( - name: "Networking", - plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")] + name: "Networking" + // plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")] ), .testTarget( name: "NetworkingTests", dependencies: ["Networking"], - resources: [.process("Resources")], - plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")] + resources: [.process("Resources")] + // plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")] ) ] ) From cf8635e84e44b281f62d62511dcafb16fbf6579a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 17:14:25 +0100 Subject: [PATCH 26/52] [feat]: Remove workspace --- .../contents.xcworkspacedata | 10 --- .../xcshareddata/IDEWorkspaceChecks.plist | 8 -- .../xcshareddata/WorkspaceSettings.xcsettings | 8 -- .../xcshareddata/swiftpm/Package.resolved | 86 ------------------- 4 files changed, 112 deletions(-) delete mode 100644 Networking.xcworkspace/contents.xcworkspacedata delete mode 100644 Networking.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 Networking.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 Networking.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/Networking.xcworkspace/contents.xcworkspacedata b/Networking.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index e0fdae21..00000000 --- a/Networking.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/Networking.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Networking.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/Networking.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Networking.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Networking.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5..00000000 --- a/Networking.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/Networking.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Networking.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 63931882..00000000 --- a/Networking.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,86 +0,0 @@ -{ - "pins" : [ - { - "identity" : "collectionconcurrencykit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git", - "state" : { - "revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95", - "version" : "0.2.0" - } - }, - { - "identity" : "cryptoswift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", - "state" : { - "revision" : "32f641cf24fc7abc1c591a2025e9f2f572648b0f", - "version" : "1.7.2" - } - }, - { - "identity" : "sourcekitten", - "kind" : "remoteSourceControl", - "location" : "https://github.com/jpsim/SourceKitten.git", - "state" : { - "revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56", - "version" : "0.34.1" - } - }, - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser.git", - "state" : { - "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", - "version" : "1.2.3" - } - }, - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", - "state" : { - "revision" : "74203046135342e4a4a627476dd6caf8b28fe11b", - "version" : "509.0.0" - } - }, - { - "identity" : "swiftlint", - "kind" : "remoteSourceControl", - "location" : "https://github.com/realm/SwiftLint.git", - "state" : { - "revision" : "6d2e58271ebc14c37bf76d7c9f4082cc15bad718", - "version" : "0.53.0" - } - }, - { - "identity" : "swiftytexttable", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scottrhoyt/SwiftyTextTable.git", - "state" : { - "revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", - "version" : "0.9.0" - } - }, - { - "identity" : "swxmlhash", - "kind" : "remoteSourceControl", - "location" : "https://github.com/drmohundro/SWXMLHash.git", - "state" : { - "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f", - "version" : "7.0.2" - } - }, - { - "identity" : "yams", - "kind" : "remoteSourceControl", - "location" : "https://github.com/jpsim/Yams.git", - "state" : { - "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", - "version" : "5.0.6" - } - } - ], - "version" : 2 -} From 3bd4c44c8c9b6b1be70f99a150e9cb3f1f550d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 17:26:51 +0100 Subject: [PATCH 27/52] Revert "[feat]: Remove workspace" This reverts commit cf8635e84e44b281f62d62511dcafb16fbf6579a. --- .../contents.xcworkspacedata | 10 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++ .../xcshareddata/WorkspaceSettings.xcsettings | 8 ++ .../xcshareddata/swiftpm/Package.resolved | 86 +++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 Networking.xcworkspace/contents.xcworkspacedata create mode 100644 Networking.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Networking.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 Networking.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/Networking.xcworkspace/contents.xcworkspacedata b/Networking.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..e0fdae21 --- /dev/null +++ b/Networking.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Networking.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Networking.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Networking.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Networking.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Networking.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/Networking.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/Networking.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Networking.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..63931882 --- /dev/null +++ b/Networking.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,86 @@ +{ + "pins" : [ + { + "identity" : "collectionconcurrencykit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git", + "state" : { + "revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95", + "version" : "0.2.0" + } + }, + { + "identity" : "cryptoswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", + "state" : { + "revision" : "32f641cf24fc7abc1c591a2025e9f2f572648b0f", + "version" : "1.7.2" + } + }, + { + "identity" : "sourcekitten", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/SourceKitten.git", + "state" : { + "revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56", + "version" : "0.34.1" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", + "version" : "1.2.3" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax.git", + "state" : { + "revision" : "74203046135342e4a4a627476dd6caf8b28fe11b", + "version" : "509.0.0" + } + }, + { + "identity" : "swiftlint", + "kind" : "remoteSourceControl", + "location" : "https://github.com/realm/SwiftLint.git", + "state" : { + "revision" : "6d2e58271ebc14c37bf76d7c9f4082cc15bad718", + "version" : "0.53.0" + } + }, + { + "identity" : "swiftytexttable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scottrhoyt/SwiftyTextTable.git", + "state" : { + "revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", + "version" : "0.9.0" + } + }, + { + "identity" : "swxmlhash", + "kind" : "remoteSourceControl", + "location" : "https://github.com/drmohundro/SWXMLHash.git", + "state" : { + "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f", + "version" : "7.0.2" + } + }, + { + "identity" : "yams", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/Yams.git", + "state" : { + "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", + "version" : "5.0.6" + } + } + ], + "version" : 2 +} From 958f5d31c375e887fd9a5dcf11039b8b974b0225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 17:28:02 +0100 Subject: [PATCH 28/52] [feat]: Revert swiftlint --- Package.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Package.swift b/Package.swift index 99027a47..152471e9 100644 --- a/Package.swift +++ b/Package.swift @@ -17,20 +17,19 @@ let package = Package( targets: ["Networking"] ) ], - // dependencies: [.package(url: "https://github.com/realm/SwiftLint.git", exact: "0.53.0")], - dependencies: [], + dependencies: [.package(url: "https://github.com/realm/SwiftLint.git", exact: "0.53.0")], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( - name: "Networking" - // plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")] + name: "Networking", + plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")] ), .testTarget( name: "NetworkingTests", dependencies: ["Networking"], - resources: [.process("Resources")] - // plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")] + resources: [.process("Resources")], + plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")] ) ] ) From fd26d0a852cea80afdc6f14a08872ec54c24d186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 17:29:26 +0100 Subject: [PATCH 29/52] [feat]: Change Xcode to 15.0.1 --- .github/workflows/ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3a32f84..e2bbcf06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,17 +23,17 @@ jobs: fail-fast: false matrix: include: -# - name: 'iOS 17.2' -# destination: 'OS=17.2,name=iPhone 15 Pro' -# xcode: 'Xcode_15.2' -# runsOn: macos-13 + - name: 'iOS 17.2' + destination: 'OS=17.2,name=iPhone 15 Pro' + xcode: 'Xcode_15.2' + runsOn: macos-13 # - 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.1' - destination: 'OS=macOS' - xcode: 'Xcode_15.1' # + - name: 'macOS 13, Xcode 15.0.1' + destination: 'OS=macOS,name=Any Mac' + xcode: 'Xcode_15.0.1' # runsOn: macos-13 steps: - uses: actions/checkout@v4 From b673d89fb39e30e2f2a811d3e4ef8bfab5b07567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Sun, 21 Jan 2024 18:45:12 +0100 Subject: [PATCH 30/52] [feat]: Add back result bundle --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2bbcf06..14547be7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,8 +32,8 @@ jobs: # xcode: 'Xcode_14.3.1' # runsOn: macos-13 - name: 'macOS 13, Xcode 15.0.1' - destination: 'OS=macOS,name=Any Mac' - xcode: 'Xcode_15.0.1' # + destination: 'platform=macOS' + xcode: 'Xcode_15.0.1' runsOn: macos-13 steps: - uses: actions/checkout@v4 @@ -41,7 +41,7 @@ jobs: - name: 'Running unit tests on ${{ matrix.name }}' run: | set -o pipefail && \ - xcodebuild -skipPackagePluginValidation -scheme "Networking" -destination "${{ matrix.destination }}" clean test | tee "build-log-${{ matrix.name }}.txt" | xcpretty + 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: From 88baabd33fa4dcdb134f81d480c99b7fb1ff9997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Mon, 22 Jan 2024 08:52:52 +0100 Subject: [PATCH 31/52] [feat]: Increase timeout --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14547be7..1a03e842 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: runs-on: ${{ matrix.runsOn }} env: DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" - timeout-minutes: 10 + timeout-minutes: 30 strategy: fail-fast: false matrix: From aa82148080204725c62a633784d025b7407a3560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Tue, 23 Jan 2024 16:15:30 +0100 Subject: [PATCH 32/52] [feat]: Remove Task.sleeps in tests --- .../EndpointRequestStorageProcessor.swift | 12 ++--- ...EndpointRequestStorageProcessorTests.swift | 22 ++------- .../Mocks/MockFileManager.swift | 49 +++++++++++++++++++ .../MockResponseProviderTests.swift | 0 4 files changed, 60 insertions(+), 23 deletions(-) create mode 100644 Tests/NetworkingTests/Mocks/MockFileManager.swift rename Tests/NetworkingTests/{ => Mocks}/MockResponseProviderTests.swift (100%) diff --git a/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift b/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift index 91ec57a2..991bc631 100644 --- a/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift +++ b/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift @@ -68,7 +68,7 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing /// - endpointRequest: An endpoint request wrapper. /// - Returns: The original ``Response``. public func process(_ response: Response, with urlRequest: URLRequest, for endpointRequest: EndpointRequest) async throws -> Response { - storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) + await storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) return response } @@ -87,9 +87,9 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing switch error { case let .unacceptableStatusCode(_, _, response): - storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) + await storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) case let .noStatusCode(response): - storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) + await storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) case .headerIsInvalid, .underlying, .unknown: break } @@ -137,8 +137,8 @@ private extension EndpointRequestStorageProcessor { _ response: Response, endpointRequest: EndpointRequest, urlRequest: URLRequest - ) { - Task(priority: .background) { [weak self] in + ) async { + await Task(priority: .background) { [weak self] in guard let self else { return } @@ -184,7 +184,7 @@ private extension EndpointRequestStorageProcessor { ) multipeerConnectivityManager?.send(model: storageModel) - } + }.value } func createFolderIfNeeded(_ sessionId: String?) { diff --git a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift index 3980fde9..3ce5e408 100644 --- a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift +++ b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift @@ -14,8 +14,8 @@ import XCTest final class EndpointRequestStorageProcessorTests: XCTestCase { private let sessionId = "sessionId_request_storage" - private let fileManager = FileManager.default - + private let fileManager = MockFileManager() + struct MockBody: Codable { let parameter: String } @@ -104,10 +104,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let processor = EndpointRequestStorageProcessor(fileManager: fileManager, jsonEncoder: encoder) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - - // The storing runs on background thread so we need to wait before reading the file - try await Task.sleep(nanoseconds: 1000000000) - + let fileUrl = fileUrl(for: mockEndpointRequest) guard let data = fileManager.contents(atPath: fileUrl.path) else { @@ -157,10 +154,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let processor = EndpointRequestStorageProcessor(fileManager: fileManager, jsonEncoder: encoder) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - - // The storing runs on background thread so we need to wait before reading the file - try await Task.sleep(nanoseconds: 1000000000) - + let fileUrl = fileUrl(for: mockEndpointRequest) guard let data = fileManager.contents(atPath: fileUrl.path) else { @@ -206,10 +200,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let processor = EndpointRequestStorageProcessor(fileManager: fileManager, jsonEncoder: encoder) _ = await processor.process(mockError, for: mockEndpointRequest) - - // The storing runs on background thread so we need to wait before reading the file - try await Task.sleep(nanoseconds: 1000000000) - + let fileUrl = fileUrl(for: mockEndpointRequest) guard let data = fileManager.contents(atPath: fileUrl.path) else { @@ -251,9 +242,6 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let processor = EndpointRequestStorageProcessor(fileManager: fileManager, jsonEncoder: encoder) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - // The storing runs on background thread so we need to wait before reading the file - try await Task.sleep(nanoseconds: 1000000000) - let fileUrl = fileUrl(for: mockEndpointRequest) guard let data = fileManager.contents(atPath: fileUrl.path) else { diff --git a/Tests/NetworkingTests/Mocks/MockFileManager.swift b/Tests/NetworkingTests/Mocks/MockFileManager.swift new file mode 100644 index 00000000..0e8745d4 --- /dev/null +++ b/Tests/NetworkingTests/Mocks/MockFileManager.swift @@ -0,0 +1,49 @@ +// +// MockFileManager.swift +// +// +// Created by Jan Kodeš on 23.01.2024. +// + +import Foundation +import Foundation +import XCTest + +/// A subclass of `FileManager` where the file existence is based on a dictionary whose key is the file path. +final class MockFileManager: FileManager { + var dataByFilePath: [String: Data] = [:] + + /// The mocked results for the `attributesOfItem(atPath:)`. + private var attributesOfItemResults = [String: [FileAttributeKey: Any]]() + + override func fileExists(atPath path: String) -> Bool { + dataByFilePath[path] != nil + } + + override func createFile(atPath path: String, contents data: Data?, attributes attr: [FileAttributeKey: Any]? = nil) -> Bool { + dataByFilePath[path] = data + return true + } + + override func removeItem(at URL: URL) throws { + dataByFilePath.removeValue(forKey: URL.path) + } + + override func attributesOfItem(atPath path: String) throws -> [FileAttributeKey: Any] { + guard let attributes = attributesOfItemResults[path] else { + XCTFail("No mocked attributes for path \(path)") + return [:] + } + + return attributes + } +} + +// MARK: - Mocking + +extension MockFileManager { + /// Sets the return value when `attributesOfItem(atPath:)` is called. + func whenRetrievingAttributesOfItem(atPath path: String, thenReturn: [FileAttributeKey: Any]) { + attributesOfItemResults[path] = thenReturn + } +} diff --git a/Tests/NetworkingTests/MockResponseProviderTests.swift b/Tests/NetworkingTests/Mocks/MockResponseProviderTests.swift similarity index 100% rename from Tests/NetworkingTests/MockResponseProviderTests.swift rename to Tests/NetworkingTests/Mocks/MockResponseProviderTests.swift From 26403b6c88b42890ccc4cc43d11d707cc769d285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Wed, 24 Jan 2024 10:46:14 +0100 Subject: [PATCH 33/52] [feat]: Bring back skipped tests --- .swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme index 757cff7e..5134859e 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme @@ -48,11 +48,6 @@ BlueprintName = "NetworkingTests" ReferencedContainer = "container:"> - - - - From 899595132a41d4b829b187793ba8eb41fe8d3ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Wed, 24 Jan 2024 10:46:54 +0100 Subject: [PATCH 34/52] [feat]: Try latest Xcode for macOS tests --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a03e842..e4a365c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,9 +31,9 @@ jobs: # destination: 'OS=16.4,name=iPhone 14 Pro' # xcode: 'Xcode_14.3.1' # runsOn: macos-13 - - name: 'macOS 13, Xcode 15.0.1' + - name: 'macOS 13, Xcode 15.2' destination: 'platform=macOS' - xcode: 'Xcode_15.0.1' + xcode: 'Xcode_15.2' runsOn: macos-13 steps: - uses: actions/checkout@v4 From ce5394b38f0a39056ccb2e775eaa894d96f96f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Wed, 24 Jan 2024 10:58:02 +0100 Subject: [PATCH 35/52] [feat]: Rename MockResponseProvider to StoredResponseProvider --- .../API/SampleAuthorizationManager.swift | 2 +- .../AuthorizationViewModel.swift | 2 +- .../Core/StoredResponseProvider.swift | 79 +++++++++++++ .../Misc/StoredResponseProviderError.swift | 16 +++ .../Mocks/MockFileManager.swift | 1 - .../StoredResponseProviderTests.swift | 104 ++++++++++++++++++ 6 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 Sources/Networking/Core/StoredResponseProvider.swift create mode 100644 Sources/Networking/Misc/StoredResponseProviderError.swift create mode 100644 Tests/NetworkingTests/StoredResponseProviderTests.swift diff --git a/NetworkingSampleApp/NetworkingSampleApp/API/SampleAuthorizationManager.swift b/NetworkingSampleApp/NetworkingSampleApp/API/SampleAuthorizationManager.swift index f85d1624..f13babad 100644 --- a/NetworkingSampleApp/NetworkingSampleApp/API/SampleAuthorizationManager.swift +++ b/NetworkingSampleApp/NetworkingSampleApp/API/SampleAuthorizationManager.swift @@ -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, diff --git a/NetworkingSampleApp/NetworkingSampleApp/Scenes/Authorization/AuthorizationViewModel.swift b/NetworkingSampleApp/NetworkingSampleApp/Scenes/Authorization/AuthorizationViewModel.swift index 6a16f7ae..37f4134d 100644 --- a/NetworkingSampleApp/NetworkingSampleApp/Scenes/Authorization/AuthorizationViewModel.swift +++ b/NetworkingSampleApp/NetworkingSampleApp/Scenes/Authorization/AuthorizationViewModel.swift @@ -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 diff --git a/Sources/Networking/Core/StoredResponseProvider.swift b/Sources/Networking/Core/StoredResponseProvider.swift new file mode 100644 index 00000000..e4b996d9 --- /dev/null +++ b/Sources/Networking/Core/StoredResponseProvider.swift @@ -0,0 +1,79 @@ +// +// StoredResponseProvider.swift +// +// +// Created by Matej Molnár on 04.01.2023. +// + +import Foundation + +// necessary for NSDataAsset import +#if os(macOS) + import AppKit +#else + import UIKit +#endif + +/// A response provider which creates responses for requests from corresponding data files stored in Assets. +open class StoredResponseProvider: ResponseProviding { + private let bundle: Bundle + private let sessionId: String + private let requestCounter = Counter() + private lazy var decoder = JSONDecoder() + + /// Creates MockResponseProvider instance. + /// - Parameters: + /// - bundle: A bundle which includes the assets file. + /// - sessionId: An ID of a session, which data should be read. + public init(with bundle: Bundle, sessionId: String) { + self.bundle = bundle + self.sessionId = sessionId + } + + /// Creates a ``Response`` for a given `URLRequest` based on data from a corresponding file stored in Assets. + /// - 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: StoredResponseProviderError.unableToLoadAssetData) + } + + guard + let statusCode = model.statusCode, + let url = request.url, + let httpResponse = HTTPURLResponse( + url: url, + statusCode: statusCode, + httpVersion: nil, + headerFields: model.responseHeaders + ) + else { + throw NetworkError.underlying(error: StoredResponseProviderError.unableToConstructResponse) + } + + return Response(model.responseBody ?? Data(), httpResponse) + } +} + +// MARK: Private helper functions + +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 + let count = await requestCounter.count(for: request.identifier) + + if let data = NSDataAsset(name: "\(sessionId)_\(request.identifier)_\(count)", bundle: bundle)?.data { + // store info about next indexed api call + await requestCounter.increment(for: request.identifier) + return try decoder.decode(EndpointRequestStorageModel.self, from: data) + } + + // return previous response, if no more stored indexed api calls + // swiftlint:disable:next empty_count + if count > 0, let data = NSDataAsset(name: "\(sessionId)_\(request.identifier)_\(count - 1)", bundle: bundle)?.data { + return try decoder.decode(EndpointRequestStorageModel.self, from: data) + } + + return nil + } +} diff --git a/Sources/Networking/Misc/StoredResponseProviderError.swift b/Sources/Networking/Misc/StoredResponseProviderError.swift new file mode 100644 index 00000000..378f0440 --- /dev/null +++ b/Sources/Networking/Misc/StoredResponseProviderError.swift @@ -0,0 +1,16 @@ +// +// StoredResponseProvider.swift +// +// +// Created by Matej Molnár on 04.01.2023. +// + +import Foundation + +/// 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. + case unableToConstructResponse +} diff --git a/Tests/NetworkingTests/Mocks/MockFileManager.swift b/Tests/NetworkingTests/Mocks/MockFileManager.swift index 0e8745d4..b4eb4ada 100644 --- a/Tests/NetworkingTests/Mocks/MockFileManager.swift +++ b/Tests/NetworkingTests/Mocks/MockFileManager.swift @@ -5,7 +5,6 @@ // Created by Jan Kodeš on 23.01.2024. // -import Foundation import Foundation import XCTest diff --git a/Tests/NetworkingTests/StoredResponseProviderTests.swift b/Tests/NetworkingTests/StoredResponseProviderTests.swift new file mode 100644 index 00000000..2ef1689b --- /dev/null +++ b/Tests/NetworkingTests/StoredResponseProviderTests.swift @@ -0,0 +1,104 @@ +// +// StoredResponseProviderTests.swift +// +// +// Created by Matej Molnár on 05.01.2023. +// + +@testable import Networking +import XCTest + +final class StoredResponseProviderTests: XCTestCase { + // swiftlint:disable:next force_unwrapping + private lazy var mockUrlRequest = URLRequest(url: URL(string: "https://reqres.in/api/users?page=2")!) + private let mockSessionId = "2023-01-04T16:15:29Z" + + private let mockHeaderFields = [ + "Server": "cloudflare", + "Etag": "W/\"406-ut0vzoCuidvyMf8arZpMpJ6ZRDw\"", + "x-powered-by": "Express", + "nel": "{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}", + "Content-Encoding": "br", + "Vary": "Accept-Encoding", + // swiftlint:disable:next line_length + "report-to": "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=5XGHUrnfYDsl7guBAx0nFk7LTbUgOLjp5%2BGMkSPetC5OrW6fKlUc1NBBtOKHKe9yWrcbXkF4TQe8jsv1c4KggYW1q4pYf5G2rQvA8XACg1znl6MbWiNj1w2wOg%3D%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}", + "Content-Type": "application/json; charset=utf-8", + "cf-cache-status": "HIT", + "Cache-Control": "max-age=14400", + "Access-Control-Allow-Origin": "*", + "cf-ray": "784545f34d2f27bc-PRG", + "Date": "Wed, 04 Jan 2023 16:15:29 GMT", + "Via": "1.1 vegur", + "Age": "6306" + ] + + func testLoadingData() async throws { + let storedResponseProvider = StoredResponseProvider(with: Bundle.module, sessionId: mockSessionId) + + // call request multiple times, 6 testing data files + // test reading correct file + for index in 0...10 { + let response = try await storedResponseProvider.response(for: mockUrlRequest) + + XCTAssert(response.response is HTTPURLResponse) + + guard let httpResponse = response.response as? HTTPURLResponse else { + XCTAssert(false, "Wrong response type") + return + } + + guard let headerFields = httpResponse.allHeaderFields as? [String: String] else { + XCTAssert(false, "Wrong response header fields type") + return + } + + XCTAssertEqual(headerFields, mockHeaderFields) + + switch index { + case 3: + XCTAssertEqual(httpResponse.statusCode, 200) + XCTAssertEqual(response.data.count, 0) + case 4: + XCTAssertEqual(httpResponse.statusCode, 400) + XCTAssertEqual(response.data.count, 0) + default: + XCTAssertEqual(httpResponse.statusCode, 200) + XCTAssertEqual(response.data.count, 1030) + } + } + } + + func testUnableToLoadAssetError() async { + let storedResponseProvider = StoredResponseProvider(with: Bundle.module, sessionId: "NonexistentSessionId") + + do { + _ = try await storedResponseProvider.response(for: mockUrlRequest) + XCTAssert(false, "function didn't throw an error even though it should have") + } catch { + var correctError = false + if case NetworkError.underlying(error: StoredResponseProviderError.unableToLoadAssetData) = error { + correctError = true + } + XCTAssert(correctError, "function threw an incorrect error") + } + } + + func testUnableToConstructResponseError() async { + let storedResponseProvider = StoredResponseProvider(with: Bundle.module, sessionId: "2023-01-04T16:15:29Z(corrupted)") + + do { + _ = try await storedResponseProvider.response(for: mockUrlRequest) + XCTAssert(false, "function didn't throw an error even though it should have") + } catch { + var correctError = false + if case NetworkError.underlying(error: StoredResponseProviderError.unableToConstructResponse) = error { + correctError = true + } + XCTAssert(correctError, "function threw an incorrect error") + } + } + + static var allTests = [ + ("testLoadingData", testLoadingData) + ] +} From 032023ff76f3bb4d55db62102c9ff4dbfa4b37cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 25 Jan 2024 11:13:45 +0100 Subject: [PATCH 36/52] [feat]: Add FileDataWriter --- .../EndpointRequestStorageProcessor.swift | 19 +- Sources/Networking/Utils/FileDataWriter.swift | 26 +++ ...EndpointRequestStorageProcessorTests.swift | 168 +++++++++++------- .../NetworkingTests/Mocks/FileWriterSpy.swift | 25 +++ .../Mocks/MockFileManager.swift | 4 + 5 files changed, 174 insertions(+), 68 deletions(-) create mode 100644 Sources/Networking/Utils/FileDataWriter.swift create mode 100644 Tests/NetworkingTests/Mocks/FileWriterSpy.swift diff --git a/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift b/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift index 991bc631..1a6da60f 100644 --- a/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift +++ b/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift @@ -23,6 +23,7 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing // MARK: Private variables private let fileManager: FileManager private let jsonEncoder: JSONEncoder + private let fileDataWriter: FileDataWriterProtocol private let config: Config private lazy var responsesDirectory = fileManager.temporaryDirectory.appendingPathComponent("responses") @@ -51,13 +52,15 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing public init( fileManager: FileManager = .default, + fileDataWriter: FileDataWriterProtocol = FileDataWriter(), jsonEncoder: JSONEncoder? = nil, config: Config = .default ) { self.fileManager = fileManager + self.fileDataWriter = fileDataWriter self.jsonEncoder = jsonEncoder ?? .default self.config = config - + deleteStoredSessionsExceedingLimit() } @@ -68,7 +71,7 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing /// - endpointRequest: An endpoint request wrapper. /// - Returns: The original ``Response``. public func process(_ response: Response, with urlRequest: URLRequest, for endpointRequest: EndpointRequest) async throws -> Response { - await storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) + storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) return response } @@ -87,9 +90,9 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing switch error { case let .unacceptableStatusCode(_, _, response): - await storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) + storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) case let .noStatusCode(response): - await storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) + storeResponse(response, endpointRequest: endpointRequest, urlRequest: urlRequest) case .headerIsInvalid, .underlying, .unknown: break } @@ -137,8 +140,8 @@ private extension EndpointRequestStorageProcessor { _ response: Response, endpointRequest: EndpointRequest, urlRequest: URLRequest - ) async { - await Task(priority: .background) { [weak self] in + ) { + Task.detached(priority: .background) { [weak self] in guard let self else { return } @@ -184,7 +187,7 @@ private extension EndpointRequestStorageProcessor { ) multipeerConnectivityManager?.send(model: storageModel) - }.value + } } func createFolderIfNeeded(_ sessionId: String?) { @@ -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) diff --git a/Sources/Networking/Utils/FileDataWriter.swift b/Sources/Networking/Utils/FileDataWriter.swift new file mode 100644 index 00000000..d1305025 --- /dev/null +++ b/Sources/Networking/Utils/FileDataWriter.swift @@ -0,0 +1,26 @@ +// +// FileWriter.swift +// +// +// Created by Jan Kodeš on 24.01.2024. +// + +import Foundation + +public protocol FileDataWriterProtocol { + /// 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 +} + +public class FileDataWriter: FileDataWriterProtocol { + public init() {} + + public func write(_ data: Data, to url: URL) throws { + try data.write(to: url) + } +} diff --git a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift index 3ce5e408..54ccc069 100644 --- a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift +++ b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift @@ -75,6 +75,12 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { } } + override func tearDown() { + fileManager.reset() + + super.tearDown() + } + func testResponseStaysTheSameAfterStoringData() async throws { let mockEndpointRequest = EndpointRequest(MockRouter.testStoringGet, sessionId: sessionId) let mockURLRequest = URLRequest(url: MockRouter.testStoringGet.baseURL) @@ -98,13 +104,24 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { )! let mockResponseData = "Mock data".data(using: .utf8)! let mockResponse = (mockResponseData, mockURLResponse) - + let expectation = expectation(description: "Data was written") + let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted - - let processor = EndpointRequestStorageProcessor(fileManager: fileManager, jsonEncoder: encoder) + let fileDataWriterSpy = FileDataWriterSpy() + fileDataWriterSpy.writeClosure = { + expectation.fulfill() + } + + let processor = EndpointRequestStorageProcessor( + fileManager: fileManager, + fileDataWriter: fileDataWriterSpy, + jsonEncoder: encoder + ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) + await fulfillment(of: [expectation], timeout: 10) + let fileUrl = fileUrl(for: mockEndpointRequest) guard let data = fileManager.contents(atPath: fileUrl.path) else { @@ -113,19 +130,17 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { } let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: data) - - XCTAssert( - model.statusCode == 200 && - model.method == "GET" && - model.path == mockEndpointRequest.endpoint.path && - model.parameters == ["query": "mock"] && - model.requestBody == nil && - model.requestBodyString == nil && - model.requestHeaders == mockURLRequest.allHTTPHeaderFields && - model.responseBody == mockResponseData && - model.responseBodyString == String(data: mockResponseData, encoding: .utf8) && - model.responseHeaders == ["mockResponseHeader": "mock"] - ) + + XCTAssertEqual(model.statusCode, 200) + XCTAssertEqual(model.method, "GET") + XCTAssertEqual(model.path, mockEndpointRequest.endpoint.path) + XCTAssertEqual(model.parameters, ["query": "mock"]) + XCTAssertNil(model.requestBody) + XCTAssertNil(model.requestBodyString) + XCTAssertEqual(model.requestHeaders, mockURLRequest.allHTTPHeaderFields) + XCTAssertEqual(model.responseBody, mockResponseData) + XCTAssertEqual(model.responseBodyString, String(data: mockResponseData, encoding: .utf8)) + XCTAssertEqual(model.responseHeaders, ["mockResponseHeader": "mock"]) } func testStoredDataForGetRequestWithImageResponse() async throws { @@ -148,13 +163,25 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { #endif let mockResponse = (mockResponseData, mockURLResponse) - + let expectation = expectation(description: "Data was written") + + let fileDataWriterSpy = FileDataWriterSpy() + fileDataWriterSpy.writeClosure = { + expectation.fulfill() + } + let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted - let processor = EndpointRequestStorageProcessor(fileManager: fileManager, jsonEncoder: encoder) + let processor = EndpointRequestStorageProcessor( + fileManager: fileManager, + fileDataWriter: fileDataWriterSpy, + jsonEncoder: encoder + ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) + await fulfillment(of: [expectation], timeout: 10) + let fileUrl = fileUrl(for: mockEndpointRequest) guard let data = fileManager.contents(atPath: fileUrl.path) else { @@ -163,19 +190,17 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { } let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: data) - - XCTAssert( - model.statusCode == 200 && - model.method == "GET" && - model.path == mockEndpointRequest.endpoint.path && - model.parameters == ["query": "mock"] && - model.requestBody == nil && - model.requestBodyString == nil && - model.requestHeaders == mockURLRequest.allHTTPHeaderFields && - model.responseBody == mockResponseData && - model.responseBodyString == String(data: mockResponseData, encoding: .utf8) && - model.responseHeaders == ["mockResponseHeader": "mock"] - ) + + XCTAssertEqual(model.statusCode, 200) + XCTAssertEqual(model.method, "GET") + XCTAssertEqual(model.path, mockEndpointRequest.endpoint.path) + XCTAssertEqual(model.parameters, ["query": "mock"]) + XCTAssertNil(model.requestBody) + XCTAssertNil(model.requestBodyString) + XCTAssertEqual(model.requestHeaders, mockURLRequest.allHTTPHeaderFields) + XCTAssertEqual(model.responseBody, mockResponseData) + XCTAssertEqual(model.responseBodyString, String(data: mockResponseData, encoding: .utf8)) + XCTAssertEqual(model.responseHeaders, ["mockResponseHeader": "mock"]) } func testStoredDataForGetRequestWithErrorResponse() async throws { @@ -194,13 +219,27 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { acceptedStatusCodes: HTTPStatusCode.successCodes, response: mockResponse ) - + + let expectation = expectation(description: "Data was written") + + let fileDataWriterSpy = FileDataWriterSpy() + fileDataWriterSpy.writeClosure = { + expectation.fulfill() + } + let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted - let processor = EndpointRequestStorageProcessor(fileManager: fileManager, jsonEncoder: encoder) + let processor = EndpointRequestStorageProcessor( + fileManager: fileManager, + fileDataWriter: fileDataWriterSpy, + jsonEncoder: encoder + ) + _ = await processor.process(mockError, for: mockEndpointRequest) + await fulfillment(of: [expectation], timeout: 10) + let fileUrl = fileUrl(for: mockEndpointRequest) guard let data = fileManager.contents(atPath: fileUrl.path) else { @@ -210,18 +249,16 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: data) - XCTAssert( - model.statusCode == 404 && - model.method == "GET" && - model.path == mockEndpointRequest.endpoint.path && - model.parameters == ["query": "mock"] && - model.requestBody == nil && - model.requestBodyString == nil && - model.requestHeaders == mockURLRequest.allHTTPHeaderFields && - model.responseBody == mockResponseData && - model.responseBodyString == String(data: mockResponseData, encoding: .utf8) && - model.responseHeaders == ["mockResponseHeader": "mock"] - ) + XCTAssertEqual(model.statusCode, 404) + XCTAssertEqual(model.method, "GET") + XCTAssertEqual(model.path, mockEndpointRequest.endpoint.path) + XCTAssertEqual(model.parameters, ["query": "mock"]) + XCTAssertNil(model.requestBody) + XCTAssertNil(model.requestBodyString) + XCTAssertEqual(model.requestHeaders, mockURLRequest.allHTTPHeaderFields) + XCTAssertEqual(model.responseBody, mockResponseData) + XCTAssertEqual(model.responseBodyString, String(data: mockResponseData, encoding: .utf8)) + XCTAssertEqual(model.responseHeaders, ["mockResponseHeader": "mock"]) } func testStoredDataForPostRequest() async throws { @@ -235,13 +272,26 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { )! let mockResponseData = "Mock data".data(using: .utf8)! let mockResponse = (mockResponseData, mockURLResponse) - + + let expectation = expectation(description: "Data was written") + + let fileDataWriterSpy = FileDataWriterSpy() + fileDataWriterSpy.writeClosure = { + expectation.fulfill() + } + let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted - let processor = EndpointRequestStorageProcessor(fileManager: fileManager, jsonEncoder: encoder) + let processor = EndpointRequestStorageProcessor( + fileManager: fileManager, + fileDataWriter: fileDataWriterSpy, + jsonEncoder: encoder + ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) + await fulfillment(of: [expectation], timeout: 10) + let fileUrl = fileUrl(for: mockEndpointRequest) guard let data = fileManager.contents(atPath: fileUrl.path) else { @@ -251,19 +301,17 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: data) let mockRequestBody = try mockEndpointRequest.endpoint.encodeBody()! - - XCTAssert( - model.statusCode == 200 && - model.method == "POST" && - model.path == mockEndpointRequest.endpoint.path && - model.parameters == ["query": "mock"] && - model.requestBody == mockRequestBody && - model.requestBodyString == String(data: mockRequestBody, encoding: .utf8) && - model.requestHeaders == mockURLRequest.allHTTPHeaderFields && - model.responseBody == mockResponseData && - model.responseBodyString == String(data: mockResponseData, encoding: .utf8) && - model.responseHeaders == ["mockResponseHeader": "mock"] - ) + + XCTAssertEqual(model.statusCode, 200) + XCTAssertEqual(model.method, "POST") + XCTAssertEqual(model.path, mockEndpointRequest.endpoint.path) + XCTAssertEqual(model.parameters, ["query": "mock"]) + XCTAssertEqual(model.requestBody, mockRequestBody) + XCTAssertEqual(model.requestBodyString, String(data: mockRequestBody, encoding: .utf8)) + XCTAssertEqual(model.requestHeaders, mockURLRequest.allHTTPHeaderFields) + XCTAssertEqual(model.responseBody, mockResponseData) + XCTAssertEqual(model.responseBodyString, String(data: mockResponseData, encoding: .utf8)) + XCTAssertEqual(model.responseHeaders, ["mockResponseHeader": "mock"]) } // swiftlint:enable force_unwrapping diff --git a/Tests/NetworkingTests/Mocks/FileWriterSpy.swift b/Tests/NetworkingTests/Mocks/FileWriterSpy.swift new file mode 100644 index 00000000..c5af89e0 --- /dev/null +++ b/Tests/NetworkingTests/Mocks/FileWriterSpy.swift @@ -0,0 +1,25 @@ +// +// FileDataWriterSpy.swift +// +// +// Created by Jan Kodeš on 24.01.2024. +// + +import Foundation +import Networking + +class FileDataWriterSpy: FileDataWriterProtocol { + var writeClosure: (() -> Void)? + private(set) var writeCalled = false + private(set) var receivedData: Data? + private(set) var receivedURL: URL? + + func write(_ data: Data, to url: URL) throws { + writeCalled = true + receivedData = data + receivedURL = url + try data.write(to: url) + + writeClosure?() + } +} diff --git a/Tests/NetworkingTests/Mocks/MockFileManager.swift b/Tests/NetworkingTests/Mocks/MockFileManager.swift index b4eb4ada..8953c11b 100644 --- a/Tests/NetworkingTests/Mocks/MockFileManager.swift +++ b/Tests/NetworkingTests/Mocks/MockFileManager.swift @@ -36,6 +36,10 @@ final class MockFileManager: FileManager { return attributes } + + func reset() { + dataByFilePath = [:] + } } // MARK: - Mocking From 28779d6b0c310dd8cee07a664ee2b7db18560dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 25 Jan 2024 11:14:26 +0100 Subject: [PATCH 37/52] [feat]: Randomize and parallelize tests --- .swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme index 5134859e..a66f0710 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme @@ -40,7 +40,9 @@ + skipped = "NO" + parallelizable = "YES" + testExecutionOrdering = "random"> Date: Thu, 25 Jan 2024 11:34:16 +0100 Subject: [PATCH 38/52] [eat]: remove duplicate files --- .../Core/MockResponseProvider.swift | 81 -------------- .../Mocks/MockResponseProviderTests.swift | 104 ------------------ 2 files changed, 185 deletions(-) delete mode 100644 Sources/Networking/Core/MockResponseProvider.swift delete mode 100644 Tests/NetworkingTests/Mocks/MockResponseProviderTests.swift diff --git a/Sources/Networking/Core/MockResponseProvider.swift b/Sources/Networking/Core/MockResponseProvider.swift deleted file mode 100644 index 307c471c..00000000 --- a/Sources/Networking/Core/MockResponseProvider.swift +++ /dev/null @@ -1,81 +0,0 @@ -// -// MockResponseProvider.swift -// -// -// Created by Matej Molnár on 04.01.2023. -// - -import Foundation - -// necessary for NSDataAsset import -#if os(macOS) - import AppKit -#else - 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 { - private let bundle: Bundle - private let sessionId: String - private let requestCounter = Counter() - private lazy var decoder = JSONDecoder() - - /// Creates MockResponseProvider instance. - /// - Parameters: - /// - bundle: A bundle which includes the assets file. - /// - sessionId: An ID of a session, which data should be read. - public init(with bundle: Bundle, sessionId: String) { - self.bundle = bundle - self.sessionId = sessionId - } - - /// Creates a ``Response`` for a given `URLRequest` based on data from a corresponding file stored in Assets. - /// - 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) - } - - guard - let statusCode = model.statusCode, - let url = request.url, - let httpResponse = HTTPURLResponse( - url: url, - statusCode: statusCode, - httpVersion: nil, - headerFields: model.responseHeaders - ) - else { - throw NetworkError.underlying(error: MockResponseProviderError.unableToConstructResponse) - } - - return Response(model.responseBody ?? Data(), httpResponse) - } -} - -// MARK: Private helper functions - -private extension MockResponseProvider { - /// 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 - let count = await requestCounter.count(for: request.identifier) - - if let data = NSDataAsset(name: "\(sessionId)_\(request.identifier)_\(count)", bundle: bundle)?.data { - // store info about next indexed api call - await requestCounter.increment(for: request.identifier) - return try decoder.decode(EndpointRequestStorageModel.self, from: data) - } - - // return previous response, if no more stored indexed api calls - // swiftlint:disable:next empty_count - if count > 0, let data = NSDataAsset(name: "\(sessionId)_\(request.identifier)_\(count - 1)", bundle: bundle)?.data { - return try decoder.decode(EndpointRequestStorageModel.self, from: data) - } - - return nil - } -} diff --git a/Tests/NetworkingTests/Mocks/MockResponseProviderTests.swift b/Tests/NetworkingTests/Mocks/MockResponseProviderTests.swift deleted file mode 100644 index c3ca6027..00000000 --- a/Tests/NetworkingTests/Mocks/MockResponseProviderTests.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// MockResponseProviderTests.swift -// -// -// Created by Matej Molnár on 05.01.2023. -// - -@testable import Networking -import XCTest - -final class MockResponseProviderTests: XCTestCase { - // swiftlint:disable:next force_unwrapping - private lazy var mockUrlRequest = URLRequest(url: URL(string: "https://reqres.in/api/users?page=2")!) - private let mockSessionId = "2023-01-04T16:15:29Z" - - private let mockHeaderFields = [ - "Server": "cloudflare", - "Etag": "W/\"406-ut0vzoCuidvyMf8arZpMpJ6ZRDw\"", - "x-powered-by": "Express", - "nel": "{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}", - "Content-Encoding": "br", - "Vary": "Accept-Encoding", - // swiftlint:disable:next line_length - "report-to": "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=5XGHUrnfYDsl7guBAx0nFk7LTbUgOLjp5%2BGMkSPetC5OrW6fKlUc1NBBtOKHKe9yWrcbXkF4TQe8jsv1c4KggYW1q4pYf5G2rQvA8XACg1znl6MbWiNj1w2wOg%3D%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}", - "Content-Type": "application/json; charset=utf-8", - "cf-cache-status": "HIT", - "Cache-Control": "max-age=14400", - "Access-Control-Allow-Origin": "*", - "cf-ray": "784545f34d2f27bc-PRG", - "Date": "Wed, 04 Jan 2023 16:15:29 GMT", - "Via": "1.1 vegur", - "Age": "6306" - ] - - func testLoadingData() async throws { - let mockResponseProvider = MockResponseProvider(with: Bundle.module, sessionId: mockSessionId) - - // call request multiple times, 6 testing data files - // test reading correct file - for index in 0...10 { - let response = try await mockResponseProvider.response(for: mockUrlRequest) - - XCTAssert(response.response is HTTPURLResponse) - - guard let httpResponse = response.response as? HTTPURLResponse else { - XCTAssert(false, "Wrong response type") - return - } - - guard let headerFields = httpResponse.allHeaderFields as? [String: String] else { - XCTAssert(false, "Wrong response header fields type") - return - } - - XCTAssertEqual(headerFields, mockHeaderFields) - - switch index { - case 3: - XCTAssertEqual(httpResponse.statusCode, 200) - XCTAssertEqual(response.data.count, 0) - case 4: - XCTAssertEqual(httpResponse.statusCode, 400) - XCTAssertEqual(response.data.count, 0) - default: - XCTAssertEqual(httpResponse.statusCode, 200) - XCTAssertEqual(response.data.count, 1030) - } - } - } - - func testUnableToLoadAssetError() async { - let mockResponseProvider = MockResponseProvider(with: Bundle.module, sessionId: "NonexistentSessionId") - - do { - _ = try await mockResponseProvider.response(for: mockUrlRequest) - XCTAssert(false, "function didn't throw an error even though it should have") - } catch { - var correctError = false - if case NetworkError.underlying(error: MockResponseProviderError.unableToLoadAssetData) = error { - correctError = true - } - XCTAssert(correctError, "function threw an incorrect error") - } - } - - func testUnableToConstructResponseError() async { - let mockResponseProvider = MockResponseProvider(with: Bundle.module, sessionId: "2023-01-04T16:15:29Z(corrupted)") - - do { - _ = try await mockResponseProvider.response(for: mockUrlRequest) - XCTAssert(false, "function didn't throw an error even though it should have") - } catch { - var correctError = false - if case NetworkError.underlying(error: MockResponseProviderError.unableToConstructResponse) = error { - correctError = true - } - XCTAssert(correctError, "function threw an incorrect error") - } - } - - static var allTests = [ - ("testLoadingData", testLoadingData) - ] -} From ef0ef9be3507265a1c7d2d50adac0b0f6a3b535d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 25 Jan 2024 14:28:09 +0100 Subject: [PATCH 39/52] [feat]: Increase timeouts --- .../EndpointRequestStorageProcessorTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift index 54ccc069..b1bb644b 100644 --- a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift +++ b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift @@ -120,7 +120,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - await fulfillment(of: [expectation], timeout: 10) + await fulfillment(of: [expectation], timeout: 20) let fileUrl = fileUrl(for: mockEndpointRequest) @@ -180,7 +180,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - await fulfillment(of: [expectation], timeout: 10) + await fulfillment(of: [expectation], timeout: 20) let fileUrl = fileUrl(for: mockEndpointRequest) @@ -238,7 +238,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { _ = await processor.process(mockError, for: mockEndpointRequest) - await fulfillment(of: [expectation], timeout: 10) + await fulfillment(of: [expectation], timeout: 20) let fileUrl = fileUrl(for: mockEndpointRequest) @@ -290,7 +290,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - await fulfillment(of: [expectation], timeout: 10) + await fulfillment(of: [expectation], timeout: 20) let fileUrl = fileUrl(for: mockEndpointRequest) From 8ec2ba0b43e5d856db8ac1bb39a9b63602744df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 25 Jan 2024 15:21:39 +0100 Subject: [PATCH 40/52] [chore]: Update GA upload-artifact --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4a365c1..c8a57516 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: if: success() || failure() - name: 'Upload Build Log' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: 'build-log-${{ matrix.name }}' path: 'build-log-${{ matrix.name }}.txt' From 3374cc725918994e61f19366341fe47b4c13d207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 25 Jan 2024 17:00:25 +0100 Subject: [PATCH 41/52] [chore]: Turn parallel testing --- .swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme | 1 - 1 file changed, 1 deletion(-) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme index a66f0710..5f948cd1 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Networking.xcscheme @@ -41,7 +41,6 @@ Date: Thu, 25 Jan 2024 17:46:02 +0100 Subject: [PATCH 42/52] [feat]: Add documentation --- .../EndpointRequestStorageProcessor.swift | 4 ++-- Sources/Networking/Utils/FileDataWriter.swift | 7 +++++-- .../EndpointRequestStorageProcessorTests.swift | 8 ++++---- .../{FileWriterSpy.swift => MockFileDataWriter.swift} | 4 +++- 4 files changed, 14 insertions(+), 9 deletions(-) rename Tests/NetworkingTests/Mocks/{FileWriterSpy.swift => MockFileDataWriter.swift} (75%) diff --git a/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift b/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift index 1a6da60f..ea343047 100644 --- a/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift +++ b/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift @@ -23,7 +23,7 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing // MARK: Private variables private let fileManager: FileManager private let jsonEncoder: JSONEncoder - private let fileDataWriter: FileDataWriterProtocol + private let fileDataWriter: FileDataWriting private let config: Config private lazy var responsesDirectory = fileManager.temporaryDirectory.appendingPathComponent("responses") @@ -52,7 +52,7 @@ open class EndpointRequestStorageProcessor: ResponseProcessing, ErrorProcessing public init( fileManager: FileManager = .default, - fileDataWriter: FileDataWriterProtocol = FileDataWriter(), + fileDataWriter: FileDataWriting = FileDataWriter(), jsonEncoder: JSONEncoder? = nil, config: Config = .default ) { diff --git a/Sources/Networking/Utils/FileDataWriter.swift b/Sources/Networking/Utils/FileDataWriter.swift index d1305025..41af0adc 100644 --- a/Sources/Networking/Utils/FileDataWriter.swift +++ b/Sources/Networking/Utils/FileDataWriter.swift @@ -7,7 +7,8 @@ import Foundation -public protocol FileDataWriterProtocol { +/// A protocol defining an interface for writing data to a file. +public protocol FileDataWriting { /// Writes the given data to the specified URL. /// /// - Parameters: @@ -17,7 +18,9 @@ public protocol FileDataWriterProtocol { func write(_ data: Data, to url: URL) throws } -public class FileDataWriter: FileDataWriterProtocol { + +/// A class that implements data writing functionality. +public class FileDataWriter: FileDataWriting { public init() {} public func write(_ data: Data, to url: URL) throws { diff --git a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift index b1bb644b..ebcef460 100644 --- a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift +++ b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift @@ -108,7 +108,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted - let fileDataWriterSpy = FileDataWriterSpy() + let fileDataWriterSpy = MockFileDataWriter() fileDataWriterSpy.writeClosure = { expectation.fulfill() } @@ -165,7 +165,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let mockResponse = (mockResponseData, mockURLResponse) let expectation = expectation(description: "Data was written") - let fileDataWriterSpy = FileDataWriterSpy() + let fileDataWriterSpy = MockFileDataWriter() fileDataWriterSpy.writeClosure = { expectation.fulfill() } @@ -222,7 +222,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let expectation = expectation(description: "Data was written") - let fileDataWriterSpy = FileDataWriterSpy() + let fileDataWriterSpy = MockFileDataWriter() fileDataWriterSpy.writeClosure = { expectation.fulfill() } @@ -275,7 +275,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let expectation = expectation(description: "Data was written") - let fileDataWriterSpy = FileDataWriterSpy() + let fileDataWriterSpy = MockFileDataWriter() fileDataWriterSpy.writeClosure = { expectation.fulfill() } diff --git a/Tests/NetworkingTests/Mocks/FileWriterSpy.swift b/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift similarity index 75% rename from Tests/NetworkingTests/Mocks/FileWriterSpy.swift rename to Tests/NetworkingTests/Mocks/MockFileDataWriter.swift index c5af89e0..ef65d81f 100644 --- a/Tests/NetworkingTests/Mocks/FileWriterSpy.swift +++ b/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift @@ -8,7 +8,9 @@ import Foundation import Networking -class FileDataWriterSpy: FileDataWriterProtocol { +/// A test mock class for `FileDataWriting`. +/// It writes into a file but let's us react when it's finished. +class MockFileDataWriter: FileDataWriting { var writeClosure: (() -> Void)? private(set) var writeCalled = false private(set) var receivedData: Data? From 6501bad97eb1f53818f6d284d38380c404bf04aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 25 Jan 2024 18:10:46 +0100 Subject: [PATCH 43/52] [feat]: Increase priority --- .../EndpointRequestStorageProcessor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift b/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift index ea343047..83ee383e 100644 --- a/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift +++ b/Sources/Networking/Modifiers/Processors/EndpointRequestStorageProcessor/EndpointRequestStorageProcessor.swift @@ -141,7 +141,7 @@ private extension EndpointRequestStorageProcessor { endpointRequest: EndpointRequest, urlRequest: URLRequest ) { - Task.detached(priority: .background) { [weak self] in + Task.detached(priority: .utility) { [weak self] in guard let self else { return } From 0a10d96e0567309fc6b6aecf4a7cdf6f0b372712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Mon, 29 Jan 2024 16:00:49 +0100 Subject: [PATCH 44/52] [feat]: Make class final --- Tests/NetworkingTests/Mocks/MockFileDataWriter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift b/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift index ef65d81f..17c56c76 100644 --- a/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift +++ b/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift @@ -10,7 +10,7 @@ import Networking /// A test mock class for `FileDataWriting`. /// It writes into a file but let's us react when it's finished. -class MockFileDataWriter: FileDataWriting { +final class MockFileDataWriter: FileDataWriting { var writeClosure: (() -> Void)? private(set) var writeCalled = false private(set) var receivedData: Data? From fe1b1df72177d63acdd9f6b0cab88a0da846406e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 1 Feb 2024 18:29:21 +0100 Subject: [PATCH 45/52] [feat]: Rework FileManager + tests --- Sources/Networking/Utils/FileDataWriter.swift | 2 +- ...EndpointRequestStorageProcessorTests.swift | 117 ++++++++++-------- .../Mocks/MockFileManager.swift | 63 ++++++---- 3 files changed, 107 insertions(+), 75 deletions(-) diff --git a/Sources/Networking/Utils/FileDataWriter.swift b/Sources/Networking/Utils/FileDataWriter.swift index 41af0adc..2f1eb089 100644 --- a/Sources/Networking/Utils/FileDataWriter.swift +++ b/Sources/Networking/Utils/FileDataWriter.swift @@ -1,5 +1,5 @@ // -// FileWriter.swift +// FileDataWriter.swift // // // Created by Jan Kodeš on 24.01.2024. diff --git a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift index ebcef460..ab4dea68 100644 --- a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift +++ b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift @@ -14,7 +14,7 @@ import XCTest final class EndpointRequestStorageProcessorTests: XCTestCase { private let sessionId = "sessionId_request_storage" - private let fileManager = MockFileManager() + private let mockFileManager = MockFileManager() struct MockBody: Codable { let parameter: String @@ -76,7 +76,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { } override func tearDown() { - fileManager.reset() + mockFileManager.reset() super.tearDown() } @@ -90,7 +90,38 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let response = try await EndpointRequestStorageProcessor().process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) // test storing data processor doesn't effect response in anyway - XCTAssert(response.data == mockResponse.0 && response.response == mockResponse.1) + XCTAssertEqual(response.data, mockResponse.0) + XCTAssertEqual(response.response, mockResponse.1) + } + + func testProcessCreatesCorrectFolder() async throws { + let mockEndpointRequest = EndpointRequest(MockRouter.testStoringGet, sessionId: sessionId) + let mockURLRequest = URLRequest(url: MockRouter.testStoringGet.baseURL) + let mockURLResponse: URLResponse = HTTPURLResponse(url: MockRouter.testStoringGet.baseURL, statusCode: 200, httpVersion: nil, headerFields: nil)! + let mockResponse = (Data(), mockURLResponse) + + let expectation = expectation(description: "Data was written") + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + let mockFileDataWriter = MockFileDataWriter() + mockFileDataWriter.writeClosure = { + expectation.fulfill() + } + + let processor = EndpointRequestStorageProcessor( + fileManager: mockFileManager, + fileDataWriter: mockFileDataWriter, + jsonEncoder: encoder + ) + _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) + + await fulfillment(of: [expectation], timeout: 20) + + mockFileManager.verifyFunctionCall(.fileExists(path: responsesDirectory(for: mockEndpointRequest).path)) + mockFileManager.verifyFunctionCall(.createDirectory(path: responsesDirectory(for: mockEndpointRequest).path)) + + XCTAssertEqual(mockFileDataWriter.receivedURL, fileUrl(for: mockEndpointRequest)) } func testStoredDataForGetRequestWithJSONResponse() async throws { @@ -108,28 +139,22 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted - let fileDataWriterSpy = MockFileDataWriter() - fileDataWriterSpy.writeClosure = { + let mockFileDataWriter = MockFileDataWriter() + mockFileDataWriter.writeClosure = { expectation.fulfill() } let processor = EndpointRequestStorageProcessor( - fileManager: fileManager, - fileDataWriter: fileDataWriterSpy, + fileManager: mockFileManager, + fileDataWriter: mockFileDataWriter, jsonEncoder: encoder ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) await fulfillment(of: [expectation], timeout: 20) - let fileUrl = fileUrl(for: mockEndpointRequest) - - guard let data = fileManager.contents(atPath: fileUrl.path) else { - XCTAssert(false, "File doesn't exist") - return - } - - let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: data) + let receivedData = try XCTUnwrap(mockFileDataWriter.receivedData) + let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: receivedData) XCTAssertEqual(model.statusCode, 200) XCTAssertEqual(model.method, "GET") @@ -165,8 +190,8 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let mockResponse = (mockResponseData, mockURLResponse) let expectation = expectation(description: "Data was written") - let fileDataWriterSpy = MockFileDataWriter() - fileDataWriterSpy.writeClosure = { + let mockFileDataWriter = MockFileDataWriter() + mockFileDataWriter.writeClosure = { expectation.fulfill() } @@ -174,22 +199,16 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { encoder.outputFormatting = .prettyPrinted let processor = EndpointRequestStorageProcessor( - fileManager: fileManager, - fileDataWriter: fileDataWriterSpy, + fileManager: mockFileManager, + fileDataWriter: mockFileDataWriter, jsonEncoder: encoder ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) await fulfillment(of: [expectation], timeout: 20) - let fileUrl = fileUrl(for: mockEndpointRequest) - - guard let data = fileManager.contents(atPath: fileUrl.path) else { - XCTAssert(false, "File doesn't exist") - return - } - - let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: data) + let receivedData = try XCTUnwrap(mockFileDataWriter.receivedData) + let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: receivedData) XCTAssertEqual(model.statusCode, 200) XCTAssertEqual(model.method, "GET") @@ -222,8 +241,8 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let expectation = expectation(description: "Data was written") - let fileDataWriterSpy = MockFileDataWriter() - fileDataWriterSpy.writeClosure = { + let mockFileDataWriter = MockFileDataWriter() + mockFileDataWriter.writeClosure = { expectation.fulfill() } @@ -231,8 +250,8 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { encoder.outputFormatting = .prettyPrinted let processor = EndpointRequestStorageProcessor( - fileManager: fileManager, - fileDataWriter: fileDataWriterSpy, + fileManager: mockFileManager, + fileDataWriter: mockFileDataWriter, jsonEncoder: encoder ) @@ -240,15 +259,10 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { await fulfillment(of: [expectation], timeout: 20) - let fileUrl = fileUrl(for: mockEndpointRequest) + let receivedData = try XCTUnwrap(mockFileDataWriter.receivedData) + + let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: receivedData) - guard let data = fileManager.contents(atPath: fileUrl.path) else { - XCTAssert(false, "File doesn't exist") - return - } - - let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: data) - XCTAssertEqual(model.statusCode, 404) XCTAssertEqual(model.method, "GET") XCTAssertEqual(model.path, mockEndpointRequest.endpoint.path) @@ -275,8 +289,8 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let expectation = expectation(description: "Data was written") - let fileDataWriterSpy = MockFileDataWriter() - fileDataWriterSpy.writeClosure = { + let mockFileDataWriter = MockFileDataWriter() + mockFileDataWriter.writeClosure = { expectation.fulfill() } @@ -284,22 +298,17 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { encoder.outputFormatting = .prettyPrinted let processor = EndpointRequestStorageProcessor( - fileManager: fileManager, - fileDataWriter: fileDataWriterSpy, + fileManager: mockFileManager, + fileDataWriter: mockFileDataWriter, jsonEncoder: encoder ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) await fulfillment(of: [expectation], timeout: 20) - let fileUrl = fileUrl(for: mockEndpointRequest) + let receivedData = try XCTUnwrap(mockFileDataWriter.receivedData) - guard let data = fileManager.contents(atPath: fileUrl.path) else { - XCTAssert(false, "File doesn't exist") - return - } - - let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: data) + let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: receivedData) let mockRequestBody = try mockEndpointRequest.endpoint.encodeBody()! XCTAssertEqual(model.statusCode, 200) @@ -326,10 +335,14 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { private extension EndpointRequestStorageProcessorTests { func fileUrl(for endpointRequest: EndpointRequest) -> URL { - let responsesDirectory = fileManager.temporaryDirectory.appendingPathComponent("responses") let fileName = "\(endpointRequest.sessionId)_\(endpointRequest.endpoint.identifier)_0" + return responsesDirectory(for: endpointRequest) + .appendingPathComponent("\(fileName).json") + } + + func responsesDirectory(for endpointRequest: EndpointRequest) -> URL { + let responsesDirectory = mockFileManager.temporaryDirectory.appendingPathComponent("responses") return responsesDirectory .appendingPathComponent(endpointRequest.sessionId) - .appendingPathComponent("\(fileName).json") } } diff --git a/Tests/NetworkingTests/Mocks/MockFileManager.swift b/Tests/NetworkingTests/Mocks/MockFileManager.swift index 8953c11b..d51633c5 100644 --- a/Tests/NetworkingTests/Mocks/MockFileManager.swift +++ b/Tests/NetworkingTests/Mocks/MockFileManager.swift @@ -10,43 +10,62 @@ import XCTest /// A subclass of `FileManager` where the file existence is based on a dictionary whose key is the file path. final class MockFileManager: FileManager { + enum Function: Equatable { + case fileExists(path: String) + case createDirectory(path: String) + case contentsOfDirectory(path: String) + case removeItem(path: String) + } + + /// Mocked or received data var dataByFilePath: [String: Data] = [:] - /// The mocked results for the `attributesOfItem(atPath:)`. - private var attributesOfItemResults = [String: [FileAttributeKey: Any]]() + /// Received functions + private var functionCallHistory: [Function] = [] override func fileExists(atPath path: String) -> Bool { - dataByFilePath[path] != nil + recordCall(.fileExists(path: path)) + return dataByFilePath[path] != nil } - override func createFile(atPath path: String, contents data: Data?, attributes attr: [FileAttributeKey: Any]? = nil) -> Bool { - dataByFilePath[path] = data - return true - } + override func createDirectory(atPath path: String, withIntermediateDirectories createIntermediates: Bool, attributes: [FileAttributeKey: Any]? = nil) throws { + recordCall(.createDirectory(path: path)) - override func removeItem(at URL: URL) throws { - dataByFilePath.removeValue(forKey: URL.path) + // Simulate directory creation by adding an empty entry + dataByFilePath[path] = Data() } - override func attributesOfItem(atPath path: String) throws -> [FileAttributeKey: Any] { - guard let attributes = attributesOfItemResults[path] else { - XCTFail("No mocked attributes for path \(path)") - return [:] - } + override func contentsOfDirectory(atPath path: String) throws -> [String] { + recordCall(.contentsOfDirectory(path: path)) - return attributes + // Return file names in the specified directory + return dataByFilePath.keys + .filter { $0.hasPrefix(path) } + .map { $0.replacingOccurrences(of: path, with: "") } } - func reset() { - dataByFilePath = [:] + override func removeItem(atPath path: String) throws { + recordCall(.removeItem(path: path)) + + dataByFilePath.removeValue(forKey: path) } } -// MARK: - Mocking - extension MockFileManager { - /// Sets the return value when `attributesOfItem(atPath:)` is called. - func whenRetrievingAttributesOfItem(atPath path: String, thenReturn: [FileAttributeKey: Any]) { - attributesOfItemResults[path] = thenReturn + private func recordCall(_ method: Function) { + functionCallHistory.append(method) + } + + func verifyFunctionCall(_ expectedMethod: Function, file: StaticString = #file, line: UInt = #line) { + guard functionCallHistory.contains(where: { $0 == expectedMethod }) else { + XCTFail("Expected to have called \(expectedMethod). Received: \(functionCallHistory)", file: file, line: line) + print(functionCallHistory) + return + } + } + + func reset() { + dataByFilePath = [:] + functionCallHistory = [] } } From a90c35ce948ba4c2020b551fcd1adedf61105b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 1 Feb 2024 18:30:04 +0100 Subject: [PATCH 46/52] [feat]: Use latest GA runner --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8a57516..e364fe7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - name: 'iOS 17.2' destination: 'OS=17.2,name=iPhone 15 Pro' xcode: 'Xcode_15.2' - runsOn: macos-13 + runsOn: macos-14 # - name: 'iOS 16.4' # destination: 'OS=16.4,name=iPhone 14 Pro' # xcode: 'Xcode_14.3.1' @@ -34,7 +34,7 @@ jobs: - name: 'macOS 13, Xcode 15.2' destination: 'platform=macOS' xcode: 'Xcode_15.2' - runsOn: macos-13 + runsOn: macos-14 steps: - uses: actions/checkout@v4 From a79c45c4ad38811ecab59a64fea94cf8a98a01e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 1 Feb 2024 18:41:46 +0100 Subject: [PATCH 47/52] [feat]: Increase timeouts --- .../EndpointRequestStorageProcessorTests.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift index ab4dea68..415f80f0 100644 --- a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift +++ b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift @@ -116,11 +116,11 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - await fulfillment(of: [expectation], timeout: 20) + await fulfillment(of: [expectation], timeout: 60) mockFileManager.verifyFunctionCall(.fileExists(path: responsesDirectory(for: mockEndpointRequest).path)) mockFileManager.verifyFunctionCall(.createDirectory(path: responsesDirectory(for: mockEndpointRequest).path)) - + XCTAssertEqual(mockFileDataWriter.receivedURL, fileUrl(for: mockEndpointRequest)) } @@ -151,7 +151,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - await fulfillment(of: [expectation], timeout: 20) + await fulfillment(of: [expectation], timeout: 60) let receivedData = try XCTUnwrap(mockFileDataWriter.receivedData) let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: receivedData) @@ -205,7 +205,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - await fulfillment(of: [expectation], timeout: 20) + await fulfillment(of: [expectation], timeout: 60) let receivedData = try XCTUnwrap(mockFileDataWriter.receivedData) let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: receivedData) @@ -257,7 +257,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { _ = await processor.process(mockError, for: mockEndpointRequest) - await fulfillment(of: [expectation], timeout: 20) + await fulfillment(of: [expectation], timeout: 60) let receivedData = try XCTUnwrap(mockFileDataWriter.receivedData) @@ -304,7 +304,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { ) _ = try await processor.process(mockResponse, with: mockURLRequest, for: mockEndpointRequest) - await fulfillment(of: [expectation], timeout: 20) + await fulfillment(of: [expectation], timeout: 60) let receivedData = try XCTUnwrap(mockFileDataWriter.receivedData) From 5ffc432337a9415ab7c372a742d6aeaf9fc8cdbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 1 Feb 2024 18:56:28 +0100 Subject: [PATCH 48/52] [feat]: Revert back to macos-13 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e364fe7c..dd582333 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - name: 'iOS 17.2' destination: 'OS=17.2,name=iPhone 15 Pro' xcode: 'Xcode_15.2' - runsOn: macos-14 + runsOn: macos-13 # - name: 'iOS 16.4' # destination: 'OS=16.4,name=iPhone 14 Pro' # xcode: 'Xcode_14.3.1' From dfb55642f5dd637f6829b53ede68ad8ffc087d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 1 Feb 2024 20:59:09 +0100 Subject: [PATCH 49/52] [feat]: Remove write in mock --- Tests/NetworkingTests/Mocks/MockFileDataWriter.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift b/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift index 17c56c76..c31085bb 100644 --- a/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift +++ b/Tests/NetworkingTests/Mocks/MockFileDataWriter.swift @@ -20,7 +20,6 @@ final class MockFileDataWriter: FileDataWriting { writeCalled = true receivedData = data receivedURL = url - try data.write(to: url) writeClosure?() } From 0d581a8dcf5136cd88843e33f1cc5354ada6b66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Thu, 1 Feb 2024 20:59:33 +0100 Subject: [PATCH 50/52] [feat]: Use macos-14 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd582333..e364fe7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - name: 'iOS 17.2' destination: 'OS=17.2,name=iPhone 15 Pro' xcode: 'Xcode_15.2' - runsOn: macos-13 + runsOn: macos-14 # - name: 'iOS 16.4' # destination: 'OS=16.4,name=iPhone 14 Pro' # xcode: 'Xcode_14.3.1' From 820b8f389c012778a40b4ef541dba9593e5c54f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Mon, 5 Feb 2024 15:32:20 +0100 Subject: [PATCH 51/52] [feat]: Remove unused file --- .../Misc/MockResponseProviderError.swift | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 Sources/Networking/Misc/MockResponseProviderError.swift diff --git a/Sources/Networking/Misc/MockResponseProviderError.swift b/Sources/Networking/Misc/MockResponseProviderError.swift deleted file mode 100644 index 0319461a..00000000 --- a/Sources/Networking/Misc/MockResponseProviderError.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// MockResponseProviderError.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 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. - case unableToConstructResponse -} From b2deb96eac2d2879fb0f3257a522c2a9ad9a9a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kodes=CC=8C?= Date: Mon, 5 Feb 2024 15:35:24 +0100 Subject: [PATCH 52/52] [feat]: Assert received URLs --- .../EndpointRequestStorageProcessorTests.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift index 415f80f0..bbe62d3b 100644 --- a/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift +++ b/Tests/NetworkingTests/EndpointRequestStorageProcessorTests.swift @@ -166,6 +166,8 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { XCTAssertEqual(model.responseBody, mockResponseData) XCTAssertEqual(model.responseBodyString, String(data: mockResponseData, encoding: .utf8)) XCTAssertEqual(model.responseHeaders, ["mockResponseHeader": "mock"]) + + XCTAssertEqual(mockFileDataWriter.receivedURL, fileUrl(for: mockEndpointRequest)) } func testStoredDataForGetRequestWithImageResponse() async throws { @@ -220,6 +222,8 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { XCTAssertEqual(model.responseBody, mockResponseData) XCTAssertEqual(model.responseBodyString, String(data: mockResponseData, encoding: .utf8)) XCTAssertEqual(model.responseHeaders, ["mockResponseHeader": "mock"]) + + XCTAssertEqual(mockFileDataWriter.receivedURL, fileUrl(for: mockEndpointRequest)) } func testStoredDataForGetRequestWithErrorResponse() async throws { @@ -273,6 +277,8 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { XCTAssertEqual(model.responseBody, mockResponseData) XCTAssertEqual(model.responseBodyString, String(data: mockResponseData, encoding: .utf8)) XCTAssertEqual(model.responseHeaders, ["mockResponseHeader": "mock"]) + + XCTAssertEqual(mockFileDataWriter.receivedURL, fileUrl(for: mockEndpointRequest)) } func testStoredDataForPostRequest() async throws { @@ -309,7 +315,7 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { let receivedData = try XCTUnwrap(mockFileDataWriter.receivedData) let model = try JSONDecoder().decode(EndpointRequestStorageModel.self, from: receivedData) - let mockRequestBody = try mockEndpointRequest.endpoint.encodeBody()! + let mockRequestBody = try XCTUnwrap(try mockEndpointRequest.endpoint.encodeBody()) XCTAssertEqual(model.statusCode, 200) XCTAssertEqual(model.method, "POST") @@ -321,6 +327,8 @@ final class EndpointRequestStorageProcessorTests: XCTestCase { XCTAssertEqual(model.responseBody, mockResponseData) XCTAssertEqual(model.responseBodyString, String(data: mockResponseData, encoding: .utf8)) XCTAssertEqual(model.responseHeaders, ["mockResponseHeader": "mock"]) + + XCTAssertEqual(mockFileDataWriter.receivedURL, fileUrl(for: mockEndpointRequest)) } // swiftlint:enable force_unwrapping