microsoft/typespec
Publicmirrored from https://github.com/microsoft/typespecAvailable
packages/openapi3/test/parameters.test.ts
237lines · modecode
| 1 | import { expectDiagnostics } from "@cadl-lang/compiler/testing"; |
| 2 | import { deepStrictEqual, ok, strictEqual } from "assert"; |
| 3 | import { diagnoseOpenApiFor, openApiFor } from "./test-host.js"; |
| 4 | |
| 5 | describe("openapi3: parameters", () => { |
| 6 | it("create a query param", async () => { |
| 7 | const res = await openApiFor( |
| 8 | ` |
| 9 | op test(@query arg1: string): void; |
| 10 | ` |
| 11 | ); |
| 12 | strictEqual(res.paths["/"].get.parameters[0].in, "query"); |
| 13 | strictEqual(res.paths["/"].get.parameters[0].name, "arg1"); |
| 14 | deepStrictEqual(res.paths["/"].get.parameters[0].schema, { type: "string" }); |
| 15 | }); |
| 16 | |
| 17 | it("create a query param with a different name", async () => { |
| 18 | const res = await openApiFor( |
| 19 | ` |
| 20 | op test(@query("$select") select: string): void; |
| 21 | ` |
| 22 | ); |
| 23 | strictEqual(res.paths["/"].get.parameters[0].in, "query"); |
| 24 | strictEqual(res.paths["/"].get.parameters[0].name, "$select"); |
| 25 | }); |
| 26 | |
| 27 | it("create a query param of array type", async () => { |
| 28 | const res = await openApiFor( |
| 29 | ` |
| 30 | op test( |
| 31 | @query({name: "$select"}) selects: string[], |
| 32 | @query({name: "$order", format: "csv"}) orders: string[], |
| 33 | ): void; |
| 34 | ` |
| 35 | ); |
| 36 | strictEqual(res.paths["/"].get.parameters[0].in, "query"); |
| 37 | strictEqual(res.paths["/"].get.parameters[0].name, "$select"); |
| 38 | strictEqual(res.paths["/"].get.parameters[0].style, "form"); |
| 39 | strictEqual(res.paths["/"].get.parameters[0].explode, true); |
| 40 | strictEqual(res.paths["/"].get.parameters[1].in, "query"); |
| 41 | strictEqual(res.paths["/"].get.parameters[1].name, "$order"); |
| 42 | strictEqual(res.paths["/"].get.parameters[1].style, "simple"); |
| 43 | }); |
| 44 | |
| 45 | it("create a query param that is a model property", async () => { |
| 46 | const res = await openApiFor( |
| 47 | ` |
| 48 | op test(@query id: UserContext.id): void; |
| 49 | |
| 50 | model UserContext { |
| 51 | id: string; |
| 52 | } |
| 53 | ` |
| 54 | ); |
| 55 | deepStrictEqual(res.paths["/"].get.parameters[0], { |
| 56 | in: "query", |
| 57 | name: "id", |
| 58 | required: true, |
| 59 | schema: { |
| 60 | type: "string", |
| 61 | }, |
| 62 | }); |
| 63 | }); |
| 64 | |
| 65 | it("create an header param", async () => { |
| 66 | const res = await openApiFor( |
| 67 | ` |
| 68 | op test(@header arg1: string): void; |
| 69 | ` |
| 70 | ); |
| 71 | strictEqual(res.paths["/"].get.parameters[0].in, "header"); |
| 72 | strictEqual(res.paths["/"].get.parameters[0].name, "arg1"); |
| 73 | deepStrictEqual(res.paths["/"].get.parameters[0].schema, { type: "string" }); |
| 74 | }); |
| 75 | |
| 76 | it("create an header param with a different name", async () => { |
| 77 | const res = await openApiFor( |
| 78 | ` |
| 79 | op test(@header("foo-bar") foo: string): void; |
| 80 | ` |
| 81 | ); |
| 82 | strictEqual(res.paths["/"].get.parameters[0].in, "header"); |
| 83 | strictEqual(res.paths["/"].get.parameters[0].name, "foo-bar"); |
| 84 | }); |
| 85 | |
| 86 | it("create an header param of array type", async () => { |
| 87 | const res = await openApiFor( |
| 88 | ` |
| 89 | op test(@header("foo-bar") foo: string[]): void; |
| 90 | ` |
| 91 | ); |
| 92 | strictEqual(res.paths["/"].get.parameters[0].in, "header"); |
| 93 | strictEqual(res.paths["/"].get.parameters[0].name, "foo-bar"); |
| 94 | strictEqual(res.paths["/"].get.parameters[0].style, "simple"); |
| 95 | }); |
| 96 | |
| 97 | // Regression test for https://github.com/microsoft/cadl/issues/414 |
| 98 | it("@doc set the description on the parameter not its schema", async () => { |
| 99 | const res = await openApiFor( |
| 100 | ` |
| 101 | op test(@query @doc("my-doc") arg1: string): void; |
| 102 | ` |
| 103 | ); |
| 104 | strictEqual(res.paths["/"].get.parameters[0].description, "my-doc"); |
| 105 | strictEqual(res.paths["/"].get.parameters[0].schema.description, undefined); |
| 106 | }); |
| 107 | |
| 108 | it("errors on duplicate parameter keys", async () => { |
| 109 | const diagnostics = await diagnoseOpenApiFor( |
| 110 | ` |
| 111 | model P { |
| 112 | @query id: string; |
| 113 | } |
| 114 | |
| 115 | @friendlyName("P") |
| 116 | model Q { |
| 117 | @header id: string; |
| 118 | } |
| 119 | |
| 120 | @route("/test1") |
| 121 | op test1(...P): void; |
| 122 | |
| 123 | @route("/test2") |
| 124 | op test2(...Q): void; |
| 125 | `, |
| 126 | { "omit-unreachable-types": true } |
| 127 | ); |
| 128 | |
| 129 | expectDiagnostics(diagnostics, [ |
| 130 | { |
| 131 | code: "@cadl-lang/openapi/duplicate-type-name", |
| 132 | message: /parameter/, |
| 133 | }, |
| 134 | ]); |
| 135 | }); |
| 136 | |
| 137 | it("encodes parameter keys in references", async () => { |
| 138 | const oapi = await openApiFor(` |
| 139 | model Pet extends Pet$Id { |
| 140 | name: string; |
| 141 | } |
| 142 | model Pet$Id { |
| 143 | @path |
| 144 | petId: string; |
| 145 | } |
| 146 | |
| 147 | @route("/Pets") |
| 148 | @get() |
| 149 | op get(... Pet$Id): Pet; |
| 150 | `); |
| 151 | |
| 152 | ok(oapi.paths["/Pets/{petId}"].get); |
| 153 | strictEqual( |
| 154 | oapi.paths["/Pets/{petId}"].get.parameters[0]["$ref"], |
| 155 | "#/components/parameters/Pet%24Id" |
| 156 | ); |
| 157 | strictEqual(oapi.components.parameters["Pet$Id"].name, "petId"); |
| 158 | }); |
| 159 | |
| 160 | it("inline spread of parameters from anonymous model", async () => { |
| 161 | const oapi = await openApiFor( |
| 162 | ` |
| 163 | op template<TParameters, TReturn>(...TParameters): TReturn; |
| 164 | op instantiation is template<{@path id: string}, void>; |
| 165 | ` |
| 166 | ); |
| 167 | |
| 168 | ok(oapi.paths["/{id}"].get); |
| 169 | |
| 170 | deepStrictEqual(oapi.paths["/{id}"].get.parameters, [ |
| 171 | { |
| 172 | name: "id", |
| 173 | in: "path", |
| 174 | required: true, |
| 175 | schema: { |
| 176 | type: "string", |
| 177 | }, |
| 178 | }, |
| 179 | ]); |
| 180 | }); |
| 181 | |
| 182 | it("omit parameters with type never", async () => { |
| 183 | const res = await openApiFor( |
| 184 | ` |
| 185 | op test(@query select: never, @query top: int32): void; |
| 186 | ` |
| 187 | ); |
| 188 | strictEqual(res.paths["/"].get.parameters.length, 1); |
| 189 | strictEqual(res.paths["/"].get.parameters[0].in, "query"); |
| 190 | strictEqual(res.paths["/"].get.parameters[0].name, "top"); |
| 191 | }); |
| 192 | |
| 193 | describe("content type parameter", () => { |
| 194 | it("header named with 'Content-Type' gets resolved as content type for operation.", async () => { |
| 195 | const res = await openApiFor( |
| 196 | ` |
| 197 | op test( |
| 198 | @header("Content-Type") explicitContentType: "application/octet-stream", |
| 199 | @body foo: string |
| 200 | ): void; |
| 201 | ` |
| 202 | ); |
| 203 | ok(res.paths["/"].post.requestBody.content["application/octet-stream"]); |
| 204 | deepStrictEqual(res.paths["/"].post.requestBody.content["application/octet-stream"].schema, { |
| 205 | type: "string", |
| 206 | }); |
| 207 | }); |
| 208 | |
| 209 | it("header named contentType gets resolved as content type for operation.", async () => { |
| 210 | const res = await openApiFor( |
| 211 | ` |
| 212 | op test( |
| 213 | @header contentType: "application/octet-stream", |
| 214 | @body foo: string |
| 215 | ): void; |
| 216 | ` |
| 217 | ); |
| 218 | ok(res.paths["/"].post.requestBody.content["application/octet-stream"]); |
| 219 | deepStrictEqual(res.paths["/"].post.requestBody.content["application/octet-stream"].schema, { |
| 220 | type: "string", |
| 221 | }); |
| 222 | }); |
| 223 | |
| 224 | it("query named contentType doesn't get resolved as the content type parmaeter.", async () => { |
| 225 | const res = await openApiFor( |
| 226 | ` |
| 227 | op test( |
| 228 | @query contentType: "application/octet-stream", |
| 229 | @body foo: string |
| 230 | ): void; |
| 231 | ` |
| 232 | ); |
| 233 | strictEqual(res.paths["/"].post.requestBody.content["application/octet-stream"], undefined); |
| 234 | ok(res.paths["/"].post.requestBody.content["application/json"]); |
| 235 | }); |
| 236 | }); |
| 237 | }); |
| 238 | |