github.com/w3security/vervet/v5@v5.3.1-0.20230618081846-5bd9b5d799dc/collator_test.go (about) 1 package vervet_test 2 3 import ( 4 "testing" 5 6 qt "github.com/frankban/quicktest" 7 8 "github.com/w3security/vervet/v5" 9 "github.com/w3security/vervet/v5/testdata" 10 ) 11 12 func TestRefRemover(t *testing.T) { 13 c := qt.New(t) 14 doc, err := vervet.NewDocumentFile(testdata.Path("resources/projects/2021-08-20/spec.yaml")) 15 c.Assert(err, qt.IsNil) 16 resp400 := doc.Paths["/orgs/{org_id}/projects/{project_id}"].Delete.Responses["400"] 17 errDoc := resp400.Value.Content["application/vnd.api+json"].Schema 18 c.Assert(err, qt.IsNil) 19 c.Assert("{\"$ref\":\"../errors.yaml#/ErrorDocument\"}", qt.JSONEquals, errDoc) 20 in := vervet.NewRefRemover(errDoc) 21 err = in.RemoveRef() 22 c.Assert(err, qt.IsNil) 23 c.Assert(err, qt.IsNil) 24 //nolint:lll // acked 25 c.Assert("{\"additionalProperties\":false,\"example\":{\"errors\":[{\"detail\":\"Permission denied for this "+ 26 "resource\",\"status\":\"403\"}],\"jsonapi\":{\"version\":\"1.0\"}},\"properties\":{\"errors\":{\"example\":"+ 27 "[{\"detail\":\"Permission denied for this resource\",\"status\":\"403\"}],\"items\":{\"additionalProperties\""+ 28 ":false,\"example\":{\"detail\":\"Not Found\",\"status\":\"404\"},\"properties\":{\"detail\":{\"description\":"+ 29 "\"A human-readable explanation specific to this occurrence of the problem.\",\"example\":\"The request was "+ 30 "missing these required fields: ...\",\"type\":\"string\"},\"id\":{\"description\":\"A unique identifier for "+ 31 "this particular occurrence of the problem.\",\"example\":\"f16c31b5-6129-4571-add8-d589da9be524\",\"format\""+ 32 ":\"uuid\",\"type\":\"string\"},\"meta\":{\"additionalProperties\":true,\"example\":{\"key\":\"value\"},\"type\""+ 33 ":\"object\"},\"source\":{\"additionalProperties\":false,\"example\":{\"pointer\":\"/data/attributes\"},\"properties\""+ 34 ":{\"parameter\":{\"description\":\"A string indicating which URI query parameter caused the error.\",\"example\""+ 35 ":\"param1\",\"type\":\"string\"},\"pointer\":{\"description\":\"A JSON Pointer [RFC6901] to the associated entity "+ 36 "in the request document.\",\"example\":\"/data/attributes\",\"type\":\"string\"}},\"type\":\"object\"},\"status\""+ 37 ":{\"description\":\"The HTTP status code applicable to this problem, expressed as a string value.\",\"example\""+ 38 ":\"400\",\"pattern\":\"^[45]\\\\d\\\\d$\",\"type\":\"string\"}},\"required\":[\"status\",\"detail\"],\"type\""+ 39 ":\"object\"},\"minItems\":1,\"type\":\"array\"},\"jsonapi\":{\"additionalProperties\":false,\"example\":"+ 40 "{\"version\":\"1.0\"},\"properties\":{\"version\":{\"description\":\"Version of the JSON API specification "+ 41 "this server supports.\",\"example\":\"1.0\",\"pattern\":\"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)$\",\"type\""+ 42 ":\"string\"}},\"required\":[\"version\"],\"type\":\"object\"}},\"required\":[\"jsonapi\",\"errors\"],\"type\""+ 43 ":\"object\"}\n", qt.JSONEquals, errDoc) 44 } 45 46 func TestCollator(t *testing.T) { 47 c := qt.New(t) 48 collator := vervet.NewCollator() 49 projects, err := vervet.LoadResourceVersions(testdata.Path("conflict-components/projects")) 50 c.Assert(err, qt.IsNil) 51 projectV, err := projects.At("2021-06-04~experimental") 52 c.Assert(err, qt.IsNil) 53 examples, err := vervet.LoadResourceVersions(testdata.Path("conflict-components/_examples")) 54 c.Assert(err, qt.IsNil) 55 examplesV, err := examples.At("2021-06-01~experimental") 56 c.Assert(err, qt.IsNil) 57 58 err = collator.Collate(projectV) 59 c.Assert(err, qt.IsNil) 60 err = collator.Collate(examplesV) 61 c.Assert(err, qt.IsNil) 62 63 result := collator.Result() 64 c.Assert( 65 result.Paths["/orgs/{orgId}/projects"]. 66 Get.Responses["200"]. 67 Value. 68 Content["application/vnd.api+json"]. 69 Schema.Value.Properties["jsonapi"].Ref, 70 qt.Equals, 71 "#/components/schemas/JsonApi", 72 ) 73 schemaRef := result. 74 Paths["/examples/hello-world/{id}"]. 75 Get. 76 Responses["200"]. 77 Value. 78 Content["application/vnd.api+json"]. 79 Schema. 80 Value. 81 Properties["jsonapi"] 82 c.Assert(schemaRef.Ref, qt.Equals, "") 83 c.Assert("{\"additionalProperties\":false,\"example\":{\"version\":\"1.0\"},\"properties\":{\"version\":"+ 84 "{\"description\":\"Version of the JSON API specification this server supports.\",\"example\":\"1.0\","+ 85 "\"pattern\":\"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)$\",\"type\":\"string\"}},\"required\":[\"version\"],\"type\""+ 86 ":\"object\"}\n", qt.JSONEquals, schemaRef.Value) 87 c.Assert(result.Components.Schemas["JsonApi"], qt.IsNotNil) 88 89 projectParameterRef := result.Paths["/orgs/{orgId}/projects"].Get.Parameters[0] 90 c.Assert(projectParameterRef.Ref, qt.Equals, "#/components/parameters/Version") 91 exampleParameterRef := result.Paths["/examples/hello-world/{id}"].Get.Parameters[0] 92 c.Assert(exampleParameterRef.Ref, qt.Equals, "") 93 //nolint:lll // acked 94 c.Assert("{\"description\":\"The requested version of the endpoint to process the request\",\"example\""+ 95 ":\"2021-06-04\",\"in\":\"query\",\"name\":\"version\",\"required\":true,\"schema\":{\"description\":"+ 96 "\"Requested API version\",\"pattern\":\"^(wip|work-in-progress|experimental|beta|((([0-9]{4})-([0-1][0-9]))"+ 97 "-((3[01])|(0[1-9])|([12][0-9]))(~(wip|work-in-progress|experimental|beta))?))$\",\"type\":\"string\"}}\n", qt.JSONEquals, exampleParameterRef.Value) 98 99 projectResp400Ref := result.Paths["/orgs/{orgId}/projects"].Get.Responses["400"] 100 c.Assert(projectResp400Ref.Ref, qt.Equals, "#/components/responses/400") 101 exampleResp400Ref := result.Paths["/examples/hello-world/{id}"].Get.Responses["400"] 102 c.Assert(exampleResp400Ref.Ref, qt.Equals, "") 103 c.Assert("{\"content\":{\"application/vnd.api+json\":{\"schema\":{\"additionalProperties\":false,\"example\":{"+ 104 "\"errors\":[{\"detail\":\"Permission denied for this resource\",\"status\":\"403\"}],\"jsonapi\":{\"version\":"+ 105 "\"1.0\"}},\"properties\":{\"errors\":{\"example\":[{\"detail\":\"Permission denied for this resource\",\"status"+ 106 "\":\"403\"}],\"items\":{\"additionalProperties\":false,\"example\":{\"detail\":\"Not Found\",\"status\":\"404\"}"+ 107 ",\"properties\":{\"detail\":{\"description\":\"A human-readable explanation specific to this occurrence of the "+ 108 "problem.\",\"example\":\"The request was missing these required fields: ...\",\"type\":\"string\"},\"id\":"+ 109 "{\"description\":\"A unique identifier for this particular occurrence of the problem.\",\"example\":"+ 110 "\"f16c31b5-6129-4571-add8-d589da9be524\",\"format\":\"uuid\",\"type\":\"string\"},\"meta\":"+ 111 "{\"additionalProperties\":true,\"example\":{\"key\":\"value\"},\"type\":\"object\"},\"source\":"+ 112 "{\"additionalProperties\":false,\"example\":{\"pointer\":\"/data/attributes\"},\"properties\":"+ 113 "{\"parameter\":{\"description\":\"A string indicating which URI query parameter caused the error."+ 114 "\",\"example\":\"param1\",\"type\":\"string\"},\"pointer\":{\"description\":\"A JSON Pointer [RFC6901] to the "+ 115 "associated entity in the request document.\",\"example\":\"/data/attributes\",\"type\":\"string\"}},\"type\":"+ 116 "\"object\"},\"status\":{\"description\":\"The HTTP status code applicable to this problem, expressed as a "+ 117 "string value.\",\"example\":\"400\",\"pattern\":\"^[45]\\\\d\\\\d$\",\"type\":\"string\"}},\"required\":"+ 118 "[\"status\",\"detail\"],\"type\":\"object\"},\"minItems\":1,\"type\":\"array\"},\"jsonapi\":"+ 119 "{\"additionalProperties\":false,\"example\":{\"version\":\"1.0\"},\"properties\":{\"version\":"+ 120 "{\"description\":\"Version of the JSON API specification this server supports.\",\"example\":\"1.0\","+ 121 "\"pattern\":\"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)$\",\"type\":\"string\"}},\"required\":[\"version\"],\"type\""+ 122 ":\"object\"}},\"required\":[\"jsonapi\",\"errors\"],\"type\":\"object\"}}},\"description\":\"Bad Request: A "+ 123 "parameter provided as a part of the request was invalid.\",\"headers\":{\"deprecation\":{\"description\":\""+ 124 "A header containing the deprecation date of the underlying endpoint. For more information, please refer to "+ 125 "the deprecation header RFC:\\nhttps://tools.ietf.org/id/draft-dalal-deprecation-header-01.html\\n\",\"example"+ 126 "\":\"2021-07-01T00:00:00Z\",\"schema\":{\"format\":\"date-time\",\"type\":\"string\"}},\"w3security-request-id\":"+ 127 "{\"description\":\"A header containing a unique id used for tracking this request. If you are reporting an "+ 128 "issue to W3Security it's very helpful to provide this ID.\\n\",\"example\":\"4b58e274-ec62-4fab-917b-1d2c48d6bdef\""+ 129 ",\"schema\":{\"format\":\"uuid\",\"type\":\"string\"}},\"w3security-version-lifecycle-stage\":{\"description\":"+ 130 "\"A header containing the version stage of the endpoint. This stage describes the guarantees w3security provides "+ 131 "surrounding stability of the endpoint.\\n\",\"schema\":{\"enum\":[\"wip\",\"experimental\",\"beta\",\"ga\","+ 132 "\"deprecated\",\"sunset\"],\"example\":\"ga\",\"type\":\"string\"}},\"w3security-version-requested\":{\"description\""+ 133 ":\"A header containing the version of the endpoint requested by the caller.\",\"example\":\"2021-06-04\",\""+ 134 "schema\":{\"description\":\"Requested API version\",\"pattern\":\"^(wip|work-in-progress|experimental|beta|"+ 135 "((([0-9]{4})-([0-1][0-9]))-((3[01])|(0[1-9])|([12][0-9]))(~(wip|work-in-progress|experimental|beta))?))$\""+ 136 ",\"type\":\"string\"}},\"w3security-version-served\":{\"description\":\"A header containing the version of the "+ 137 "endpoint that was served by the API.\",\"example\":\"2021-06-04\",\"schema\":{\"description\":\"Resolved API "+ 138 "version\",\"pattern\":\"^((([0-9]{4})-([0-1][0-9]))-((3[01])|(0[1-9])|([12][0-9]))(~"+ 139 "(wip|work-in-progress|experimental|beta))?)$\",\"type\":\"string\"}},\"sunset\":{\"description\":"+ 140 "\"A header containing the date of when the underlying endpoint will be removed. This header is only present if "+ 141 "the endpoint has been deprecated. Please refer to the RFC for more information:"+ 142 "\\nhttps://datatracker.ietf.org/doc/html/rfc8594\\n\",\"example\":\"2021-08-02T00:00:00Z\",\"schema\":"+ 143 "{\"format\":\"date-time\",\"type\":\"string\"}}}}\n", qt.JSONEquals, exampleResp400Ref.Value) 144 } 145 146 func TestCollateUseFirstRoute(t *testing.T) { 147 c := qt.New(t) 148 collator := vervet.NewCollator(vervet.UseFirstRoute(true)) 149 examples1, err := vervet.LoadResourceVersions(testdata.Path("conflict/_examples")) 150 c.Assert(err, qt.IsNil) 151 examples1v, err := examples1.At("2021-06-15~experimental") 152 c.Assert(err, qt.IsNil) 153 154 examples2, err := vervet.LoadResourceVersions(testdata.Path("conflict/_examples2")) 155 c.Assert(err, qt.IsNil) 156 examples2v, err := examples2.At("2021-06-15~experimental") 157 c.Assert(err, qt.IsNil) 158 159 err = collator.Collate(examples1v) 160 c.Assert(err, qt.IsNil) 161 err = collator.Collate(examples2v) 162 c.Assert(err, qt.IsNil) 163 164 result := collator.Result() 165 166 // First path chosen, route matching rules ignore path variable 167 c.Assert(result.Paths["/examples/hello-world/{id1}"], qt.Not(qt.IsNil)) 168 c.Assert(result.Paths["/examples/hello-world/{id2}"], qt.IsNil) 169 170 // First chosen path has description expected 171 c.Assert(result.Paths["/examples/hello-world/{id1}"].Get.Description, qt.Contains, " - from example 1") 172 } 173 174 func TestCollatePathConflict(t *testing.T) { 175 c := qt.New(t) 176 collator := vervet.NewCollator(vervet.UseFirstRoute(false)) 177 examples1, err := vervet.LoadResourceVersions(testdata.Path("conflict/_examples")) 178 c.Assert(err, qt.IsNil) 179 examples1v, err := examples1.At("2021-06-15~experimental") 180 c.Assert(err, qt.IsNil) 181 182 examples2, err := vervet.LoadResourceVersions(testdata.Path("conflict/_examples2")) 183 c.Assert(err, qt.IsNil) 184 examples2v, err := examples2.At("2021-06-15~experimental") 185 c.Assert(err, qt.IsNil) 186 187 err = collator.Collate(examples1v) 188 c.Assert(err, qt.IsNil) 189 err = collator.Collate(examples2v) 190 c.Assert(err, qt.ErrorMatches, `.*conflict in #/paths /examples/hello-world/{id2}: declared in both.*`) 191 c.Assert(err, qt.ErrorMatches, `.*conflict in #/paths /examples/hello-world: declared in both.*`) 192 } 193 194 func TestCollateMergingResources(t *testing.T) { 195 c := qt.New(t) 196 collator := vervet.NewCollator(vervet.UseFirstRoute(true)) 197 198 newService, err := vervet.LoadResourceVersions(testdata.Path("competing-specs/special_projects")) 199 c.Assert(err, qt.IsNil) 200 specV1, err := newService.At("2023-03-13~experimental") 201 c.Assert(err, qt.IsNil) 202 203 originalService, err := vervet.LoadResourceVersions(testdata.Path("competing-specs/projects")) 204 c.Assert(err, qt.IsNil) 205 specV2, err := originalService.At("2021-08-20~experimental") 206 c.Assert(err, qt.IsNil) 207 208 err = collator.Collate(specV2) 209 c.Assert(err, qt.IsNil) 210 err = collator.Collate(specV1) 211 c.Assert(err, qt.IsNil) 212 213 result := collator.Result() 214 c.Assert(result.Paths["/orgs/{org_id}/projects/{project_id}"].Delete.Responses["204"], qt.IsNotNil) 215 }