From d9f6085bf6f93f4b764b77cb541fbf464bdd86ba Mon Sep 17 00:00:00 2001
From: Mathew Polzin <matt.polzin@gmail.com>
Date: Thu, 30 Jan 2020 19:18:14 -0800
Subject: [PATCH] Add path components tests

---
 Sources/OpenAPIKit/Path Item/PathItem.swift   |  44 +-
 .../DeclarativeEaseOfUseTests.swift           |   4 +-
 Tests/OpenAPIKitTests/DocumentTests.swift     |   6 +-
 .../Path Item/PathItemTests.swift             | 443 +++++++++++++++++-
 4 files changed, 448 insertions(+), 49 deletions(-)

diff --git a/Sources/OpenAPIKit/Path Item/PathItem.swift b/Sources/OpenAPIKit/Path Item/PathItem.swift
index be003a5ec..684a5c48a 100644
--- a/Sources/OpenAPIKit/Path Item/PathItem.swift	
+++ b/Sources/OpenAPIKit/Path Item/PathItem.swift	
@@ -111,47 +111,7 @@ extension OpenAPI {
             trace = op
         }
 
-        public typealias Map = OrderedDictionary<PathComponents, Either<JSONReference<Components, PathItem>, PathItem>>
-    }
-}
-
-extension Either where A == JSONReference<OpenAPI.Components, OpenAPI.PathItem>, B == OpenAPI.PathItem {
-    public static func pathItem(_ pathItem: OpenAPI.PathItem) -> Self {
-        return .b(pathItem)
-    }
-
-    public static func pathItem(summary: String? = nil,
-                                description: String? = nil,
-                                servers: [OpenAPI.Server]? = nil,
-                                parameters: OpenAPI.PathItem.Parameter.Array = [],
-                                get: OpenAPI.PathItem.Operation? = nil,
-                                put: OpenAPI.PathItem.Operation? = nil,
-                                post: OpenAPI.PathItem.Operation? = nil,
-                                delete: OpenAPI.PathItem.Operation? = nil,
-                                options: OpenAPI.PathItem.Operation? = nil,
-                                head: OpenAPI.PathItem.Operation? = nil,
-                                patch: OpenAPI.PathItem.Operation? = nil,
-                                trace: OpenAPI.PathItem.Operation? = nil) -> Self {
-        return .b(
-            OpenAPI.PathItem(
-                summary: summary,
-                description: description,
-                servers: servers,
-                parameters: parameters,
-                get: get,
-                put: put,
-                post: post,
-                delete: delete,
-                options: options,
-                head: head,
-                patch: patch,
-                trace: trace
-            )
-        )
-    }
-
-    public static func pathItem(reference: JSONReference<OpenAPI.Components, OpenAPI.PathItem>) -> Self {
-        return .a(reference)
+        public typealias Map = OrderedDictionary<PathComponents, PathItem>
     }
 }
 
@@ -224,7 +184,7 @@ extension OpenAPI.PathComponents: Decodable {
 
         let rawValue = try container.decode(String.self)
 
-        components = rawValue.split(separator: "/").map(String.init)
+        self.init(rawValue: rawValue)
     }
 }
 
