From 3dc9297094ce7681ce202f4e7101c311dac2dc5b Mon Sep 17 00:00:00 2001 From: "Lingling Ye (from Dev Box)" Date: Thu, 14 May 2026 17:02:19 +0800 Subject: [PATCH] add tests for multiple sync tokens scenarios --- .../src/internal/syncTokenPolicy.ts | 5 +- .../test/internal/node/http.spec.ts | 59 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/src/internal/syncTokenPolicy.ts b/sdk/appconfiguration/app-configuration/src/internal/syncTokenPolicy.ts index fc077b4a1f55..aef919b37692 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/syncTokenPolicy.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/syncTokenPolicy.ts @@ -69,7 +69,10 @@ export class SyncTokens { return; } - const newTokens = syncTokenHeaderValue.split(",").map(parseSyncToken); + const newTokens = syncTokenHeaderValue + .split(",") + .map((s) => s.trim()) + .map(parseSyncToken); for (const newToken of newTokens) { const existingToken = this._currentSyncTokens.get(newToken.id); diff --git a/sdk/appconfiguration/app-configuration/test/internal/node/http.spec.ts b/sdk/appconfiguration/app-configuration/test/internal/node/http.spec.ts index 675672154a1a..b6aa086f6882 100644 --- a/sdk/appconfiguration/app-configuration/test/internal/node/http.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/internal/node/http.spec.ts @@ -65,6 +65,11 @@ describe("http request related tests", () => { splitAndSort(syncTokens.getSyncTokenHeaderValue()), ); + // multiple tokens with spaces after commas (as HTTP may join duplicate headers) + syncTokens.addSyncTokenFromHeaderValue(undefined); // reset + syncTokens.addSyncTokenFromHeaderValue("x=val1;sn=1, y=val2;sn=2"); + assert.equal(splitAndSort(syncTokens.getSyncTokenHeaderValue()), "x=val1,y=val2"); + // and if we get back undefined (ie, the header wasn't there) then it // resets the entire thing // (sync tokens are temporary in nature and expire as things are committed @@ -232,6 +237,60 @@ describe("http request related tests", () => { assert.equal(syncTokens.getSyncTokenHeaderValue(), "listRevisions=value"); }); + it("handles multiple sync tokens in a single response header", async () => { + let capturedSyncTokenHeader: string | undefined; + let callCount = 0; + + client = createMockSyncTokenClient(syncTokens, async (request: PipelineRequest) => { + callCount++; + if (callCount === 1) { + return { + headers: createHttpHeaders({ + "sync-token": "tokenA=valueA;sn=1,tokenB=valueB;sn=2,tokenC=valueC;sn=3", + }), + status: 200, + request, + }; + } + capturedSyncTokenHeader = request.headers.get("sync-token"); + return { + headers: createHttpHeaders(), + status: 200, + request, + }; + }); + + await client.setConfigurationSetting({ key: "key1" }); + + const headerValue = syncTokens.getSyncTokenHeaderValue()!; + const tokens = headerValue.split(",").sort(); + assert.equal(tokens.length, 3); + assert.deepEqual(tokens, ["tokenA=valueA", "tokenB=valueB", "tokenC=valueC"]); + + await client.getConfigurationSetting({ key: "key2" }); + const sentTokens = capturedSyncTokenHeader!.split(",").sort(); + assert.deepEqual(sentTokens, ["tokenA=valueA", "tokenB=valueB", "tokenC=valueC"]); + }); + + it("handles sync tokens with spaces after commas (HTTP joined headers)", async () => { + client = createMockSyncTokenClient(syncTokens, async (request: PipelineRequest) => { + return { + headers: createHttpHeaders({ + "sync-token": "tokenA=valueA;sn=1, tokenB=valueB;sn=2", + }), + status: 200, + request, + }; + }); + + await client.setConfigurationSetting({ key: "key1" }); + + const headerValue = syncTokens.getSyncTokenHeaderValue()!; + const tokens = headerValue.split(",").sort(); + assert.equal(tokens.length, 2); + assert.deepEqual(tokens, ["tokenA=valueA", "tokenB=valueB"]); + }); + it("setReadOnly (clear and set)", async () => { client = createMockSyncTokenClient(syncTokens, async (request: PipelineRequest) => { return {