github.com/snyk/vervet/v6@v6.2.4/collator_test.go (about) 1 package vervet_test 2 3 import ( 4 "testing" 5 6 qt "github.com/frankban/quicktest" 7 8 "github.com/snyk/vervet/v6" 9 "github.com/snyk/vervet/v6/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 projectConflictRef := result.Paths["/orgs/{orgId}/projects"].Get.Parameters[6] 100 exampleConflictRef := result.Paths["/examples/hello-world/{id}"].Get.Parameters[3] 101 c.Assert(projectConflictRef.Ref, qt.Not(qt.Equals), exampleConflictRef.Ref) 102 103 projectResp400Ref := result.Paths["/orgs/{orgId}/projects"].Get.Responses["400"] 104 c.Assert(projectResp400Ref.Ref, qt.Equals, "#/components/responses/400") 105 exampleResp400Ref := result.Paths["/examples/hello-world/{id}"].Get.Responses["400"] 106 c.Assert(exampleResp400Ref.Ref, qt.Equals, "") 107 c.Assert("{\"content\":{\"application/vnd.api+json\":{\"schema\":{\"additionalProperties\":false,\"example\":{"+ 108 "\"errors\":[{\"detail\":\"Permission denied for this resource\",\"status\":\"403\"}],\"jsonapi\":{\"version\":"+ 109 "\"1.0\"}},\"properties\":{\"errors\":{\"example\":[{\"detail\":\"Permission denied for this resource\",\"status"+ 110 "\":\"403\"}],\"items\":{\"additionalProperties\":false,\"example\":{\"detail\":\"Not Found\",\"status\":\"404\"}"+ 111 ",\"properties\":{\"detail\":{\"description\":\"A human-readable explanation specific to this occurrence of the "+ 112 "problem.\",\"example\":\"The request was missing these required fields: ...\",\"type\":\"string\"},\"id\":"+ 113 "{\"description\":\"A unique identifier for this particular occurrence of the problem.\",\"example\":"+ 114 "\"f16c31b5-6129-4571-add8-d589da9be524\",\"format\":\"uuid\",\"type\":\"string\"},\"meta\":"+ 115 "{\"additionalProperties\":true,\"example\":{\"key\":\"value\"},\"type\":\"object\"},\"source\":"+ 116 "{\"additionalProperties\":false,\"example\":{\"pointer\":\"/data/attributes\"},\"properties\":"+ 117 "{\"parameter\":{\"description\":\"A string indicating which URI query parameter caused the error."+ 118 "\",\"example\":\"param1\",\"type\":\"string\"},\"pointer\":{\"description\":\"A JSON Pointer [RFC6901] to the "+ 119 "associated entity in the request document.\",\"example\":\"/data/attributes\",\"type\":\"string\"}},\"type\":"+ 120 "\"object\"},\"status\":{\"description\":\"The HTTP status code applicable to this problem, expressed as a "+ 121 "string value.\",\"example\":\"400\",\"pattern\":\"^[45]\\\\d\\\\d$\",\"type\":\"string\"}},\"required\":"+ 122 "[\"status\",\"detail\"],\"type\":\"object\"},\"minItems\":1,\"type\":\"array\"},\"jsonapi\":"+ 123 "{\"additionalProperties\":false,\"example\":{\"version\":\"1.0\"},\"properties\":{\"version\":"+ 124 "{\"description\":\"Version of the JSON API specification this server supports.\",\"example\":\"1.0\","+ 125 "\"pattern\":\"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)$\",\"type\":\"string\"}},\"required\":[\"version\"],\"type\""+ 126 ":\"object\"}},\"required\":[\"jsonapi\",\"errors\"],\"type\":\"object\"}}},\"description\":\"Bad Request: A "+ 127 "parameter provided as a part of the request was invalid.\",\"headers\":{\"deprecation\":{\"description\":\""+ 128 "A header containing the deprecation date of the underlying endpoint. For more information, please refer to "+ 129 "the deprecation header RFC:\\nhttps://tools.ietf.org/id/draft-dalal-deprecation-header-01.html\\n\",\"example"+ 130 "\":\"2021-07-01T00:00:00Z\",\"schema\":{\"format\":\"date-time\",\"type\":\"string\"}},\"snyk-request-id\":"+ 131 "{\"description\":\"A header containing a unique id used for tracking this request. If you are reporting an "+ 132 "issue to Snyk it's very helpful to provide this ID.\\n\",\"example\":\"4b58e274-ec62-4fab-917b-1d2c48d6bdef\""+ 133 ",\"schema\":{\"format\":\"uuid\",\"type\":\"string\"}},\"snyk-version-lifecycle-stage\":{\"description\":"+ 134 "\"A header containing the version stage of the endpoint. This stage describes the guarantees snyk provides "+ 135 "surrounding stability of the endpoint.\\n\",\"schema\":{\"enum\":[\"wip\",\"experimental\",\"beta\",\"ga\","+ 136 "\"deprecated\",\"sunset\"],\"example\":\"ga\",\"type\":\"string\"}},\"snyk-version-requested\":{\"description\""+ 137 ":\"A header containing the version of the endpoint requested by the caller.\",\"example\":\"2021-06-04\",\""+ 138 "schema\":{\"description\":\"Requested API version\",\"pattern\":\"^(wip|work-in-progress|experimental|beta|"+ 139 "((([0-9]{4})-([0-1][0-9]))-((3[01])|(0[1-9])|([12][0-9]))(~(wip|work-in-progress|experimental|beta))?))$\""+ 140 ",\"type\":\"string\"}},\"snyk-version-served\":{\"description\":\"A header containing the version of the "+ 141 "endpoint that was served by the API.\",\"example\":\"2021-06-04\",\"schema\":{\"description\":\"Resolved API "+ 142 "version\",\"pattern\":\"^((([0-9]{4})-([0-1][0-9]))-((3[01])|(0[1-9])|([12][0-9]))(~"+ 143 "(wip|work-in-progress|experimental|beta))?)$\",\"type\":\"string\"}},\"sunset\":{\"description\":"+ 144 "\"A header containing the date of when the underlying endpoint will be removed. This header is only present if "+ 145 "the endpoint has been deprecated. Please refer to the RFC for more information:"+ 146 "\\nhttps://datatracker.ietf.org/doc/html/rfc8594\\n\",\"example\":\"2021-08-02T00:00:00Z\",\"schema\":"+ 147 "{\"format\":\"date-time\",\"type\":\"string\"}}}}\n", qt.JSONEquals, exampleResp400Ref.Value) 148 } 149 150 func TestCollateUseFirstRoute(t *testing.T) { 151 c := qt.New(t) 152 collator := vervet.NewCollator(vervet.UseFirstRoute(true)) 153 examples1, err := vervet.LoadResourceVersions(testdata.Path("conflict/_examples")) 154 c.Assert(err, qt.IsNil) 155 examples1v, err := examples1.At("2021-06-15~experimental") 156 c.Assert(err, qt.IsNil) 157 158 examples2, err := vervet.LoadResourceVersions(testdata.Path("conflict/_examples2")) 159 c.Assert(err, qt.IsNil) 160 examples2v, err := examples2.At("2021-06-15~experimental") 161 c.Assert(err, qt.IsNil) 162 163 err = collator.Collate(examples1v) 164 c.Assert(err, qt.IsNil) 165 err = collator.Collate(examples2v) 166 c.Assert(err, qt.IsNil) 167 168 result := collator.Result() 169 170 // First path chosen, route matching rules ignore path variable 171 c.Assert(result.Paths["/examples/hello-world/{id1}"], qt.Not(qt.IsNil)) 172 c.Assert(result.Paths["/examples/hello-world/{id2}"], qt.IsNil) 173 174 // First chosen path has description expected 175 c.Assert(result.Paths["/examples/hello-world/{id1}"].Get.Description, qt.Contains, " - from example 1") 176 } 177 178 func TestCollatePathConflict(t *testing.T) { 179 c := qt.New(t) 180 collator := vervet.NewCollator(vervet.UseFirstRoute(false)) 181 examples1, err := vervet.LoadResourceVersions(testdata.Path("conflict/_examples")) 182 c.Assert(err, qt.IsNil) 183 examples1v, err := examples1.At("2021-06-15~experimental") 184 c.Assert(err, qt.IsNil) 185 186 examples2, err := vervet.LoadResourceVersions(testdata.Path("conflict/_examples2")) 187 c.Assert(err, qt.IsNil) 188 examples2v, err := examples2.At("2021-06-15~experimental") 189 c.Assert(err, qt.IsNil) 190 191 err = collator.Collate(examples1v) 192 c.Assert(err, qt.IsNil) 193 err = collator.Collate(examples2v) 194 c.Assert(err, qt.ErrorMatches, `.*conflict in #/paths /examples/hello-world/{id2}: declared in both.*`) 195 c.Assert(err, qt.ErrorMatches, `.*conflict in #/paths /examples/hello-world: declared in both.*`) 196 } 197 198 func TestCollateMergingResources(t *testing.T) { 199 c := qt.New(t) 200 collator := vervet.NewCollator(vervet.UseFirstRoute(true)) 201 202 newService, err := vervet.LoadResourceVersions(testdata.Path("competing-specs/special_projects")) 203 c.Assert(err, qt.IsNil) 204 specV1, err := newService.At("2023-03-13~experimental") 205 c.Assert(err, qt.IsNil) 206 207 originalService, err := vervet.LoadResourceVersions(testdata.Path("competing-specs/projects")) 208 c.Assert(err, qt.IsNil) 209 specV2, err := originalService.At("2021-08-20~experimental") 210 c.Assert(err, qt.IsNil) 211 212 err = collator.Collate(specV2) 213 c.Assert(err, qt.IsNil) 214 err = collator.Collate(specV1) 215 c.Assert(err, qt.IsNil) 216 217 result := collator.Result() 218 c.Assert(result.Paths["/orgs/{org_id}/projects/{project_id}"].Delete.Responses["204"], qt.IsNotNil) 219 }