From 88c78577c74ab09b68d1509ea9f15f8683ecc7f7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 08:47:42 +0000
Subject: [PATCH 1/7] Initial plan
From 2bb1b95df78680c92307504e1135727d67ae8a6e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 08:54:53 +0000
Subject: [PATCH 2/7] fix: remove broken prettier.format() call in
xmlBodyEquals that caused UndefinedParserError
Co-authored-by: weidongxu-microsoft <53292327+weidongxu-microsoft@users.noreply.github.com>
---
packages/spec-api/src/request-validations.ts | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/packages/spec-api/src/request-validations.ts b/packages/spec-api/src/request-validations.ts
index 223b439ce2a..3dc7f7260ab 100644
--- a/packages/spec-api/src/request-validations.ts
+++ b/packages/spec-api/src/request-validations.ts
@@ -1,5 +1,4 @@
import deepEqual from "deep-equal";
-import * as prettier from "prettier";
import { parseString } from "xml2js";
import { CollectionFormat, RequestExt } from "./types.js";
import { ValidationError } from "./validation-error.js";
@@ -66,11 +65,7 @@ export const validateXmlBodyEquals = (request: RequestExt, expectedBody: string)
});
if (!deepEqual(actualParsedBody, expectedParsedBody, { strict: true })) {
- throw new ValidationError(
- BODY_NOT_EQUAL_ERROR_MESSAGE,
- prettier.format(expectedBody),
- prettier.format(request.body),
- );
+ throw new ValidationError(BODY_NOT_EQUAL_ERROR_MESSAGE, expectedBody, request.rawBody);
}
};
From 42d17aa7ec5593e1af76771073d1029329556905 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 08:55:39 +0000
Subject: [PATCH 3/7] fix: update ModelWithDatetime rfc3339 value to match
client output (no .000 milliseconds)
Co-authored-by: weidongxu-microsoft <53292327+weidongxu-microsoft@users.noreply.github.com>
---
packages/http-specs/specs/payload/xml/main.tsp | 2 +-
packages/http-specs/specs/payload/xml/mockapi.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/http-specs/specs/payload/xml/main.tsp b/packages/http-specs/specs/payload/xml/main.tsp
index d768dfb2d2d..a3ccdc8964b 100644
--- a/packages/http-specs/specs/payload/xml/main.tsp
+++ b/packages/http-specs/specs/payload/xml/main.tsp
@@ -364,7 +364,7 @@ interface ModelWithDatetimeValue
ModelWithDatetime,
"""
- 2022-08-26T18:38:00.000Z
+ 2022-08-26T18:38:00Z
Fri, 26 Aug 2022 14:38:00 GMT
"""
diff --git a/packages/http-specs/specs/payload/xml/mockapi.ts b/packages/http-specs/specs/payload/xml/mockapi.ts
index 1560c5e84ba..c7fccb850b8 100644
--- a/packages/http-specs/specs/payload/xml/mockapi.ts
+++ b/packages/http-specs/specs/payload/xml/mockapi.ts
@@ -131,7 +131,7 @@ export const modelWithEnum = `
export const modelWithDatetime = `
- 2022-08-26T18:38:00.000Z
+ 2022-08-26T18:38:00Z
Fri, 26 Aug 2022 14:38:00 GMT
`;
From 20a9187fe76b5df307de420a356ada2ea14a754d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 09:05:03 +0000
Subject: [PATCH 4/7] fix(spec-api): normalize RFC3339 UTC datetimes in XML
body comparison
Co-authored-by: weidongxu-microsoft <53292327+weidongxu-microsoft@users.noreply.github.com>
---
.../http-specs/specs/payload/xml/main.tsp | 2 +-
.../http-specs/specs/payload/xml/mockapi.ts | 2 +-
packages/spec-api/src/request-validations.ts | 24 ++++++++++++++++++-
3 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/packages/http-specs/specs/payload/xml/main.tsp b/packages/http-specs/specs/payload/xml/main.tsp
index a3ccdc8964b..d768dfb2d2d 100644
--- a/packages/http-specs/specs/payload/xml/main.tsp
+++ b/packages/http-specs/specs/payload/xml/main.tsp
@@ -364,7 +364,7 @@ interface ModelWithDatetimeValue
ModelWithDatetime,
"""
- 2022-08-26T18:38:00Z
+ 2022-08-26T18:38:00.000Z
Fri, 26 Aug 2022 14:38:00 GMT
"""
diff --git a/packages/http-specs/specs/payload/xml/mockapi.ts b/packages/http-specs/specs/payload/xml/mockapi.ts
index c7fccb850b8..1560c5e84ba 100644
--- a/packages/http-specs/specs/payload/xml/mockapi.ts
+++ b/packages/http-specs/specs/payload/xml/mockapi.ts
@@ -131,7 +131,7 @@ export const modelWithEnum = `
export const modelWithDatetime = `
- 2022-08-26T18:38:00Z
+ 2022-08-26T18:38:00.000Z
Fri, 26 Aug 2022 14:38:00 GMT
`;
diff --git a/packages/spec-api/src/request-validations.ts b/packages/spec-api/src/request-validations.ts
index 3dc7f7260ab..c8ad094e3cf 100644
--- a/packages/spec-api/src/request-validations.ts
+++ b/packages/spec-api/src/request-validations.ts
@@ -64,11 +64,33 @@ export const validateXmlBodyEquals = (request: RequestExt, expectedBody: string)
expectedParsedBody = result;
});
- if (!deepEqual(actualParsedBody, expectedParsedBody, { strict: true })) {
+ if (!deepEqual(normalizeXmlDatetimes(actualParsedBody), normalizeXmlDatetimes(expectedParsedBody), { strict: true })) {
throw new ValidationError(BODY_NOT_EQUAL_ERROR_MESSAGE, expectedBody, request.rawBody);
}
};
+/**
+ * Recursively walk a parsed-XML object (from xml2js) and normalize RFC3339 UTC datetime strings
+ * so that equivalent forms compare equal (e.g. "2022-08-26T18:38:00.000Z" == "2022-08-26T18:38:00Z").
+ */
+function normalizeXmlDatetimes(value: unknown): unknown {
+ if (typeof value === "string") {
+ // Strip trailing zero milliseconds from UTC RFC3339 timestamps: .000Z -> Z
+ return value.replace(/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\.0+Z$/, "$1Z");
+ }
+ if (Array.isArray(value)) {
+ return value.map(normalizeXmlDatetimes);
+ }
+ if (value !== null && typeof value === "object") {
+ const result: Record = {};
+ for (const [k, v] of Object.entries(value as Record)) {
+ result[k] = normalizeXmlDatetimes(v);
+ }
+ return result;
+ }
+ return value;
+}
+
export const validateCoercedDateBodyEquals = (
request: RequestExt,
expectedBody: unknown | undefined,
From 7e3ef5d29eb5f386a0033ee384948e83c242ebe9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 09:29:19 +0000
Subject: [PATCH 5/7] fix(http-specs): accept both RFC3339 UTC datetime forms
in ModelWithDatetime XML test
Co-authored-by: weidongxu-microsoft <53292327+weidongxu-microsoft@users.noreply.github.com>
---
.../http-specs/specs/payload/xml/mockapi.ts | 54 ++++++++++++++++---
packages/spec-api/src/request-validations.ts | 24 +--------
2 files changed, 49 insertions(+), 29 deletions(-)
diff --git a/packages/http-specs/specs/payload/xml/mockapi.ts b/packages/http-specs/specs/payload/xml/mockapi.ts
index 1560c5e84ba..de2d3f85834 100644
--- a/packages/http-specs/specs/payload/xml/mockapi.ts
+++ b/packages/http-specs/specs/payload/xml/mockapi.ts
@@ -136,6 +136,16 @@ export const modelWithDatetime = `
`;
+// Some clients serialize UTC datetimes without trailing zero milliseconds. Both
+// "2022-08-26T18:38:00.000Z" and "2022-08-26T18:38:00Z" are valid RFC3339 representations
+// of the same instant; accept either form.
+const modelWithDatetimeNoMs = `
+
+ 2022-08-26T18:38:00Z
+ Fri, 26 Aug 2022 14:38:00 GMT
+
+`;
+
function createServerTests(uri: string, data?: any) {
return {
get: passOnSuccess({
@@ -251,12 +261,44 @@ const Payload_Xml_ModelWithEnum = createServerTests("/payload/xml/modelWithEnum"
Scenarios.Payload_Xml_ModelWithEnumValue_get = Payload_Xml_ModelWithEnum.get;
Scenarios.Payload_Xml_ModelWithEnumValue_put = Payload_Xml_ModelWithEnum.put;
-const Payload_Xml_ModelWithDatetime = createServerTests(
- "/payload/xml/modelWithDatetime",
- modelWithDatetime,
-);
-Scenarios.Payload_Xml_ModelWithDatetimeValue_get = Payload_Xml_ModelWithDatetime.get;
-Scenarios.Payload_Xml_ModelWithDatetimeValue_put = Payload_Xml_ModelWithDatetime.put;
+Scenarios.Payload_Xml_ModelWithDatetimeValue_get = passOnSuccess({
+ uri: "/payload/xml/modelWithDatetime",
+ method: "get",
+ request: {},
+ response: {
+ status: 200,
+ body: xml(modelWithDatetime),
+ },
+ kind: "MockApiDefinition",
+});
+
+Scenarios.Payload_Xml_ModelWithDatetimeValue_put = passOnSuccess({
+ uri: "/payload/xml/modelWithDatetime",
+ method: "put",
+ request: {
+ body: xml(modelWithDatetime),
+ },
+ handler: (req: MockRequest) => {
+ req.expect.containsHeader("content-type", "application/xml");
+ // Accept both "2022-08-26T18:38:00.000Z" and "2022-08-26T18:38:00Z" as equivalent UTC datetimes.
+ let firstError: unknown;
+ try {
+ req.expect.xmlBodyEquals(modelWithDatetime);
+ } catch (e) {
+ firstError = e;
+ }
+ if (firstError !== undefined) {
+ req.expect.xmlBodyEquals(modelWithDatetimeNoMs);
+ }
+ return {
+ status: 204,
+ };
+ },
+ response: {
+ status: 204,
+ },
+ kind: "MockApiDefinition",
+});
export const xmlError = `
diff --git a/packages/spec-api/src/request-validations.ts b/packages/spec-api/src/request-validations.ts
index c8ad094e3cf..3dc7f7260ab 100644
--- a/packages/spec-api/src/request-validations.ts
+++ b/packages/spec-api/src/request-validations.ts
@@ -64,33 +64,11 @@ export const validateXmlBodyEquals = (request: RequestExt, expectedBody: string)
expectedParsedBody = result;
});
- if (!deepEqual(normalizeXmlDatetimes(actualParsedBody), normalizeXmlDatetimes(expectedParsedBody), { strict: true })) {
+ if (!deepEqual(actualParsedBody, expectedParsedBody, { strict: true })) {
throw new ValidationError(BODY_NOT_EQUAL_ERROR_MESSAGE, expectedBody, request.rawBody);
}
};
-/**
- * Recursively walk a parsed-XML object (from xml2js) and normalize RFC3339 UTC datetime strings
- * so that equivalent forms compare equal (e.g. "2022-08-26T18:38:00.000Z" == "2022-08-26T18:38:00Z").
- */
-function normalizeXmlDatetimes(value: unknown): unknown {
- if (typeof value === "string") {
- // Strip trailing zero milliseconds from UTC RFC3339 timestamps: .000Z -> Z
- return value.replace(/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\.0+Z$/, "$1Z");
- }
- if (Array.isArray(value)) {
- return value.map(normalizeXmlDatetimes);
- }
- if (value !== null && typeof value === "object") {
- const result: Record = {};
- for (const [k, v] of Object.entries(value as Record)) {
- result[k] = normalizeXmlDatetimes(v);
- }
- return result;
- }
- return value;
-}
-
export const validateCoercedDateBodyEquals = (
request: RequestExt,
expectedBody: unknown | undefined,
From 812d87334583563b26d70db76404080467761c1a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 09:39:46 +0000
Subject: [PATCH 6/7] chore: add changelog for RFC3339 datetime fix
Co-authored-by: weidongxu-microsoft <53292327+weidongxu-microsoft@users.noreply.github.com>
---
.../copilot-fix-xml-test-case-error-2026-2-11-9-39-28.md | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 .chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-9-39-28.md
diff --git a/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-9-39-28.md b/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-9-39-28.md
new file mode 100644
index 00000000000..549e7540ff9
--- /dev/null
+++ b/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-9-39-28.md
@@ -0,0 +1,8 @@
+---
+changeKind: fix
+packages:
+ - "@typespec/http-specs"
+ - "@typespec/spec-api"
+---
+
+Accept both `2022-08-26T18:38:00.000Z` and `2022-08-26T18:38:00Z` as valid RFC3339 UTC datetime forms in the `ModelWithDatetime` XML scenario
\ No newline at end of file
From 0f8a294c742fb386db421456f2e7c1dc041a0206 Mon Sep 17 00:00:00 2001
From: Weidong Xu
Date: Wed, 11 Mar 2026 20:09:34 +0800
Subject: [PATCH 7/7] remove changelog by agent, add 2 by hand
---
...> copilot-fix-xml-test-case-error-2026-2-11-20-7-16.md} | 3 +--
.../copilot-fix-xml-test-case-error-2026-2-11-20-8-54.md | 7 +++++++
2 files changed, 8 insertions(+), 2 deletions(-)
rename .chronus/changes/{copilot-fix-xml-test-case-error-2026-2-11-9-39-28.md => copilot-fix-xml-test-case-error-2026-2-11-20-7-16.md} (83%)
create mode 100644 .chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-20-8-54.md
diff --git a/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-9-39-28.md b/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-20-7-16.md
similarity index 83%
rename from .chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-9-39-28.md
rename to .chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-20-7-16.md
index 549e7540ff9..37a44acbc51 100644
--- a/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-9-39-28.md
+++ b/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-20-7-16.md
@@ -2,7 +2,6 @@
changeKind: fix
packages:
- "@typespec/http-specs"
- - "@typespec/spec-api"
---
-Accept both `2022-08-26T18:38:00.000Z` and `2022-08-26T18:38:00Z` as valid RFC3339 UTC datetime forms in the `ModelWithDatetime` XML scenario
\ No newline at end of file
+Accept both `2022-08-26T18:38:00.000Z` and `2022-08-26T18:38:00Z` as valid RFC3339 UTC datetime forms in the `ModelWithDatetime` XML scenario.
\ No newline at end of file
diff --git a/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-20-8-54.md b/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-20-8-54.md
new file mode 100644
index 00000000000..4446f699c26
--- /dev/null
+++ b/.chronus/changes/copilot-fix-xml-test-case-error-2026-2-11-20-8-54.md
@@ -0,0 +1,7 @@
+---
+changeKind: fix
+packages:
+ - "@typespec/spec-api"
+---
+
+Remove prettier used for ValidationError message, in validateXmlBodyEquals.
\ No newline at end of file