From f13e31e45a23c68ae6a3ecdede08a2f5a0bd2360 Mon Sep 17 00:00:00 2001 From: UllashPodder Date: Tue, 14 Jul 2026 17:45:24 +0100 Subject: [PATCH 1/3] added resository protocol, endpoint, api error and endpoint test --- .../Stream-iOS/Core/Networking/APIError.swift | 13 ++++++++++ .../Core/Networking/CharacterRepository.swift | 10 +++++++ .../Stream-iOS/Core/Networking/Endpoint.swift | 26 +++++++++++++++++++ .../URLSessionCharacterRepository.swift | 8 ++++++ .../Stream-iOSTests/EndpointTests.swift | 16 ++++++++++++ .../Mocks/MockCharacterRepository.swift | 19 ++++++++++++++ 6 files changed, 92 insertions(+) create mode 100644 Stream-iOS/Stream-iOS/Core/Networking/APIError.swift create mode 100644 Stream-iOS/Stream-iOS/Core/Networking/CharacterRepository.swift create mode 100644 Stream-iOS/Stream-iOS/Core/Networking/Endpoint.swift create mode 100644 Stream-iOS/Stream-iOS/Core/Networking/URLSessionCharacterRepository.swift create mode 100644 Stream-iOS/Stream-iOSTests/EndpointTests.swift create mode 100644 Stream-iOS/Stream-iOSTests/Mocks/MockCharacterRepository.swift diff --git a/Stream-iOS/Stream-iOS/Core/Networking/APIError.swift b/Stream-iOS/Stream-iOS/Core/Networking/APIError.swift new file mode 100644 index 0000000..2cefb24 --- /dev/null +++ b/Stream-iOS/Stream-iOS/Core/Networking/APIError.swift @@ -0,0 +1,13 @@ +// +// APIError.swift +// Stream-iOS +// +// Created by Ullash Podder on 14/07/2026. +// + +import Foundation +enum APIError: Error, Equatable { + case invalidResponse + case httpStatus(Int) + case decodingFailed +} diff --git a/Stream-iOS/Stream-iOS/Core/Networking/CharacterRepository.swift b/Stream-iOS/Stream-iOS/Core/Networking/CharacterRepository.swift new file mode 100644 index 0000000..53cfb66 --- /dev/null +++ b/Stream-iOS/Stream-iOS/Core/Networking/CharacterRepository.swift @@ -0,0 +1,10 @@ +// +// CharacterRepository.swift +// Stream-iOS +// +// Created by Ullash Podder on 14/07/2026. +// + +protocol CharacterRepository: Sendable { + func fetchCharacters(page: Int) async throws -> PagedResponse +} diff --git a/Stream-iOS/Stream-iOS/Core/Networking/Endpoint.swift b/Stream-iOS/Stream-iOS/Core/Networking/Endpoint.swift new file mode 100644 index 0000000..368afc1 --- /dev/null +++ b/Stream-iOS/Stream-iOS/Core/Networking/Endpoint.swift @@ -0,0 +1,26 @@ +// +// Endpoint.swift +// Stream-iOS +// +// Created by Ullash Podder on 14/07/2026. +// + +import Foundation + +struct Endpoint{ + let path : String + let queryItems : [URLQueryItem] + var url:URL? { + var components = URLComponents() + components.scheme = "https" + components.host = "rickandmortyapi.com" + components.path = "api/" + path + components.queryItems = queryItems.isEmpty ? nil : queryItems + return components.url + } +} +extension Endpoint { + static func characters(page:Int) -> Endpoint{ + Endpoint(path: "character", queryItems: [URLQueryItem(name: "page", value: "\(page)")]) + } +} diff --git a/Stream-iOS/Stream-iOS/Core/Networking/URLSessionCharacterRepository.swift b/Stream-iOS/Stream-iOS/Core/Networking/URLSessionCharacterRepository.swift new file mode 100644 index 0000000..7ef6e29 --- /dev/null +++ b/Stream-iOS/Stream-iOS/Core/Networking/URLSessionCharacterRepository.swift @@ -0,0 +1,8 @@ +// +// URLSessionCharacterRepository.swift +// Stream-iOS +// +// Created by Ullash Podder on 14/07/2026. +// + +import Foundation diff --git a/Stream-iOS/Stream-iOSTests/EndpointTests.swift b/Stream-iOS/Stream-iOSTests/EndpointTests.swift new file mode 100644 index 0000000..3f17340 --- /dev/null +++ b/Stream-iOS/Stream-iOSTests/EndpointTests.swift @@ -0,0 +1,16 @@ +// +// EndpointTests.swift +// Stream-iOS +// +// Created by Ullash Podder on 14/07/2026. +// +import Testing +import Foundation +@testable import Stream_iOS + +struct EndpointTests { + @Test func buildsCharacterPageURL() throws { + let url = #require(Endpoint.characters(page: 3).url) + #expect(url.absoluteString == "https://rickandmortyapi.com/api/character?page=3") + } +} diff --git a/Stream-iOS/Stream-iOSTests/Mocks/MockCharacterRepository.swift b/Stream-iOS/Stream-iOSTests/Mocks/MockCharacterRepository.swift new file mode 100644 index 0000000..9b23812 --- /dev/null +++ b/Stream-iOS/Stream-iOSTests/Mocks/MockCharacterRepository.swift @@ -0,0 +1,19 @@ +// +// MockCharacterRepository.swift +// Stream-iOS +// +// Created by Ullash Podder on 14/07/2026. +// +//import Testing +//import Foundation +@testable import Stream_iOS + +final class MockCharacterRepository: CharacterRepository, @unchecked Sendable { + var result: Result, Error> + private(set) var requestedPages: [Int] = [] + + func fetchCharacters(page: Int) async throws -> PagedResponse { + requestedPages.append(page) + return try result.get() + } +} From 6b545ddab5f9b3f5660ca37b073d225c0f9a5ed5 Mon Sep 17 00:00:00 2001 From: UllashPodder Date: Wed, 15 Jul 2026 07:51:13 +0100 Subject: [PATCH 2/3] Fix, in MockCharacterRepository.swift, added failure case --- .../Mocks/MockCharacterRepository.swift | 2 +- .../Stream-iOSTests/Stream_iOSTests.swift | 19 ------------------- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 Stream-iOS/Stream-iOSTests/Stream_iOSTests.swift diff --git a/Stream-iOS/Stream-iOSTests/Mocks/MockCharacterRepository.swift b/Stream-iOS/Stream-iOSTests/Mocks/MockCharacterRepository.swift index 9b23812..dbd7308 100644 --- a/Stream-iOS/Stream-iOSTests/Mocks/MockCharacterRepository.swift +++ b/Stream-iOS/Stream-iOSTests/Mocks/MockCharacterRepository.swift @@ -9,7 +9,7 @@ @testable import Stream_iOS final class MockCharacterRepository: CharacterRepository, @unchecked Sendable { - var result: Result, Error> + var result: Result, Error> = .failure(APIError.invalidResponse) private(set) var requestedPages: [Int] = [] func fetchCharacters(page: Int) async throws -> PagedResponse { diff --git a/Stream-iOS/Stream-iOSTests/Stream_iOSTests.swift b/Stream-iOS/Stream-iOSTests/Stream_iOSTests.swift deleted file mode 100644 index 2962f51..0000000 --- a/Stream-iOS/Stream-iOSTests/Stream_iOSTests.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Stream_iOSTests.swift -// Stream-iOSTests -// -// Created by Ullash Podder on 10/07/2026. -// - -import Testing -@testable import Stream_iOS - -struct Stream_iOSTests { - - @Test func example() async throws { - // Write your test here and use APIs like `#expect(...)` to check expected conditions. - // Swift Testing Documentation - // https://developer.apple.com/documentation/testing - } - -} From 4aa82e43a1268b14c534d9cffebda3a22afa5404 Mon Sep 17 00:00:00 2001 From: UllashPodder Date: Wed, 15 Jul 2026 08:11:15 +0100 Subject: [PATCH 3/3] added fix to endpoint component for test --- Stream-iOS/Stream-iOS/Core/Networking/Endpoint.swift | 4 ++-- Stream-iOS/Stream-iOSTests/EndpointTests.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Stream-iOS/Stream-iOS/Core/Networking/Endpoint.swift b/Stream-iOS/Stream-iOS/Core/Networking/Endpoint.swift index 368afc1..5153f7a 100644 --- a/Stream-iOS/Stream-iOS/Core/Networking/Endpoint.swift +++ b/Stream-iOS/Stream-iOS/Core/Networking/Endpoint.swift @@ -14,13 +14,13 @@ struct Endpoint{ var components = URLComponents() components.scheme = "https" components.host = "rickandmortyapi.com" - components.path = "api/" + path + components.path = "/api" + path components.queryItems = queryItems.isEmpty ? nil : queryItems return components.url } } extension Endpoint { static func characters(page:Int) -> Endpoint{ - Endpoint(path: "character", queryItems: [URLQueryItem(name: "page", value: "\(page)")]) + Endpoint(path: "/character", queryItems: [URLQueryItem(name: "page", value: "\(page)")]) } } diff --git a/Stream-iOS/Stream-iOSTests/EndpointTests.swift b/Stream-iOS/Stream-iOSTests/EndpointTests.swift index 3f17340..13ff3a0 100644 --- a/Stream-iOS/Stream-iOSTests/EndpointTests.swift +++ b/Stream-iOS/Stream-iOSTests/EndpointTests.swift @@ -9,8 +9,8 @@ import Foundation @testable import Stream_iOS struct EndpointTests { - @Test func buildsCharacterPageURL() throws { - let url = #require(Endpoint.characters(page: 3).url) + @Test func buildsCharacterPageURL() throws { // <- throws here + let url = try #require(Endpoint.characters(page: 3).url) #expect(url.absoluteString == "https://rickandmortyapi.com/api/character?page=3") } }