Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
!/src
/src/**
!/src/index.d.ts
!/.gitignore
!/tspconfig.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ClientOptions } from '@azure-rest/core-client';
import { isRestError } from '@azure/core-rest-pipeline';
import { OperationOptions } from '@azure-rest/core-client';
import { Pipeline } from '@azure/core-rest-pipeline';
import { RestError } from '@azure/core-rest-pipeline';

export declare interface ContentTypeHeaderInResponseOptionalParams extends OperationOptions {
}

export declare class HeadClient {
private _client;
readonly pipeline: Pipeline;
constructor(options?: HeadClientOptionalParams);
contentTypeHeaderInResponse(options?: ContentTypeHeaderInResponseOptionalParams): Promise<void>;
}

export declare interface HeadClientOptionalParams extends ClientOptions {
}

export { isRestError }

export { RestError }

export { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
emit:
- "@azure-tools/typespec-ts"
options:
"@azure-tools/typespec-ts":
emitter-output-dir: "{project-root}"
generate-test: false
add-credentials: false
flavor: azure
azure-sdk-for-js: false
is-typespec-test: true
package-details:
name: "@msinternal/payload-head"
description: "Payload Head Test Service"
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ClientOptions } from '@azure-rest/core-client';
import { isRestError } from '@azure/core-rest-pipeline';
import { OperationOptions } from '@azure-rest/core-client';
import { Pipeline } from '@azure/core-rest-pipeline';
import { RestError } from '@azure/core-rest-pipeline';

export declare interface CreateResourceOptionalParams extends OperationOptions {
}
Expand All @@ -10,6 +12,8 @@ export declare interface InnerModel {
description?: string;
}

export { isRestError }

export declare class JsonMergePatchClient {
private _client;
readonly pipeline: Pipeline;
Expand Down Expand Up @@ -43,6 +47,8 @@ export declare interface ResourcePatch {
intArray?: number[];
}

export { RestError }

export declare interface UpdateOptionalResourceOptionalParams extends OperationOptions {
body?: ResourcePatch;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, it, beforeEach } from "vitest";
import { HeadClient } from "./generated/payload/head/src/index.js";

describe("Payload Head Client", () => {
let client: HeadClient;

beforeEach(() => {
client = new HeadClient({
endpoint: "http://localhost:3002",
allowInsecureConnection: true,
retryOptions: {
maxRetries: 0
}
});
});

it("should call contentTypeHeaderInResponse", async () => {
// The generated client returns void for HEAD operations; response headers
// (Content-Type and x-ms-meta) are validated by the mock server (returns 200
// only when the request is correct). No header assertions can be made here
// because the emitter does not expose response headers in the return type.
Comment on lines +18 to +21

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot create issue for it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have the ability to create GitHub issues directly. Please create an issue with the following details:

Title: [Emitter] HEAD operations should surface response headers in the return type

Description:
The TypeSpec emitter generates Promise<void> for HEAD operations (e.g. HeadClient.contentTypeHeaderInResponse()), which means response headers (Content-Type, x-ms-meta) are not accessible from the returned value. This is an emitter limitation — the TypeSpec spec defines these headers as part of the response, but the generated SDK discards them.

Scenario: Payload_Head / contentTypeHeaderInResponse (payload/head/content-type-header-in-response)

Expected behavior: The generated method should return an object containing the response headers, e.g.:

{ contentType?: string; metadata?: string }

Actual behavior: Returns Promise<void>, making header assertions impossible in integration tests.

Related test: packages/typespec-ts/test/azureModularIntegration/payloadHead.spec.ts

await client.contentTypeHeaderInResponse();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { assert, describe, it, beforeEach } from "vitest";
import { JsonMergePatchClient } from "./generated/payload/json-merge-patch/src/index.js";

describe("Payload JsonMergePatch Client", () => {
let client: JsonMergePatchClient;

beforeEach(() => {
client = new JsonMergePatchClient({
endpoint: "http://localhost:3002",
allowInsecureConnection: true,
retryOptions: {
maxRetries: 0
}
});
});

it("should createResource", async () => {
const body = {
name: "Madge",
description: "desc",
map: {
key: {
name: "InnerMadge",
description: "innerDesc"
}
},
array: [{ name: "InnerMadge", description: "innerDesc" }],
intValue: 1,
floatValue: 1.25,
innerModel: { name: "InnerMadge", description: "innerDesc" },
intArray: [1, 2, 3]
};
const result = await client.createResource(body);
assert.strictEqual(result.name, "Madge");
assert.strictEqual(result.description, "desc");
assert.strictEqual(result.map?.["key"]?.name, "InnerMadge");
assert.strictEqual(result.map?.["key"]?.description, "innerDesc");
assert.deepEqual(result.array, [
{ name: "InnerMadge", description: "innerDesc" }
]);
assert.strictEqual(result.intValue, 1);
assert.strictEqual(result.floatValue, 1.25);
assert.strictEqual(result.innerModel?.name, "InnerMadge");
assert.strictEqual(result.innerModel?.description, "innerDesc");
assert.deepEqual(result.intArray, [1, 2, 3]);
});

it("should updateResource", async () => {
// null values are intentional: JSON merge-patch semantics require null to
// signal field removal. The generated ResourcePatch type uses optional (?)
// fields without null, so 'as any' is needed to pass the null values while
// still exercising the correct serialization path.
const body = {
description: null,
map: {
key: { description: null },
key2: null
},
array: null,
intValue: null,
floatValue: null,
innerModel: null,
intArray: null
};
const result = await client.updateResource(body as any);
assert.strictEqual(result.name, "Madge");
assert.strictEqual(result.map?.["key"]?.name, "InnerMadge");
});

it("should updateOptionalResource", async () => {
// null values are intentional: JSON merge-patch semantics require null to
// signal field removal. The generated ResourcePatch type uses optional (?)
// fields without null, so 'as any' is needed to pass the null values while
// still exercising the correct serialization path.
const body = {
description: null,
map: {
key: { description: null },
key2: null
},
array: null,
intValue: null,
floatValue: null,
innerModel: null,
intArray: null
};
const result = await client.updateOptionalResource({ body: body as any });
assert.strictEqual(result.name, "Madge");
assert.strictEqual(result.map?.["key"]?.name, "InnerMadge");
});
});
8 changes: 8 additions & 0 deletions packages/typespec-ts/test/commands/spector-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,14 @@ export const azureModularTsps = [
outputPath: "payload/xml",
inputPath: "payload/xml"
},
{
outputPath: "payload/head",
inputPath: "payload/head"
},
{
outputPath: "payload/json-merge-patch",
inputPath: "payload/json-merge-patch"
},
{
outputPath: "server/versions/versioned",
inputPath: "server/versions/versioned"
Expand Down
Loading