diff --git a/Tests/OpenAPIKitTests/DeclarativeEaseOfUseTests.swift b/Tests/OpenAPIKitTests/DeclarativeEaseOfUseTests.swift
index 097c94e6b..5f4359bfd 100644
--- a/Tests/OpenAPIKitTests/DeclarativeEaseOfUseTests.swift
+++ b/Tests/OpenAPIKitTests/DeclarativeEaseOfUseTests.swift
@@ -33,7 +33,7 @@ final class DeclarativeEaseOfUseTests: XCTestCase {
                 )
             ],
             paths: [
-                "/test/api/endpoint/{param}": .pathItem(
+                "/test/api/endpoint/{param}": .init(
                     summary: "Test Endpoint",
                     description: "Test Endpoint description",
                     parameters: [
@@ -267,7 +267,7 @@ final class DeclarativeEaseOfUseTests: XCTestCase {
             info: apiInfo,
             servers: [server],
             paths: [
-                "/test/api/endpoint/{param}": .pathItem(testRoute)
+                "/test/api/endpoint/{param}": testRoute
             ],
             components: components,
             security: [],
diff --git a/Tests/OpenAPIKitTests/DocumentTests.swift b/Tests/OpenAPIKitTests/DocumentTests.swift
index 91c6c3564..3914952f5 100644
--- a/Tests/OpenAPIKitTests/DocumentTests.swift
+++ b/Tests/OpenAPIKitTests/DocumentTests.swift
@@ -25,7 +25,7 @@ final class DocumentTests: XCTestCase {
                 .init(url: URL(string: "https://google.com")!)
             ],
             paths: [
-                "/hi/there": .pathItem(
+                "/hi/there": .init(
                     parameters: [],
                     get: .init(
                         tags: "hi",
@@ -278,7 +278,7 @@ extension DocumentTests {
         let document = OpenAPI.Document(
             info: .init(title: "API", version: "1.0"),
             servers: [],
-            paths: ["test": .pathItem(summary: "hi")],
+            paths: ["test": .init(summary: "hi")],
             components: .noComponents
         )
         let encodedDocument = try testStringFromEncoding(of: document)
@@ -325,7 +325,7 @@ extension DocumentTests {
             OpenAPI.Document(
                 info: .init(title: "API", version: "1.0"),
                 servers: [],
-                paths: ["test": .pathItem(summary: "hi")],
+                paths: ["test": .init(summary: "hi")],
                 components: .noComponents
             )
         )
diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift
index 01cef4479..402cf4e00 100644
--- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift	
+++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift	
@@ -1,5 +1,5 @@
 //
-//  File.swift
+//  PathItemTests.swift
 //  
 //
 //  Created by Mathew Polzin on 12/29/19.
@@ -7,7 +7,446 @@
 
 import XCTest
 import OpenAPIKit
+import FineJSON
 
 final class PathItemTests: XCTestCase {
-    // TODO: write tests
+    func test_initializePathComponents() {
+        let t1 = OpenAPI.PathComponents(["hello", "world"])
+        let t2 = OpenAPI.PathComponents(rawValue: "/hello/world")
+        let t3 = OpenAPI.PathComponents(rawValue: "hello/world")
+        let t4: OpenAPI.PathComponents = "/hello/world"
+        let t5: OpenAPI.PathComponents = "hello/world"
+
+        XCTAssertEqual(t1, t2)
+        XCTAssertEqual(t2, t3)
+        XCTAssertEqual(t3, t4)
+        XCTAssertEqual(t4, t5)
+
+        XCTAssertEqual(t1.rawValue, "/hello/world")
+        XCTAssertEqual(t2.rawValue, "/hello/world")
+        XCTAssertEqual(t3.rawValue, "/hello/world")
+        XCTAssertEqual(t4.rawValue, "/hello/world")
+        XCTAssertEqual(t5.rawValue, "/hello/world")
+    }
+
+    func test_initializePathItem() {
+        // minimal
+        let _ = OpenAPI.PathItem()
+
+        // maximal
+        let op = OpenAPI.PathItem.Operation(responses: [:])
+        let _ = OpenAPI.PathItem(
+            summary: "summary",
+            description: "description",
+            servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)],
+            parameters: [.parameter(name: "hello", parameterLocation: .query, schema: .string)],
+            get: op,
+            put: op,
+            post: op,
+            delete: op,
+            options: op,
+            head: op,
+            patch: op,
+            trace: op
+        )
+    }
+
+    func test_pathItemMutations() {
+        let op = OpenAPI.PathItem.Operation(responses: [:])
+
+        // adding/removing paths
+        var pathItem = OpenAPI.PathItem()
+        XCTAssertNil(pathItem.get)
+        XCTAssertNil(pathItem.put)
+        XCTAssertNil(pathItem.post)
+        XCTAssertNil(pathItem.delete)
+        XCTAssertNil(pathItem.options)
+        XCTAssertNil(pathItem.head)
+        XCTAssertNil(pathItem.patch)
+        XCTAssertNil(pathItem.trace)
+
+        pathItem.get(op)
+        XCTAssertEqual(pathItem.get, op)
+
+        pathItem.get(nil)
+        XCTAssertNil(pathItem.get)
+
+        pathItem.put(op)
+        XCTAssertEqual(pathItem.put, op)
+
+        pathItem.post(op)
+        XCTAssertEqual(pathItem.post, op)
+
+        pathItem.delete(op)
+        XCTAssertEqual(pathItem.delete, op)
+
+        pathItem.options(op)
+        XCTAssertEqual(pathItem.options, op)
+
+        pathItem.head(op)
+        XCTAssertEqual(pathItem.head, op)
+
+        pathItem.patch(op)
+        XCTAssertEqual(pathItem.patch, op)
+
+        pathItem.trace(op)
+        XCTAssertEqual(pathItem.trace, op)
+
+        // for/set/subscript
+        pathItem = .init()
+        XCTAssertNil(pathItem[.get])
+        XCTAssertNil(pathItem[.put])
+        XCTAssertNil(pathItem[.post])
+        XCTAssertNil(pathItem[.delete])
+        XCTAssertNil(pathItem[.options])
+        XCTAssertNil(pathItem[.head])
+        XCTAssertNil(pathItem[.patch])
+        XCTAssertNil(pathItem[.trace])
+
+        pathItem[.get] = op
+        XCTAssertEqual(pathItem.for(.get), op)
+
+        pathItem[.put] = op
+        XCTAssertEqual(pathItem.for(.put), op)
+
+        pathItem[.post] = op
+        XCTAssertEqual(pathItem.for(.post), op)
+
+        pathItem[.delete] = op
+        XCTAssertEqual(pathItem.for(.delete), op)
+
+        pathItem[.options] = op
+        XCTAssertEqual(pathItem.for(.options), op)
+
+        pathItem[.head] = op
+        XCTAssertEqual(pathItem.for(.head), op)
+
+        pathItem[.patch] = op
+        XCTAssertEqual(pathItem.for(.patch), op)
+
+        pathItem[.trace] = op
+        XCTAssertEqual(pathItem.for(.trace), op)
+    }
+
+    func test_initializePathItemMap() {
+        let _: OpenAPI.PathItem.Map = [
+            "hello/world": .init(),
+        ]
+    }
+}
+
+// MARK: Codable Tests
+extension PathItemTests {
+    func test_minimal_encode() throws {
+        let pathItem = OpenAPI.PathItem()
+
+        let encodedPathItem = try testStringFromEncoding(of: pathItem)
+
+        assertJSONEquivalent(
+            encodedPathItem,
+"""
+{
+
+}
+"""
+        )
+    }
+
+    func test_minimal_decode() throws {
+        let pathItemData =
+"""
+{
+
+}
+""".data(using: .utf8)!
+
+        let pathItem = try testDecoder.decode(OpenAPI.PathItem.self, from: pathItemData)
+
+        XCTAssertEqual(pathItem, OpenAPI.PathItem())
+    }
+
+    func test_meta_encode() throws {
+        let pathItem = OpenAPI.PathItem(
+            summary: "summary",
+            description: "description",
+            servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)],
+            parameters: [.parameter(name: "hello", parameterLocation: .query, schema: .string)]
+        )
+
+        let encodedPathItem = try testStringFromEncoding(of: pathItem)
+
+        assertJSONEquivalent(
+            encodedPathItem,
+"""
+{
+  "description" : "description",
+  "parameters" : [
+    {
+      "in" : "query",
+      "name" : "hello",
+      "schema" : {
+        "type" : "string"
+      }
+    }
+  ],
+  "servers" : [
+    {
+      "url" : "http:\\/\\/google.com"
+    }
+  ],
+  "summary" : "summary"
+}
+"""
+        )
+    }
+
+    func test_meta_decode() throws {
+        let pathItemData =
+"""
+{
+  "description" : "description",
+  "parameters" : [
+    {
+      "in" : "query",
+      "name" : "hello",
+      "schema" : {
+        "type" : "string"
+      }
+    }
+  ],
+  "servers" : [
+    {
+      "url" : "http:\\/\\/google.com"
+    }
+  ],
+  "summary" : "summary"
+}
+""".data(using: .utf8)!
+
+        let pathItem = try testDecoder.decode(OpenAPI.PathItem.self, from: pathItemData)
+
+        XCTAssertEqual(
+            pathItem,
+            OpenAPI.PathItem(
+                summary: "summary",
+                description: "description",
+                servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)],
+                parameters: [.parameter(name: "hello", parameterLocation: .query, schema: .string(required: false))]
+            )
+        )
+    }
+
+    func test_operations_encode() throws {
+        let op = OpenAPI.PathItem.Operation(responses: [:])
+
+        let pathItem = OpenAPI.PathItem(
+            get: op,
+            put: op,
+            post: op,
+            delete: op,
+            options: op,
+            head: op,
+            patch: op,
+            trace: op
+        )
+
+        let encodedPathItem = try testStringFromEncoding(of: pathItem)
+
+        assertJSONEquivalent(
+            encodedPathItem,
+"""
+{
+  "delete" : {
+    "responses" : {
+
+    }
+  },
+  "get" : {
+    "responses" : {
+
+    }
+  },
+  "head" : {
+    "responses" : {
+
+    }
+  },
+  "options" : {
+    "responses" : {
+
+    }
+  },
+  "patch" : {
+    "responses" : {
+
+    }
+  },
+  "post" : {
+    "responses" : {
+
+    }
+  },
+  "put" : {
+    "responses" : {
+
+    }
+  },
+  "trace" : {
+    "responses" : {
+
+    }
+  }
+}
+"""
+        )
+    }
+
+    func test_operations_decode() throws {
+        let pathItemData =
+"""
+{
+  "delete" : {
+    "responses" : {
+
+    }
+  },
+  "get" : {
+    "responses" : {
+
+    }
+  },
+  "head" : {
+    "responses" : {
+
+    }
+  },
+  "options" : {
+    "responses" : {
+
+    }
+  },
+  "patch" : {
+    "responses" : {
+
+    }
+  },
+  "post" : {
+    "responses" : {
+
+    }
+  },
+  "put" : {
+    "responses" : {
+
+    }
+  },
+  "trace" : {
+    "responses" : {
+
+    }
+  }
+}
+""".data(using: .utf8)!
+
+        let pathItem = try testDecoder.decode(OpenAPI.PathItem.self, from: pathItemData)
+
+        let op = OpenAPI.PathItem.Operation(responses: [:])
+
+        XCTAssertEqual(
+            pathItem,
+            OpenAPI.PathItem(
+                get: op,
+                put: op,
+                post: op,
+                delete: op,
+                options: op,
+                head: op,
+                patch: op,
+                trace: op
+            )
+        )
+    }
+
+    func test_pathComponents_encode() throws {
+        let test: [OpenAPI.PathComponents] = ["/hello/world", "hi/there"]
+
+        let encodedTest = try testStringFromEncoding(of: test)
+
+        assertJSONEquivalent(
+            encodedTest,
+"""
+[
+  "\\/hello\\/world",
+  "\\/hi\\/there"
+]
+"""
+        )
+    }
+
+    func test_pathComponents_decode() throws {
+        let testData =
+"""
+[
+  "\\/hello\\/world",
+  "\\/hi\\/there"
+]
+""".data(using: .utf8)!
+
+        let test = try testDecoder.decode([OpenAPI.PathComponents].self, from: testData)
+
+        XCTAssertEqual(
+            test,
+            [
+                "/hello/world",
+                "hi/there"
+            ]
+        )
+    }
+
+    func test_pathItemMap_encode() throws {
+        let map: OpenAPI.PathItem.Map = [
+            "/hello/world": .init(),
+            "hi/there": .init()
+        ]
+
+        let encodedMap = try testStringFromEncoding(of: map)
+
+        assertJSONEquivalent(
+            encodedMap,
+"""
+{
+  "\\/hello\\/world" : {
+
+  },
+  "\\/hi\\/there" : {
+
+  }
+}
+"""
+        )
+    }
+
+    func test_pathItemMap_decode() throws {
+        let mapData =
+"""
+{
+  "\\/hello\\/world" : {
+
+  },
+  "\\/hi\\/there" : {
+
+  }
+}
+""".data(using: .utf8)!
+
+        let map = try FineJSONDecoder().decode(OpenAPI.PathItem.Map.self, from: mapData)
+
+        XCTAssertEqual(
+            map,
+            [
+                "/hello/world": .init(),
+                "/hi/there": .init()
+            ]
+        )
+    }
 }