diff --git a/NodeKit.xcodeproj/project.pbxproj b/NodeKit.xcodeproj/project.pbxproj index 71023460..77582ce9 100644 --- a/NodeKit.xcodeproj/project.pbxproj +++ b/NodeKit.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 390E696C2A13654A007F2304 /* RequestSenderNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390E696B2A13654A007F2304 /* RequestSenderNode.swift */; }; - 390E69702A136586007F2304 /* NodeParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390E696D2A136586007F2304 /* NodeParameterEncoding.swift */; }; 390E69712A136586007F2304 /* RequestEncodingNodeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390E696E2A136586007F2304 /* RequestEncodingNodeError.swift */; }; 390E69722A136586007F2304 /* UrlJsonRequestEncodingNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390E696F2A136586007F2304 /* UrlJsonRequestEncodingNode.swift */; }; 390E69752A136591007F2304 /* RequestEncodingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390E69742A136591007F2304 /* RequestEncodingModel.swift */; }; @@ -68,6 +67,21 @@ 5060A4922BC43D71004E84E2 /* LoggerStreamNodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5060A4912BC43D71004E84E2 /* LoggerStreamNodeTests.swift */; }; 5060A4942BC43FBD004E84E2 /* LoggerNodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5060A4932BC43FBD004E84E2 /* LoggerNodeTests.swift */; }; 5060A4962BC44225004E84E2 /* EntryInputDtoOutputNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5060A4952BC44225004E84E2 /* EntryInputDtoOutputNode.swift */; }; + 5060A4982BC44B61004E84E2 /* DataLoadingResponseProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5060A4972BC44B61004E84E2 /* DataLoadingResponseProcessorTests.swift */; }; + 508169D52BC566D000A43F3D /* ArrayDTODecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169D42BC566D000A43F3D /* ArrayDTODecodableTests.swift */; }; + 508169D72BC56B9200A43F3D /* RawDecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169D62BC56B9200A43F3D /* RawDecodableTests.swift */; }; + 508169D92BC56F5C00A43F3D /* ServerRequestsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169D82BC56F5C00A43F3D /* ServerRequestsManagerTests.swift */; }; + 508169DB2BC5707100A43F3D /* MultipartModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169DA2BC5707100A43F3D /* MultipartModelTests.swift */; }; + 508169DD2BC5725C00A43F3D /* DTOConvertibleMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169DC2BC5725C00A43F3D /* DTOConvertibleMock.swift */; }; + 508169DF2BC5731100A43F3D /* RawMappableMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169DE2BC5731100A43F3D /* RawMappableMock.swift */; }; + 508169E12BC57CDF00A43F3D /* UrlChainConfigModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169E02BC57CDF00A43F3D /* UrlChainConfigModelTests.swift */; }; + 508169E32BC57E4800A43F3D /* ArrayRawMappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169E22BC57E4800A43F3D /* ArrayRawMappableTests.swift */; }; + 508169E52BC582EA00A43F3D /* DictionaryDTOConvertibleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169E42BC582EA00A43F3D /* DictionaryDTOConvertibleTests.swift */; }; + 508169E72BC584B300A43F3D /* DTODecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169E62BC584B300A43F3D /* DTODecodableTests.swift */; }; + 508169E92BC5861500A43F3D /* LogableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169E82BC5861500A43F3D /* LogableTests.swift */; }; + 508169EB2BC588D300A43F3D /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169EA2BC588D300A43F3D /* Array+Extension.swift */; }; + 508169ED2BC5891700A43F3D /* URLRoutingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169EC2BC5891700A43F3D /* URLRoutingTests.swift */; }; + 508169EF2BC58B9E00A43F3D /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508169EE2BC58B9E00A43F3D /* ParameterEncoding.swift */; }; 509135932BC44EF400EC17D2 /* ThirdParty in Frameworks */ = {isa = PBXBuildFile; productRef = 509135922BC44EF400EC17D2 /* ThirdParty */; }; 509135962BC44F9300EC17D2 /* ThirdParty in Frameworks */ = {isa = PBXBuildFile; productRef = 509135952BC44F9300EC17D2 /* ThirdParty */; }; 50B6838F2BBF3615001F7EA3 /* AccessSafeNodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B6838E2BBF3615001F7EA3 /* AccessSafeNodeTests.swift */; }; @@ -208,7 +222,6 @@ /* Begin PBXFileReference section */ 390E696B2A13654A007F2304 /* RequestSenderNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestSenderNode.swift; sourceTree = ""; }; - 390E696D2A136586007F2304 /* NodeParameterEncoding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NodeParameterEncoding.swift; sourceTree = ""; }; 390E696E2A136586007F2304 /* RequestEncodingNodeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestEncodingNodeError.swift; sourceTree = ""; }; 390E696F2A136586007F2304 /* UrlJsonRequestEncodingNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UrlJsonRequestEncodingNode.swift; sourceTree = ""; }; 390E69742A136591007F2304 /* RequestEncodingModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestEncodingModel.swift; sourceTree = ""; }; @@ -268,6 +281,21 @@ 5060A4912BC43D71004E84E2 /* LoggerStreamNodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggerStreamNodeTests.swift; sourceTree = ""; }; 5060A4932BC43FBD004E84E2 /* LoggerNodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggerNodeTests.swift; sourceTree = ""; }; 5060A4952BC44225004E84E2 /* EntryInputDtoOutputNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryInputDtoOutputNode.swift; sourceTree = ""; }; + 5060A4972BC44B61004E84E2 /* DataLoadingResponseProcessorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataLoadingResponseProcessorTests.swift; sourceTree = ""; }; + 508169D42BC566D000A43F3D /* ArrayDTODecodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayDTODecodableTests.swift; sourceTree = ""; }; + 508169D62BC56B9200A43F3D /* RawDecodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawDecodableTests.swift; sourceTree = ""; }; + 508169D82BC56F5C00A43F3D /* ServerRequestsManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerRequestsManagerTests.swift; sourceTree = ""; }; + 508169DA2BC5707100A43F3D /* MultipartModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipartModelTests.swift; sourceTree = ""; }; + 508169DC2BC5725C00A43F3D /* DTOConvertibleMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DTOConvertibleMock.swift; sourceTree = ""; }; + 508169DE2BC5731100A43F3D /* RawMappableMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawMappableMock.swift; sourceTree = ""; }; + 508169E02BC57CDF00A43F3D /* UrlChainConfigModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlChainConfigModelTests.swift; sourceTree = ""; }; + 508169E22BC57E4800A43F3D /* ArrayRawMappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayRawMappableTests.swift; sourceTree = ""; }; + 508169E42BC582EA00A43F3D /* DictionaryDTOConvertibleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DictionaryDTOConvertibleTests.swift; sourceTree = ""; }; + 508169E62BC584B300A43F3D /* DTODecodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DTODecodableTests.swift; sourceTree = ""; }; + 508169E82BC5861500A43F3D /* LogableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogableTests.swift; sourceTree = ""; }; + 508169EA2BC588D300A43F3D /* Array+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = ""; }; + 508169EC2BC5891700A43F3D /* URLRoutingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRoutingTests.swift; sourceTree = ""; }; + 508169EE2BC58B9E00A43F3D /* ParameterEncoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParameterEncoding.swift; sourceTree = ""; }; 50B6838E2BBF3615001F7EA3 /* AccessSafeNodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessSafeNodeTests.swift; sourceTree = ""; }; 50B683912BBF3816001F7EA3 /* TransportUrlRequest+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TransportUrlRequest+Equatable.swift"; sourceTree = ""; }; 50C8EB272BBD7A2200C5CB93 /* AsyncStreamCombineNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStreamCombineNode.swift; sourceTree = ""; }; @@ -468,6 +496,7 @@ 502F9D992BAA389500151A8D /* LoggingContextTests.swift */, 50528E302BAE1F9800E86CB6 /* LoggingContextMock.swift */, 50C8EB3D2BBEC1E700C5CB93 /* DispatchQueue+Extension.swift */, + 508169EA2BC588D300A43F3D /* Array+Extension.swift */, ); path = Utils; sourceTree = ""; @@ -547,6 +576,8 @@ 5060A45D2BC3D999004E84E2 /* RawDecodableMock.swift */, 5060A45F2BC3DB99004E84E2 /* DTODecodableMock.swift */, 5060A46B2BC3EB26004E84E2 /* URLSessionDataTaskActorMock.swift */, + 508169DC2BC5725C00A43F3D /* DTOConvertibleMock.swift */, + 508169DE2BC5731100A43F3D /* RawMappableMock.swift */, ); path = Mocks; sourceTree = ""; @@ -684,8 +715,8 @@ 90B6090C283E1268006F4309 /* Encodings */ = { isa = PBXGroup; children = ( + 508169EE2BC58B9E00A43F3D /* ParameterEncoding.swift */, 390E69732A136591007F2304 /* Models */, - 390E696D2A136586007F2304 /* NodeParameterEncoding.swift */, 390E696E2A136586007F2304 /* RequestEncodingNodeError.swift */, 390E696F2A136586007F2304 /* UrlJsonRequestEncodingNode.swift */, ); @@ -987,6 +1018,17 @@ 5060A48F2BC43A4A004E84E2 /* VoidOutputNodeTests.swift */, 5060A4912BC43D71004E84E2 /* LoggerStreamNodeTests.swift */, 5060A4932BC43FBD004E84E2 /* LoggerNodeTests.swift */, + 5060A4972BC44B61004E84E2 /* DataLoadingResponseProcessorTests.swift */, + 508169D42BC566D000A43F3D /* ArrayDTODecodableTests.swift */, + 508169D62BC56B9200A43F3D /* RawDecodableTests.swift */, + 508169D82BC56F5C00A43F3D /* ServerRequestsManagerTests.swift */, + 508169DA2BC5707100A43F3D /* MultipartModelTests.swift */, + 508169E02BC57CDF00A43F3D /* UrlChainConfigModelTests.swift */, + 508169E22BC57E4800A43F3D /* ArrayRawMappableTests.swift */, + 508169E42BC582EA00A43F3D /* DictionaryDTOConvertibleTests.swift */, + 508169E62BC584B300A43F3D /* DTODecodableTests.swift */, + 508169E82BC5861500A43F3D /* LogableTests.swift */, + 508169EC2BC5891700A43F3D /* URLRoutingTests.swift */, ); path = UnitTests; sourceTree = ""; @@ -1299,6 +1341,7 @@ 90B60910283E1268006F4309 /* UrlNotModifiedTriggerNode.swift in Sources */, 90B6098C283E1287006F4309 /* EncodableRequestModel.swift in Sources */, 90B609A0283E1287006F4309 /* ResponseHttpErrorProcessorNode.swift in Sources */, + 508169EF2BC58B9E00A43F3D /* ParameterEncoding.swift in Sources */, 90B6099F283E1287006F4309 /* HeaderInjectorNode.swift in Sources */, 5005EB212BB8A6D000B670CD /* AsyncNode.swift in Sources */, 50C8EB2A2BBD7DEA00C5CB93 /* CombineNode.swift in Sources */, @@ -1337,7 +1380,6 @@ 90B60915283E1268006F4309 /* UrlETagReaderNode.swift in Sources */, 90B60985283E1287006F4309 /* UrlRequestTrasformatorNode.swift in Sources */, 5060A4962BC44225004E84E2 /* EntryInputDtoOutputNode.swift in Sources */, - 390E69702A136586007F2304 /* NodeParameterEncoding.swift in Sources */, 502F9DA92BAB0CF000151A8D /* TokenRefresherActor.swift in Sources */, 90B609C1283E1287006F4309 /* Log.swift in Sources */, 90B60916283E1268006F4309 /* UrlServiceChainBuilder.swift in Sources */, @@ -1355,40 +1397,50 @@ 90B609F5283E16DC006F4309 /* URLQueryBoolEncodingDefaultStartegyTests.swift in Sources */, 50C8EB322BBD932D00C5CB93 /* CombineStreamNodeTests.swift in Sources */, 90B609FE283E16DC006F4309 /* UrlWithOrderedQuery.swift in Sources */, + 508169DF2BC5731100A43F3D /* RawMappableMock.swift in Sources */, 50C8EB362BBD9CBF00C5CB93 /* NodeResultTests.swift in Sources */, 5019CCE62BC00F9E0050B6DF /* RawEncodableMock.swift in Sources */, + 508169DD2BC5725C00A43F3D /* DTOConvertibleMock.swift in Sources */, 5060A45E2BC3D999004E84E2 /* RawDecodableMock.swift in Sources */, 5060A4942BC43FBD004E84E2 /* LoggerNodeTests.swift in Sources */, 5060A4902BC43A4A004E84E2 /* VoidOutputNodeTests.swift in Sources */, + 508169D52BC566D000A43F3D /* ArrayDTODecodableTests.swift in Sources */, 50B683922BBF3816001F7EA3 /* TransportUrlRequest+Equatable.swift in Sources */, 5019CCFC2BC05D800050B6DF /* DTOEncodableMock.swift in Sources */, 90B609F9283E16DC006F4309 /* CacheReaderNodeTests.swift in Sources */, 5060A4882BC42538004E84E2 /* ModelInputNodeTests.swift in Sources */, 90B60A05283E16DC006F4309 /* UserEntry.swift in Sources */, + 508169E12BC57CDF00A43F3D /* UrlChainConfigModelTests.swift in Sources */, 5019CCF82BC03EF90050B6DF /* AlamofireMultipartFormDataFactoryTests.swift in Sources */, 5060A48A2BC42A0A004E84E2 /* ResponseHttpErrorProcessorNodeTests.swift in Sources */, 5060A4622BC3E386004E84E2 /* UrlCacheWriterNodeTests.swift in Sources */, 50C8EB342BBD951500C5CB93 /* AsyncStreamCombineNodeTests.swift in Sources */, + 508169E92BC5861500A43F3D /* LogableTests.swift in Sources */, 90B60A06283E16DC006F4309 /* AuthModel.swift in Sources */, 5019CCEC2BC01DDB0050B6DF /* RequestEncoderNodeTests.swift in Sources */, 90B60A04283E16DC006F4309 /* AuthModelEntry.swift in Sources */, 50528E312BAE1F9800E86CB6 /* LoggingContextMock.swift in Sources */, 5019CCF42BC02B490050B6DF /* MultipartFormDataMock.swift in Sources */, 50C8EB3A2BBDBBD300C5CB93 /* AsyncCombineNodeTests.swift in Sources */, + 508169EB2BC588D300A43F3D /* Array+Extension.swift in Sources */, + 508169E52BC582EA00A43F3D /* DictionaryDTOConvertibleTests.swift in Sources */, 50B6838F2BBF3615001F7EA3 /* AccessSafeNodeTests.swift in Sources */, 90B609FC283E16DC006F4309 /* UrlETagSaverNodeTests.swift in Sources */, 50C8EB302BBD90DE00C5CB93 /* CombineNodeTests.swift in Sources */, 5019CCEE2BC020DC0050B6DF /* MultipartRequestCreatorNodeTests.swift in Sources */, 5019CCF62BC02E100050B6DF /* MultipartFormDataFactoryMock.swift in Sources */, + 508169D72BC56B9200A43F3D /* RawDecodableTests.swift in Sources */, 90B609FF283E16DC006F4309 /* UrlETagUrlCacheTriggerNodeTests.swift in Sources */, 5005EB332BB9935400B670CD /* AsyncStreamNodeMock.swift in Sources */, 5060A45C2BC3D8D2004E84E2 /* EntryinputDtoOutputNodeTests.swift in Sources */, 90B60A02283E16DC006F4309 /* Infrastructure.swift in Sources */, 50C8EB2C2BBD8EBE00C5CB93 /* CombineNodeMock.swift in Sources */, 50528E332BAE295D00E86CB6 /* TokenRefresherActorMock.swift in Sources */, + 508169D92BC56F5C00A43F3D /* ServerRequestsManagerTests.swift in Sources */, 5060A4752BC3FA02004E84E2 /* URLSessionDataTaskMock.swift in Sources */, 5060A4602BC3DB99004E84E2 /* DTODecodableMock.swift in Sources */, 90B609F3283E16DC006F4309 /* URLQueryDictionaryKeyEncodingDefaultStrategyTests.swift in Sources */, + 508169ED2BC5891700A43F3D /* URLRoutingTests.swift in Sources */, 5060A4822BC4184F004E84E2 /* ResponseProcessorNodeTests.swift in Sources */, 5060A48E2BC43116004E84E2 /* UrlRequestTrasformatorNodeTests.swift in Sources */, 90B609F8283E16DC006F4309 /* EncodingTests.swift in Sources */, @@ -1416,6 +1468,7 @@ 5019CCEA2BC019D60050B6DF /* UrlRouteProviderMock.swift in Sources */, 90B609EF283E16DC006F4309 /* DTOMapperNodeTests.swift in Sources */, 50C8EB3E2BBEC1E700C5CB93 /* DispatchQueue+Extension.swift in Sources */, + 508169DB2BC5707100A43F3D /* MultipartModelTests.swift in Sources */, 5060A4712BC3ED44004E84E2 /* URLProtocolMock.swift in Sources */, 5019CCE82BC018630050B6DF /* MultipartUrlRequestTrasformatorNodeTests.swift in Sources */, 502F9D9D2BAA39F400151A8D /* Log+Equatalbe.swift in Sources */, @@ -1424,8 +1477,10 @@ 90B609FB283E16DC006F4309 /* TestUtls.swift in Sources */, 90B60A08283E16DC006F4309 /* Credentials.swift in Sources */, 90B60A00283E16DC006F4309 /* IfConnectionFailedFromCacheNodeTests.swift in Sources */, + 508169E72BC584B300A43F3D /* DTODecodableTests.swift in Sources */, 50528E372BAE34A400E86CB6 /* TokenRefresherActorTests.swift in Sources */, 5005EB3C2BB9B1C800B670CD /* AsyncNodeTests.swift in Sources */, + 508169E32BC57E4800A43F3D /* ArrayRawMappableTests.swift in Sources */, 5060A4842BC41C89004E84E2 /* VoidIONodeTests.swift in Sources */, 50C8EB522BBF302E00C5CB93 /* ResponseDataPreprocessorNodeTests.swift in Sources */, 50C8EB4C2BBF1C1000C5CB93 /* MetadataProviderMock.swift in Sources */, @@ -1433,6 +1488,7 @@ 90B609F2283E16DC006F4309 /* MockerProxyConfigNodeTests.swift in Sources */, 5060A4802BC4164E004E84E2 /* RequestRouterNodeTests.swift in Sources */, 90B609FA283E16DC006F4309 /* FirstCachePolicyTests.swift in Sources */, + 5060A4982BC44B61004E84E2 /* DataLoadingResponseProcessorTests.swift in Sources */, 5060A4862BC4213E004E84E2 /* LoadIndicatableNodeTests.swift in Sources */, 50528E392BAE39AE00E86CB6 /* AborterMock.swift in Sources */, 5060A4922BC43D71004E84E2 /* LoggerStreamNodeTests.swift in Sources */, diff --git a/NodeKit/Encodings/NodeParameterEncoding.swift b/NodeKit/Encodings/ParameterEncoding.swift similarity index 100% rename from NodeKit/Encodings/NodeParameterEncoding.swift rename to NodeKit/Encodings/ParameterEncoding.swift diff --git a/NodeKitTests/UnitTests/ArrayDTODecodableTests.swift b/NodeKitTests/UnitTests/ArrayDTODecodableTests.swift new file mode 100644 index 00000000..6fb111f9 --- /dev/null +++ b/NodeKitTests/UnitTests/ArrayDTODecodableTests.swift @@ -0,0 +1,207 @@ +// +// ArrayDTODecodableTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class ArrayDTODecodableTests: XCTestCase { + + // MARK: - Lifecycle + + override func tearDown() { + super.tearDown() + DTODecodableMock.flush() + } + + // MARK: - Tests + + func testFromDTO_thenFromDTOCalledInEachElement() throws { + // given + + let array: [RawDecodableMock] = [ + RawDecodableMock(), + RawDecodableMock(), + RawDecodableMock() + ] + + DTODecodableMock.stubbedFromResult = .success(.init()) + + // when + + _ = try? Array.from(dto: array) + + // then + + XCTAssertEqual(DTODecodableMock.invokedFromCount, array.count) + + DTODecodableMock.invokedFromParameterList.enumerated().forEach { + XCTAssertTrue($0.element === array[$0.offset]) + } + } + + func testFromDTO_whenDecodingFailure_thenFailureReceived() throws { + // given + + let array: [RawDecodableMock] = [ + RawDecodableMock() + ] + + var result: [DTODecodableMock]? + var receivedError: Error? + + DTODecodableMock.stubbedFromResult = .failure(MockError.firstError) + + // when + + do { + result = try [DTODecodableMock].from(dto: array) + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .firstError) + } + + func testFromDTO_whenDecodingSuccess_thenSuccessResultReceived() throws { + // given + + let array: [RawDecodableMock] = [ + RawDecodableMock(), + RawDecodableMock(), + RawDecodableMock() + ] + let dtoDecodableMock = DTODecodableMock() + + var result: [DTODecodableMock]? + var receivedError: Error? + + DTODecodableMock.stubbedFromResult = .success(dtoDecodableMock) + + // when + + do { + result = try Array.from(dto: array) + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result) + + XCTAssertNil(receivedError) + XCTAssertEqual(value.count, array.count) + + value.forEach { + XCTAssertTrue($0 === dtoDecodableMock) + } + } + + func testToDTO_thenToDTOCalledInEachElement() throws { + // given + + let array: [DTOEncodableMock>] = [ + .init(), + .init(), + .init() + ] + + array[0].stubbedToDTOResult = .success(.init()) + array[1].stubbedToDTOResult = .success(.init()) + array[2].stubbedToDTOResult = .success(.init()) + + // when + + _ = try? array.toDTO() + + // then + + array.forEach { + XCTAssertEqual($0.invokedToDTOCount, 1) + } + } + + func testToDTO_whenEncodingFailure_thenFailureReceived() throws { + // given + + let array: [DTOEncodableMock>] = [ + .init(), + .init(), + .init() + ] + + array[0].stubbedToDTOResult = .success(.init()) + array[1].stubbedToDTOResult = .failure(MockError.secondError) + array[2].stubbedToDTOResult = .success(.init()) + + var result: [RawEncodableMock]? + var receivedError: Error? + + // when + + do { + result = try array.toDTO() + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .secondError) + } + + func testToDTO_whenEncodingSuccess_thenSuccessResultReceived() throws { + // given + + let array: [DTOEncodableMock>] = [ + .init(), + .init(), + .init() + ] + + let expectedResult: [RawEncodableMock] = [ + .init(), + .init(), + .init() + ] + + array[0].stubbedToDTOResult = .success(expectedResult[0]) + array[1].stubbedToDTOResult = .success(expectedResult[1]) + array[2].stubbedToDTOResult = .success(expectedResult[2]) + + var result: [RawEncodableMock]? + var receivedError: Error? + + // when + + do { + result = try array.toDTO() + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result) + + XCTAssertNil(receivedError) + XCTAssertEqual(value.count, array.count) + + value.enumerated().forEach { + XCTAssertTrue($0.element === expectedResult[$0.offset]) + } + } +} diff --git a/NodeKitTests/UnitTests/ArrayRawMappableTests.swift b/NodeKitTests/UnitTests/ArrayRawMappableTests.swift new file mode 100644 index 00000000..c9cac959 --- /dev/null +++ b/NodeKitTests/UnitTests/ArrayRawMappableTests.swift @@ -0,0 +1,285 @@ +// +// ArrayRawMappableTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class ArrayRawMappableTests: XCTestCase { + + // MARK: - Lifecycle + + override func tearDown() { + super.tearDown() + RawDecodableMock.flush() + } + + // MARK: - Tests + + func testToRaw_thenToRawCalledInEachElement() { + // given + + let sut: [RawEncodableMock] = [ + .init(), + .init(), + .init() + ] + + sut.forEach { + $0.stubbedToRawResult = .success([:]) + } + + // when + + _ = try? sut.toRaw() + + // then + + sut.forEach { + XCTAssertEqual($0.invokedToRawCount, 1) + } + } + + func testToRaw_withConvertationFailure_thenFailureReceived() throws { + // given + + let sut: [RawEncodableMock] = [ + .init(), + .init(), + .init() + ] + + sut[0].stubbedToRawResult = .success([:]) + sut[1].stubbedToRawResult = .success([:]) + sut[2].stubbedToRawResult = .failure(MockError.firstError) + + var result: Json? + var receivedError: Error? + + // when + + do { + result = try sut.toRaw() + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .firstError) + } + + func testToRaw_withConvertationSuccess_thenSuccessReceived() throws { + // given + + let sut: [RawEncodableMock] = [ + .init(), + .init(), + .init() + ] + + let jsonArray = [ + ["TestKey1": "TestValue1"], + ["TestKey2": "TestValue2"], + ["TestKey3": "TestValue3"] + ] + + sut[0].stubbedToRawResult = .success(jsonArray[0]) + sut[1].stubbedToRawResult = .success(jsonArray[1]) + sut[2].stubbedToRawResult = .success(jsonArray[2]) + + var result: Json? + var receivedError: Error? + + // when + + do { + result = try sut.toRaw() + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result as? [String: [[String: String]]]) + + XCTAssertNil(receivedError) + XCTAssertEqual(value, [MappingUtils.arrayJsonKey: jsonArray]) + } + + func testFromRaw_whenRawIsEmpty_thenEmptyReceived() throws { + // given + + var result: [RawDecodableMock]? + var receivedError: Error? + + // when + + do { + result = try [RawDecodableMock].from(raw: [:]) + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result) + + XCTAssertTrue(value.isEmpty) + XCTAssertNil(receivedError) + } + + func testFromRaw_withoutArrayJsonKey_thenErrorReceived() throws { + // given + + let expectedRaw = ["TestKey": "TestValue"] + + var result: [RawDecodableMock]? + var receivedError: Error? + + // when + + do { + result = try [RawDecodableMock].from(raw: expectedRaw) + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? ErrorArrayJsonMappiong) + + XCTAssertNil(result) + + if case let .cantFindKeyInRaw(raw) = error { + let raw = try XCTUnwrap(raw as? [String: String]) + XCTAssertEqual(expectedRaw, raw) + } else { + XCTFail("Не верный результат работы метода") + } + } + + func testFromRaw_withArrayJsonKey_andWithoutJsonArray_thenErrorReceived() throws { + // given + + let expectedRaw = [MappingUtils.arrayJsonKey: "TestValue"] + + var result: [RawDecodableMock]? + var receivedError: Error? + + // when + + do { + result = try [RawDecodableMock].from(raw: expectedRaw) + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? ErrorArrayJsonMappiong) + + XCTAssertNil(result) + + if case let .cantFindKeyInRaw(raw) = error { + let raw = try XCTUnwrap(raw as? [String: String]) + XCTAssertEqual(expectedRaw, raw) + } else { + XCTFail("Не верный результат работы метода") + } + } + + func testFromRaw_withArrayJsonKey_andWithJsonArray_thenRawDecodableFromCalled() throws { + // given + + let expectedArray = [ + ["TestKey1": "TestValue1"], + ["TestKey2": "TestValue2"], + ["TestKey3": "TestValue3"] + ] + RawDecodableMock.stubbedFromResult = .success(.init()) + + // when + + _ = try? [RawDecodableMock].from(raw: [MappingUtils.arrayJsonKey: expectedArray]) + + // then + + XCTAssertEqual(RawDecodableMock.invokedFromCount, expectedArray.count) + + try RawMappableMock.invokedFromParameterList.enumerated().forEach { + let value = try XCTUnwrap($0.element as? [String: String]) + XCTAssertEqual(value, expectedArray[$0.offset]) + } + } + + func testFromRaw_withConvertationFailure_thenFailureReceived() throws { + // given + + let arr = [ + ["TestKey1": "TestValue1"], + ["TestKey2": "TestValue2"], + ["TestKey3": "TestValue3"] + ] + RawDecodableMock.stubbedFromResult = .failure(MockError.secondError) + + var result: [RawDecodableMock]? + var receivedError: Error? + + // when + + do { + result = try [RawDecodableMock].from(raw: [MappingUtils.arrayJsonKey: arr]) + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .secondError) + } + + func testFromRaw_withConvertationSuccess_thenSuccessReceived() throws { + // given + + let arr = [ + ["TestKey1": "TestValue1"], + ["TestKey2": "TestValue2"], + ["TestKey3": "TestValue3"] + ] + let rawDecodableMock = RawDecodableMock() + + RawDecodableMock.stubbedFromResult = .success(rawDecodableMock) + + var result: [RawDecodableMock]? + var receivedError: Error? + + // when + + do { + result = try [RawDecodableMock].from(raw: [MappingUtils.arrayJsonKey: arr]) + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result) + + XCTAssertNil(receivedError) + XCTAssertEqual(value.count, arr.count) + value.forEach { + XCTAssertTrue($0 === rawDecodableMock) + } + } +} diff --git a/NodeKitTests/UnitTests/DTODecodableTests.swift b/NodeKitTests/UnitTests/DTODecodableTests.swift new file mode 100644 index 00000000..6610ba69 --- /dev/null +++ b/NodeKitTests/UnitTests/DTODecodableTests.swift @@ -0,0 +1,111 @@ +// +// DTODecodableTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class DTODecodableTests: XCTestCase { + + // MARK: - Lifecycle + + override func tearDown() { + super.tearDown() + DTODecodableMock.flush() + } + + // MARK: - Tests + + func testFromDto_whenOptionalIsNil_thenNilReceived() { + // given + + var result: DTODecodableMock? + var receivedError: Error? + + // then + + do { + result = try DTODecodableMock?.from(dto: nil) + } catch { + receivedError = error + } + + // then + + XCTAssertNil(result) + XCTAssertNil(receivedError) + } + + func testFromDto_whenOptionalIsNotNil_thenFromRawCalled() throws { + // given + + let expectedInput = RawDecodableMock() + + DTODecodableMock.stubbedFromResult = .success(.init()) + + // then + + _ = try? DTODecodableMock?.from(dto: expectedInput) + + // then + + let input = try XCTUnwrap(DTODecodableMock.invokedFromParameter) + + XCTAssertEqual(DTODecodableMock.invokedFromCount, 1) + XCTAssertTrue(input === expectedInput) + } + + func testFromDto_withDecodingError_thenErrorReceived() throws { + // given + + var result: DTODecodableMock? + var receivedError: Error? + + DTODecodableMock.stubbedFromResult = .failure(MockError.firstError) + + // then + + do { + result = try DTODecodableMock?.from(dto: .init()) + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .firstError) + } + + func testFromDto_withDecodingSuccess_thenSuccessReceived() throws { + // given + + var result: DTODecodableMock? + var receivedError: Error? + + let expectedResult = DTODecodableMock() + + DTODecodableMock.stubbedFromResult = .success(expectedResult) + + // then + + do { + result = try DTODecodableMock?.from(dto: .init()) + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result) + + XCTAssertNil(receivedError) + XCTAssertTrue(value === expectedResult) + } +} diff --git a/NodeKitTests/UnitTests/DataLoadingResponseProcessorTests.swift b/NodeKitTests/UnitTests/DataLoadingResponseProcessorTests.swift new file mode 100644 index 00000000..1335e5f2 --- /dev/null +++ b/NodeKitTests/UnitTests/DataLoadingResponseProcessorTests.swift @@ -0,0 +1,141 @@ +// +// DataLoadingResponseProcessorTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 08.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class DataLoadingResponseProcessorTests: XCTestCase { + + // MARK: - Dependencies + + private var nextNodeMock: AsyncNodeMock! + private var logContextMock: LoggingContextMock! + + // MARK: - Sut + + private var sut: DataLoadingResponseProcessor! + + // MARK: - Lifecycle + + override func setUp() { + super.setUp() + nextNodeMock = AsyncNodeMock() + logContextMock = LoggingContextMock() + sut = DataLoadingResponseProcessor(next: nextNodeMock) + } + + override func tearDown() { + super.tearDown() + nextNodeMock = nil + logContextMock = nil + sut = nil + } + + // MARK: - Tests + + func testAsyncProccess_whenNextIsNil_thenDataReceived() async throws { + // given + + let expectedData = "TestData".data(using: .utf8)! + let sut = DataLoadingResponseProcessor() + let url = URL(string: "www.test.com")! + let response = UrlDataResponse( + request: URLRequest(url: url), + response: HTTPURLResponse(url: url, statusCode: 1, httpVersion: nil, headerFields: [:])!, + data: expectedData, + metrics: nil, + serializationDuration: 1 + ) + + // when + + let result = await sut.process(response, logContext: logContextMock) + + // then + + let value = try XCTUnwrap(result.value) + + XCTAssertEqual(value, expectedData) + } + + func testAsyncProccess_whenNextIsNotNil_thenNextCalled() async throws { + // given + + let url = URL(string: "www.test.com")! + let headers = ["TestHeaderKey": "TestHeaderValue"] + let response = UrlDataResponse( + request: URLRequest(url: url), + response: HTTPURLResponse(url: url, statusCode: 1, httpVersion: nil, headerFields: headers)!, + data: "TestData".data(using: .utf8)!, + metrics: nil, + serializationDuration: 1 + ) + + nextNodeMock.stubbedAsyncProccessResult = .success(()) + + // when + + _ = await sut.process(response, logContext: logContextMock) + + // then + + let input = try XCTUnwrap(nextNodeMock.invokedAsyncProcessParameters?.data) + + XCTAssertEqual(nextNodeMock.invokedAsyncProcessCount, 1) + XCTAssertEqual(input, response) + } + + func testAsyncProccess_whenNextNodeReturnsSuccess_thenSuccessReceived() async throws { + // given + + let expectedData = "TestExpectedData".data(using: .utf8)! + let url = URL(string: "www.test.com")! + let response = UrlDataResponse( + request: URLRequest(url: url), + response: HTTPURLResponse(url: url, statusCode: 1, httpVersion: nil, headerFields: [:])!, + data: expectedData, + metrics: nil, + serializationDuration: 1 + ) + nextNodeMock.stubbedAsyncProccessResult = .success(()) + + // when + + let result = await sut.process(response, logContext: logContextMock) + + // then + + let value = try XCTUnwrap(result.value) + + XCTAssertEqual(value, expectedData) + } + + func testAsyncProccess_whenNextNodeReturnsFailure_thenFailureReceived() async throws { + // given + + let url = URL(string: "www.test.com")! + let response = UrlDataResponse( + request: URLRequest(url: url), + response: HTTPURLResponse(url: url, statusCode: 1, httpVersion: nil, headerFields: [:])!, + data: Data(), + metrics: nil, + serializationDuration: 1 + ) + nextNodeMock.stubbedAsyncProccessResult = .failure(MockError.firstError) + + // when + + let result = await sut.process(response, logContext: logContextMock) + + // then + + let error = try XCTUnwrap(result.error as? MockError) + + XCTAssertEqual(error, .firstError) + } +} diff --git a/NodeKitTests/UnitTests/DictionaryDTOConvertibleTests.swift b/NodeKitTests/UnitTests/DictionaryDTOConvertibleTests.swift new file mode 100644 index 00000000..6c40a3bf --- /dev/null +++ b/NodeKitTests/UnitTests/DictionaryDTOConvertibleTests.swift @@ -0,0 +1,99 @@ +// +// DictionaryDTOConvertibleTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class DictionaryDTOConvertibleTests: XCTestCase { + + // MARK: - Tests + + func testToRaw_thenCorrectJsonReceived() throws { + // given + + let expectedJson = [ + "TestKey1": "TestValue1", + "TestKey2": "TestValue2", + "TestKey3": "TestValue3", + "TestKey4": "TestValue4" + ] + let sut = expectedJson as Json + + // when + + let result = try sut.toRaw() + + // then + + let value = try XCTUnwrap(result as? [String: String]) + XCTAssertEqual(value, expectedJson) + } + + func testFromRaw_thenCorrectDictionaryReceived() throws { + // given + + let expectedJson = [ + "TestKey1": "TestValue1", + "TestKey2": "TestValue2", + "TestKey3": "TestValue3", + "TestKey4": "TestValue4" + ] + let sut = expectedJson as Json + + // when + + let result = try [String: Any].from(raw: sut) + + // then + + let value = try XCTUnwrap(result as? [String: String]) + XCTAssertEqual(value, expectedJson) + } + + func testFromDto_thenCorrectDictionaryReceived() throws { + // given + + let expectedJson = [ + "TestKey1": "TestValue1", + "TestKey2": "TestValue2", + "TestKey3": "TestValue3", + "TestKey4": "TestValue4" + ] + let sut = expectedJson as Json + + // when + + let result = try [String: Any].from(dto: sut) + + // then + + let value = try XCTUnwrap(result as? [String: String]) + XCTAssertEqual(value, expectedJson) + } + + func testToDto_thenCorrectDictionaryReceived() throws { + // given + + let expectedJson = [ + "TestKey1": "TestValue1", + "TestKey2": "TestValue2", + "TestKey3": "TestValue3", + "TestKey4": "TestValue4" + ] + let sut = expectedJson as Json + + // when + + let result = try sut.toDTO() + + // then + + let value = try XCTUnwrap(result as? [String: String]) + XCTAssertEqual(value, expectedJson) + } +} diff --git a/NodeKitTests/UnitTests/EntryinputDtoOutputNodeTests.swift b/NodeKitTests/UnitTests/EntryinputDtoOutputNodeTests.swift index c900bbf3..e7d1e669 100644 --- a/NodeKitTests/UnitTests/EntryinputDtoOutputNodeTests.swift +++ b/NodeKitTests/UnitTests/EntryinputDtoOutputNodeTests.swift @@ -86,7 +86,7 @@ final class EntryInputDtoOutputNodeTests: XCTestCase { let expectedInput = 66 rawEncodableMock.stubbedToRawResult = .success(expectedInput) - nextNodeMock.stubbedAsyncProccessResult = .success(1) + nextNodeMock.stubbedAsyncProccessResult = .success([:]) RawDecodableMock.stubbedFromResult = .success(rawDecodableMock) DTODecodableMock.stubbedFromResult = .success(dtoDecodableMock) @@ -107,7 +107,7 @@ final class EntryInputDtoOutputNodeTests: XCTestCase { func testAsyncProcess_withToRawConvertionSuccess_thenDTOFromCalled() async throws { // given - let expectedInput = 99 + let expectedInput = ["TestKey": "TestValue"] rawEncodableMock.stubbedToRawResult = .success(1) nextNodeMock.stubbedAsyncProccessResult = .success(expectedInput) @@ -121,7 +121,7 @@ final class EntryInputDtoOutputNodeTests: XCTestCase { // then - let input = try XCTUnwrap(RawDecodableMock.invokedFromParameter) + let input = try XCTUnwrap(RawDecodableMock.invokedFromParameter as? [String: String]) XCTAssertEqual(RawDecodableMock.invokedFromCount, 1) XCTAssertEqual(input, expectedInput) @@ -131,7 +131,7 @@ final class EntryInputDtoOutputNodeTests: XCTestCase { // given rawEncodableMock.stubbedToRawResult = .success(1) - nextNodeMock.stubbedAsyncProccessResult = .success(15) + nextNodeMock.stubbedAsyncProccessResult = .success([:]) RawDecodableMock.stubbedFromResult = .failure(MockError.secondError) DTODecodableMock.stubbedFromResult = .success(dtoDecodableMock) @@ -150,7 +150,7 @@ final class EntryInputDtoOutputNodeTests: XCTestCase { // given rawEncodableMock.stubbedToRawResult = .success(1) - nextNodeMock.stubbedAsyncProccessResult = .success(1) + nextNodeMock.stubbedAsyncProccessResult = .success([:]) RawDecodableMock.stubbedFromResult = .failure(MockError.secondError) DTODecodableMock.stubbedFromResult = .success(dtoDecodableMock) @@ -170,7 +170,7 @@ final class EntryInputDtoOutputNodeTests: XCTestCase { // given rawEncodableMock.stubbedToRawResult = .success(1) - nextNodeMock.stubbedAsyncProccessResult = .success(1) + nextNodeMock.stubbedAsyncProccessResult = .success([:]) RawDecodableMock.stubbedFromResult = .success(rawDecodableMock) DTODecodableMock.stubbedFromResult = .success(dtoDecodableMock) @@ -191,7 +191,7 @@ final class EntryInputDtoOutputNodeTests: XCTestCase { // given rawEncodableMock.stubbedToRawResult = .success(1) - nextNodeMock.stubbedAsyncProccessResult = .success(1) + nextNodeMock.stubbedAsyncProccessResult = .success([:]) RawDecodableMock.stubbedFromResult = .success(rawDecodableMock) DTODecodableMock.stubbedFromResult = .failure(MockError.thirdError) @@ -211,7 +211,7 @@ final class EntryInputDtoOutputNodeTests: XCTestCase { // given rawEncodableMock.stubbedToRawResult = .success(1) - nextNodeMock.stubbedAsyncProccessResult = .success(1) + nextNodeMock.stubbedAsyncProccessResult = .success([:]) RawDecodableMock.stubbedFromResult = .success(rawDecodableMock) DTODecodableMock.stubbedFromResult = .success(dtoDecodableMock) diff --git a/NodeKitTests/UnitTests/LoadIndicatableNodeTests.swift b/NodeKitTests/UnitTests/LoadIndicatableNodeTests.swift index 1e272962..2c988150 100644 --- a/NodeKitTests/UnitTests/LoadIndicatableNodeTests.swift +++ b/NodeKitTests/UnitTests/LoadIndicatableNodeTests.swift @@ -114,7 +114,7 @@ final class LoadIndicatableNodeTests: XCTestCase { _ = await sut.process(1, logContext: logContextMock) } - await fulfillment(of: [expectation], timeout: 1) + await fulfillment(of: [expectation], timeout: 3) // then diff --git a/NodeKitTests/UnitTests/LogableTests.swift b/NodeKitTests/UnitTests/LogableTests.swift new file mode 100644 index 00000000..4a5fe7d8 --- /dev/null +++ b/NodeKitTests/UnitTests/LogableTests.swift @@ -0,0 +1,42 @@ +// +// LogableTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class LogableTests: XCTestCase { + + // MARK: - Tests + + func testFlatMap_thenCorrectResultReceived() throws { + // given + + var firstLog = Log("First message", id: "1", delimeter: "/", order: 0) + var secondLog = Log("Second message", id: "1", delimeter: "/", order: 0) + let thirdLog = Log("Third message", id: "1", delimeter: "/", order: 0) + + secondLog.next = thirdLog + firstLog.next = secondLog + + // when + + let result = firstLog.flatMap() + + // then + + let firstResult = try XCTUnwrap(result.safe(index: 0)) + let secondResult = try XCTUnwrap(result.safe(index: 1)) + let thirdResult = try XCTUnwrap(result.safe(index: 2)) + + XCTAssertEqual(result.count, 3) + XCTAssertEqual(firstResult.description, "/First message/") + XCTAssertEqual(secondResult.description, "/Second message/") + XCTAssertEqual(thirdResult.description, "/Third message/") + } + +} diff --git a/NodeKitTests/UnitTests/Mocks/DTOConvertibleMock.swift b/NodeKitTests/UnitTests/Mocks/DTOConvertibleMock.swift new file mode 100644 index 00000000..4568a804 --- /dev/null +++ b/NodeKitTests/UnitTests/Mocks/DTOConvertibleMock.swift @@ -0,0 +1,55 @@ +// +// DTOConvertibleMock.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit + +final class DTOConvertibleMock: DTOConvertible { + typealias DTO = RawMappableMock + + static var invokedFrom = false + static var invokedFromCount = 0 + static var invokedFromParameter: RawMappableMock? + static var invokedFromParameterList: [RawMappableMock] = [] + static var stubbedFromResult: Result! + + static func from(dto: RawMappableMock) throws -> DTOConvertibleMock { + invokedFrom = true + invokedFromCount += 1 + invokedFromParameter = dto + invokedFromParameterList.append(dto) + switch stubbedFromResult! { + case .success(let value): + return value + case .failure(let error): + throw error + } + } + + var invokedToDTO = false + var invokedToDTOCount = 0 + var stubbedToDTOResult: Result! + + func toDTO() throws -> RawMappableMock { + invokedToDTO = true + invokedToDTOCount += 1 + switch stubbedToDTOResult! { + case .success(let dto): + return dto + case .failure(let error): + throw error + } + } + + static func flush() { + invokedFrom = false + invokedFromCount = 0 + invokedFromParameter = nil + invokedFromParameterList = [] + stubbedFromResult = nil + } +} diff --git a/NodeKitTests/UnitTests/Mocks/RawDecodableMock.swift b/NodeKitTests/UnitTests/Mocks/RawDecodableMock.swift index 812f2306..233c9e82 100644 --- a/NodeKitTests/UnitTests/Mocks/RawDecodableMock.swift +++ b/NodeKitTests/UnitTests/Mocks/RawDecodableMock.swift @@ -9,7 +9,7 @@ @testable import NodeKit final class RawDecodableMock: RawDecodable { - typealias Raw = Int + typealias Raw = Json static var invokedFrom = false static var invokedFromCount = 0 diff --git a/NodeKitTests/UnitTests/Mocks/RawMappableMock.swift b/NodeKitTests/UnitTests/Mocks/RawMappableMock.swift new file mode 100644 index 00000000..a3d85a15 --- /dev/null +++ b/NodeKitTests/UnitTests/Mocks/RawMappableMock.swift @@ -0,0 +1,55 @@ +// +// RawMappableMock.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit + +final class RawMappableMock: RawMappable { + typealias Raw = Json + + static var invokedFrom = false + static var invokedFromCount = 0 + static var invokedFromParameter: Json? + static var invokedFromParameterList: [Json] = [] + static var stubbedFromResult: Result! + + static func from(raw: Json) throws -> RawMappableMock { + invokedFrom = true + invokedFromCount += 1 + invokedFromParameter = raw + invokedFromParameterList.append(raw) + switch stubbedFromResult! { + case .success(let success): + return success + case .failure(let failure): + throw failure + } + } + + var invokedToRaw = false + var invokedToRawCount = 0 + var stubbedToRawResult: Result! + + func toRaw() throws -> Json { + invokedToRaw = true + invokedToRawCount += 1 + switch stubbedToRawResult! { + case .success(let raw): + return raw + case .failure(let error): + throw error + } + } + + static func flush() { + invokedFrom = false + invokedFromCount = 0 + invokedFromParameter = nil + invokedFromParameterList = [] + stubbedFromResult = nil + } +} diff --git a/NodeKitTests/UnitTests/MultipartModelTests.swift b/NodeKitTests/UnitTests/MultipartModelTests.swift new file mode 100644 index 00000000..252960c6 --- /dev/null +++ b/NodeKitTests/UnitTests/MultipartModelTests.swift @@ -0,0 +1,312 @@ +// +// MultipartModelTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class MultipartModelTests: XCTestCase { + + // MARK: - Lifecycle + + override func tearDown() { + super.tearDown() + DTOConvertibleMock.flush() + RawMappableMock.flush() + } + + // MARK: - Tests + + func testFromDTO_thenPayloadFromDTOCalled() throws { + // given + + let dto = MultipartModel(payloadModel: .init()) + + DTOConvertibleMock.stubbedFromResult = .success(.init()) + + // when + + _ = try? MultipartModel.from(dto: dto) + + // then + + let input = try XCTUnwrap(DTOConvertibleMock.invokedFromParameter) + + XCTAssertEqual(DTOConvertibleMock.invokedFromCount, 1) + XCTAssertTrue(input === dto.payloadModel) + } + + func testFromDTO_whenDTOConvertionFailure_thenFailureReceived() throws { + // given + + let dto = MultipartModel(payloadModel: .init()) + + DTOConvertibleMock.stubbedFromResult = .failure(MockError.firstError) + + var result: MultipartModel? + var receivedError: Error? + + // when + + do { + result = try MultipartModel.from(dto: dto) + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .firstError) + } + + func testFromDTO_whenDTOConvertionSuccess_thenSuccessReceived() throws { + // given + + let expectedResult = DTOConvertibleMock() + let dto = MultipartModel(payloadModel: .init()) + + DTOConvertibleMock.stubbedFromResult = .success(expectedResult) + + var result: MultipartModel? + var receivedError: Error? + + // when + + do { + result = try MultipartModel.from(dto: dto) + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result) + + XCTAssertNil(receivedError) + XCTAssertTrue(value.payloadModel === expectedResult) + } + + func testToDTO_thenPayloadToDTOCalled() { + // given + + let dtoConvertibelMock = DTOConvertibleMock() + let sut = MultipartModel(payloadModel: dtoConvertibelMock) + + dtoConvertibelMock.stubbedToDTOResult = .success(.init()) + + // when + + _ = try? sut.toDTO() + + // then + + XCTAssertEqual(dtoConvertibelMock.invokedToDTOCount, 1) + } + + func testToDTO_whenDTOConvertionFailure_thenFailureReceived() throws { + // given + + let dtoConvertibelMock = DTOConvertibleMock() + let sut = MultipartModel(payloadModel: dtoConvertibelMock) + + dtoConvertibelMock.stubbedToDTOResult = .failure(MockError.secondError) + + var result: MultipartModel? + var receivedError: Error? + + // when + + do { + result = try sut.toDTO() + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .secondError) + } + + func testToDTO_whenDTOConvertionSuccess_thenSuccessReceived() throws { + // given + + let dtoConvertibelMock = DTOConvertibleMock() + let rawMappableMock = RawMappableMock() + let sut = MultipartModel(payloadModel: dtoConvertibelMock) + + dtoConvertibelMock.stubbedToDTOResult = .success(rawMappableMock) + + var result: MultipartModel? + var receivedError: Error? + + // when + + do { + result = try sut.toDTO() + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result) + + XCTAssertNil(receivedError) + XCTAssertTrue(value.payloadModel === rawMappableMock) + } + + func testToRaw_thenRawMappableToRawCalled() { + // given + + let rawMapableMock = RawMappableMock() + let sut = MultipartModel(payloadModel: rawMapableMock) + + rawMapableMock.stubbedToRawResult = .success([:]) + + // when + + _ = try? sut.toRaw() + + // then + + XCTAssertEqual(rawMapableMock.invokedToRawCount, 1) + } + + func testToRaw_withToRawConvertationFailure_thenFailureReceived() throws { + // given + + let rawMapableMock = RawMappableMock() + let sut = MultipartModel(payloadModel: rawMapableMock) + + rawMapableMock.stubbedToRawResult = .failure(MockError.thirdError) + + var result: MultipartModel? + var receivedError: Error? + + // when + + do { + result = try sut.toRaw() + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .thirdError) + } + + func testToRaw_withToRawConvertationSuccess_thenSuccessReceived() throws { + // given + + let rawMapableMock = RawMappableMock() + let expectedJson = ["TestKey": "TestValue"] + let sut = MultipartModel(payloadModel: rawMapableMock) + + rawMapableMock.stubbedToRawResult = .success(expectedJson) + + var result: MultipartModel? + var receivedError: Error? + + // when + + do { + result = try sut.toRaw() + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result?.payloadModel as? [String: String]) + + XCTAssertNil(receivedError) + XCTAssertEqual(value, expectedJson) + } + + func testFromRaw_thenRawMappableFromRawCalled() throws { + // given + + let expectedJson = ["TestKey": "TestValue"] + let raw = MultipartModel(payloadModel: expectedJson) + + RawMappableMock.stubbedFromResult = .success(.init()) + + // when + + _ = try? MultipartModel.from(raw: raw) + + // then + + let input = try XCTUnwrap(RawMappableMock.invokedFromParameter as? [String: String]) + + XCTAssertEqual(RawMappableMock.invokedFromCount, 1) + XCTAssertEqual(input, expectedJson) + } + + func testFromRaw_withFromRawConvertationFailure_thenFailureReceived() throws { + // given + + let raw = MultipartModel(payloadModel: [:]) + + RawMappableMock.stubbedFromResult = .failure(MockError.firstError) + + var result: MultipartModel? + var receivedError: Error? + + // when + + do { + result = try MultipartModel.from(raw: raw) + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .firstError) + } + + func testFromRaw_withFromRawConvertationSuccess_thenSuccessReceived() throws { + // given + + let raw = MultipartModel(payloadModel: [:]) + let rawMapableMock = RawMappableMock() + + RawMappableMock.stubbedFromResult = .success(rawMapableMock) + + var result: MultipartModel? + var receivedError: Error? + + // when + + do { + result = try MultipartModel.from(raw: raw) + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result?.payloadModel) + + XCTAssertNil(receivedError) + XCTAssertTrue(value === rawMapableMock) + } +} diff --git a/NodeKitTests/UnitTests/RawDecodableTests.swift b/NodeKitTests/UnitTests/RawDecodableTests.swift new file mode 100644 index 00000000..db3ef032 --- /dev/null +++ b/NodeKitTests/UnitTests/RawDecodableTests.swift @@ -0,0 +1,111 @@ +// +// RawDecodableTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class RawDecodableTests: XCTestCase { + + // MARK: - Lifecycle + + override func tearDown() { + super.tearDown() + RawDecodableMock.flush() + } + + // MARK: - Tests + + func testFromRaw_whenOptionalIsNil_thenNilReceived() { + // given + + var result: RawDecodableMock? + var receivedError: Error? + + // then + + do { + result = try RawDecodableMock?.from(raw: nil) + } catch { + receivedError = error + } + + // then + + XCTAssertNil(result) + XCTAssertNil(receivedError) + } + + func testFromRaw_whenOptionalIsNotNil_thenFromRawCalled() throws { + // given + + let expectedInput = ["TestKey": "TestValue"] + + RawDecodableMock.stubbedFromResult = .success(.init()) + + // then + + _ = try? RawDecodableMock?.from(raw: expectedInput) + + // then + + let input = try XCTUnwrap(RawDecodableMock.invokedFromParameter as? [String: String]) + + XCTAssertEqual(RawDecodableMock.invokedFromCount, 1) + XCTAssertEqual(input, expectedInput) + } + + func testFromRaw_withDecodingError_thenErrorReceived() throws { + // given + + var result: RawDecodableMock? + var receivedError: Error? + + RawDecodableMock.stubbedFromResult = .failure(MockError.firstError) + + // then + + do { + result = try RawDecodableMock?.from(raw: [:]) + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? MockError) + + XCTAssertNil(result) + XCTAssertEqual(error, .firstError) + } + + func testFromRaw_withDecodingSuccess_thenSuccessReceived() throws { + // given + + var result: RawDecodableMock? + var receivedError: Error? + + let expectedResult = RawDecodableMock() + + RawDecodableMock.stubbedFromResult = .success(expectedResult) + + // then + + do { + result = try RawDecodableMock?.from(raw: [:]) + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result) + + XCTAssertNil(receivedError) + XCTAssertTrue(value === expectedResult) + } +} diff --git a/NodeKitTests/UnitTests/ServerRequestsManagerTests.swift b/NodeKitTests/UnitTests/ServerRequestsManagerTests.swift new file mode 100644 index 00000000..9003dcb7 --- /dev/null +++ b/NodeKitTests/UnitTests/ServerRequestsManagerTests.swift @@ -0,0 +1,45 @@ +// +// ServerRequestsManagerTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class ServerRequestsManagerTests: XCTestCase { + + + // MARK: - Sut + + private var sut: ServerRequestsManager! + + // MARK: - Lifecycle + + override func setUp() { + super.setUp() + sut = ServerRequestsManager.shared + } + + override func tearDown() { + super.tearDown() + sut = nil + } + + // MARK: - Tests + + func testManager_thenSessionManagerWithCorrectConfiguration() { + // when + + let configuration = sut.manager.configuration + + // then + + XCTAssertEqual(configuration.timeoutIntervalForResource, 180) + XCTAssertEqual(configuration.timeoutIntervalForRequest, 180) + XCTAssertEqual(configuration.requestCachePolicy, .reloadIgnoringCacheData) + XCTAssertNil(configuration.urlCache) + } +} diff --git a/NodeKitTests/UnitTests/URLRoutingTests.swift b/NodeKitTests/UnitTests/URLRoutingTests.swift new file mode 100644 index 00000000..5d3db1a8 --- /dev/null +++ b/NodeKitTests/UnitTests/URLRoutingTests.swift @@ -0,0 +1,65 @@ +// +// URLRoutingTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class URLRoutingTests: XCTestCase { + + // MARK: - Tests + + func testAppend_whenURLIsNil_thenErrorReceived() throws { + // given + + let url: URL? = nil + let appending = "/users" + + var result: URL? + var receivedError: Error? + + // when + + do { + result = try url + appending + } catch { + receivedError = error + } + + // then + + let error = try XCTUnwrap(receivedError as? UrlRouteError) + + XCTAssertNil(result) + XCTAssertEqual(error, .cantBuildUrl) + } + + func testAppend_whenURLIsNotNil_thenNewURLReceived() throws { + // given + + let url: URL = URL(string: "www.test.com")! + let appending = "/users" + + var result: URL? + var receivedError: Error? + + // when + + do { + result = try url + appending + } catch { + receivedError = error + } + + // then + + let value = try XCTUnwrap(result?.absoluteString) + + XCTAssertNil(receivedError) + XCTAssertEqual(value, "www.test.com/users") + } +} diff --git a/NodeKitTests/UnitTests/UrlChainConfigModelTests.swift b/NodeKitTests/UnitTests/UrlChainConfigModelTests.swift new file mode 100644 index 00000000..61571130 --- /dev/null +++ b/NodeKitTests/UnitTests/UrlChainConfigModelTests.swift @@ -0,0 +1,70 @@ +// +// UrlChainConfigModelTests.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +@testable import NodeKit +import XCTest + +final class UrlChainConfigModelTests: XCTestCase { + + // MARK: - Tests + + func testUrlChainConfigModel_withCustomParameters_thenCustomParametersReceived() throws { + // given + + let model = UrlChainConfigModel( + method: .options, + route: UrlRouteProviderMock(), + metadata: ["TestKey": "TestValue"], + encoding: .urlQuery + ) + + // when + + let method = model.method + let route = model.route + let metadata = model.metadata + let encoding = model.encoding + + // then + + let receivedRoute = try XCTUnwrap(route as? UrlRouteProviderMock) + let expectedRoute = try XCTUnwrap(model.route as? UrlRouteProviderMock) + + XCTAssertEqual(method, model.method) + XCTAssertTrue(receivedRoute === expectedRoute) + XCTAssertEqual(metadata, model.metadata) + XCTAssertEqual(encoding, model.encoding) + } + + func testUrlChainConfigModel_withDefaultParameters_thenDefaultParametersReceived() throws { + // given + + let model = UrlChainConfigModel( + method: .options, + route: UrlRouteProviderMock() + ) + + // when + + let method = model.method + let route = model.route + let metadata = model.metadata + let encoding = model.encoding + + // then + + + let receivedRoute = try XCTUnwrap(route as? UrlRouteProviderMock) + let expectedRoute = try XCTUnwrap(model.route as? UrlRouteProviderMock) + + XCTAssertEqual(method, model.method) + XCTAssertTrue(receivedRoute === expectedRoute) + XCTAssertTrue(metadata.isEmpty) + XCTAssertEqual(encoding, .json) + } +} diff --git a/NodeKitTests/Utils/Array+Extension.swift b/NodeKitTests/Utils/Array+Extension.swift new file mode 100644 index 00000000..f4fcf324 --- /dev/null +++ b/NodeKitTests/Utils/Array+Extension.swift @@ -0,0 +1,16 @@ +// +// Array+Extension.swift +// NodeKitTests +// +// Created by Andrei Frolov on 09.04.24. +// Copyright © 2024 Surf. All rights reserved. +// + +extension Array { + func safe(index: Int) -> Element? { + guard count > index else { + return nil + } + return self[index] + } +}