Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/http-specs"
---

add test for discriminator model without subtypes and query params with `$` prefixes
4 changes: 4 additions & 0 deletions packages/http-client-js/.testignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ type/union/discriminated
# Multipart - https://github.com/microsoft/typespec/issues/9155

payload/multipart

# Body root issues - https://github.com/microsoft/typespec/issues/10877

parameters/body-root
51 changes: 51 additions & 0 deletions packages/http-specs/spec-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,22 @@ Expected request body:
{ "name": "foo" }
```

### Parameters_BodyRoot_nested

- Endpoint: `post /parameters/body-root/nested`

Test case for a `@bodyRoot` parameter nested inside a wrapper model.

Emitters must resolve the accessor path through the wrapper (e.g.
`body.bodyRootParameters`) rather than referencing the property name
directly.

Expected request body:

```json
{ "category": "widget", "linkType": "hard", "wasSuccessful": true }
```

### Parameters_CollectionFormat_Header_csv

- Endpoint: `get /parameters/collection-format/header/csv`
Expand Down Expand Up @@ -1534,6 +1550,18 @@ Second request path:

Expect to handle a constant value for query and mock api returns nothing

### Parameters_Query_SpecialChar_dollarSign

- Endpoint: `get /parameters/query/special-char/dollarSign`

Send a request with a dollar-sign prefixed`$filter` query parameter.

Expected query parameter:

- `$filter` = "status eq 'active'"

Expected response status code: 204

### Parameters_Spread_Alias_spreadAsRequestBody

- Endpoint: `put /parameters/spread/alias/request-body`
Expand Down Expand Up @@ -6226,6 +6254,18 @@ Expected response body:
{ "wingspan": 1, "kind": "sparrow" }
```

### Type_Model_Inheritance_SingleDiscriminator_getNoSubtypesModel

- Endpoint: `get /type/model/inheritance/single-discriminator/no-subtypes/model`

Generate and receive a discriminated model that has no defined subtypes.
The base model declares a discriminator but no models extend it.
Expected response body:

```json
{ "kind": "salmon", "size": 10 }
```

### Type_Model_Inheritance_SingleDiscriminator_getRecursiveModel

- Endpoint: `get /type/model/inheritance/single-discriminator/recursivemodel`
Expand Down Expand Up @@ -6278,6 +6318,17 @@ Expected input body:
{ "wingspan": 1, "kind": "sparrow" }
```

### Type_Model_Inheritance_SingleDiscriminator_putNoSubtypesModel

- Endpoint: `put /type/model/inheritance/single-discriminator/no-subtypes/model`

Send a discriminated model that has no defined subtypes.
Expected input body:

```json
{ "kind": "salmon", "size": 10 }
```

### Type_Model_Inheritance_SingleDiscriminator_putRecursiveModel

- Endpoint: `put /type/model/inheritance/single-discriminator/recursivemodel`
Expand Down
36 changes: 36 additions & 0 deletions packages/http-specs/specs/parameters/body-root/main.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import "@typespec/http";
import "@typespec/spector";
Comment thread
iscai-msft marked this conversation as resolved.

using Http;
using Spector;

@doc("Test for @bodyRoot parameter patterns.")
@scenarioService("/parameters/body-root")
namespace Parameters.BodyRoot;

model BodyRootModel {
category?: string;
linkType?: string;
wasSuccessful?: boolean;
}

@scenario
@scenarioDoc("""
Test case for a \`@bodyRoot\` parameter nested inside a wrapper model.

Emitters must resolve the accessor path through the wrapper (e.g.
\`body.bodyRootParameters\`) rather than referencing the property name
directly.

Expected request body:
\`\`\`json
{ "category": "widget", "linkType": "hard", "wasSuccessful": true }
\`\`\`
""")
@route("/nested")
@post
op nested(
body: {
@bodyRoot bodyRootParameters: BodyRootModel;
},
Comment thread
iscai-msft marked this conversation as resolved.
): NoContentResponse;
19 changes: 19 additions & 0 deletions packages/http-specs/specs/parameters/body-root/mockapi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { json, passOnSuccess, ScenarioMockApi } from "@typespec/spec-api";

export const Scenarios: Record<string, ScenarioMockApi> = {};

Scenarios.Parameters_BodyRoot_nested = passOnSuccess({
uri: "/parameters/body-root/nested",
method: "post",
request: {
body: json({
category: "widget",
linkType: "hard",
wasSuccessful: true,
}),
},
response: {
status: 204,
},
kind: "MockApiDefinition",
});
20 changes: 20 additions & 0 deletions packages/http-specs/specs/parameters/query/main.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,23 @@ interface Constant {
queryParam: "constantValue",
): void;
}

@doc("Special char query parameter verification")
@route("/special-char")
interface SpecialChar {
@scenario
@scenarioDoc("""
Send a request with a dollar-sign prefixed`$filter` query parameter.

Expected query parameter:
- `$filter` = "status eq 'active'"

Expected response status code: 204
""")
@route("/dollarSign")
@get
dollarSign(
Comment thread
iscai-msft marked this conversation as resolved.
@query("$filter")
filter: string,
): void;
}
12 changes: 12 additions & 0 deletions packages/http-specs/specs/parameters/query/mockapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,15 @@ Scenarios.Parameters_Query_Constant_post = passOnSuccess({
},
kind: "MockApiDefinition",
});

Scenarios.Parameters_Query_SpecialChar_dollarSign = passOnSuccess({
uri: "/parameters/query/special-char/dollar-sign",
method: "get",
request: {
query: { $filter: "status eq 'active'" },
},
response: {
status: 204,
},
kind: "MockApiDefinition",
});
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ model TRex extends Dinosaur {
kind: "t-rex";
}

@doc("A discriminated model with no defined subtypes. The discriminator is declared but no models extend it.")
@discriminator("kind")
model Fish {
kind: string;
size: int32;
}

@scenario
@route("/model")
@scenarioDoc("""
Expand Down Expand Up @@ -170,3 +177,28 @@ op getWrongDiscriminator(): Bird;
""")
@get
op getLegacyModel(): Dinosaur;

@scenario
@route("/no-subtypes/model")
@scenarioDoc("""
Generate and receive a discriminated model that has no defined subtypes.
The base model declares a discriminator but no models extend it.
Expected response body:
```json
{"kind": "salmon", "size": 10}
```
""")
@get
op getNoSubtypesModel(): Fish;

@scenario
@route("/no-subtypes/model")
@scenarioDoc("""
Send a discriminated model that has no defined subtypes.
Expected input body:
```json
{"kind": "salmon", "size": 10}
```
""")
@put
op putNoSubtypesModel(@body input: Fish): NoContentResponse;
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,31 @@ Scenarios.Type_Model_Inheritance_SingleDiscriminator_getLegacyModel = passOnSucc
},
kind: "MockApiDefinition",
});

const noSubtypesBody = {
kind: "salmon",
size: 10,
};

Scenarios.Type_Model_Inheritance_SingleDiscriminator_getNoSubtypesModel = passOnSuccess({
uri: "/type/model/inheritance/single-discriminator/no-subtypes/model",
method: "get",
request: {},
response: {
status: 200,
body: json(noSubtypesBody),
},
kind: "MockApiDefinition",
});

Scenarios.Type_Model_Inheritance_SingleDiscriminator_putNoSubtypesModel = passOnSuccess({
uri: "/type/model/inheritance/single-discriminator/no-subtypes/model",
method: "put",
request: {
body: json(noSubtypesBody),
},
response: {
status: 204,
},
kind: "MockApiDefinition",
});
Loading