Skip to content

Commit e0b6dbe

Browse files
refactor(audience-sdk): introduce ResponseFields and ConsentBodyFields
Names the wire-format JSON keys for the messages POST envelope, the messages POST response, and the consent-sync PUT body, so runtime emit and test assert read from the same source. - Constants.cs: adds ResponseFields (MessagesEnvelope, Rejected) for the messages POST envelope and the rejected-count response key. - Constants.cs: adds ConsentBodyFields (Status, Source) for the tracking-consent PUT body. - HttpTransport.cs: BuildPayload writes the envelope under ResponseFields.MessagesEnvelope; ParseRejectedCount reads ResponseFields.Rejected. - ImmutableAudience.cs: SyncConsentLevel writes the PUT body under ConsentBodyFields.Status / Source. - HttpTransportTests.cs: MockHandler responses and StringAssert.StartsWith fixtures read from ResponseFields.MessagesEnvelope / Rejected. - ConsentSyncTests.cs: PUT body asserts read from ConsentBodyFields.Status / Source and derive the expected status string from ConsentLevel.ToLowercaseString.
1 parent 9911cf6 commit e0b6dbe

5 files changed

Lines changed: 33 additions & 19 deletions

File tree

src/Packages/Audience/Runtime/Core/Constants.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ internal static class MessageTypes
6262
internal const string Alias = "alias";
6363
}
6464

65+
// JSON keys for the consent-sync PUT body.
66+
internal static class ConsentBodyFields
67+
{
68+
internal const string Status = "status";
69+
internal const string Source = "source";
70+
}
71+
72+
// JSON keys for the messages POST envelope and response.
73+
internal static class ResponseFields
74+
{
75+
internal const string MessagesEnvelope = "messages";
76+
internal const string Rejected = "rejected";
77+
}
78+
6579
// Wire-format field names that cross module boundaries inside the SDK
6680
// (read by one module, written by another).
6781
internal static class MessageFields

src/Packages/Audience/Runtime/ImmutableAudience.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,8 +623,8 @@ private static void SyncConsentToBackend(AudienceConfig config, ConsentLevel lev
623623

624624
var body = Json.Serialize(new Dictionary<string, object>
625625
{
626-
["status"] = level.ToLowercaseString(),
627-
["source"] = Constants.ConsentSource,
626+
[ConsentBodyFields.Status] = level.ToLowercaseString(),
627+
[ConsentBodyFields.Source] = Constants.ConsentSource,
628628
// Explicit null lets the backend distinguish "unknown" from a missing field.
629629
["anonymousId"] = anonymousId!,
630630
});

src/Packages/Audience/Runtime/Transport/HttpTransport.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ private void ResetBackoff()
255255
// schema (#/components/schemas/MessagesRequest property "messages").
256256
private static string? BuildPayload(IReadOnlyList<string> paths)
257257
{
258-
var sb = new StringBuilder("{\"messages\":[");
258+
var sb = new StringBuilder($"{{\"{ResponseFields.MessagesEnvelope}\":[");
259259
var count = 0;
260260

261261
for (var i = 0; i < paths.Count; i++)
@@ -307,7 +307,7 @@ private static async Task<int> ParseRejectedCount(HttpResponseMessage response,
307307
try
308308
{
309309
var parsed = JsonReader.DeserializeObject(body);
310-
if (!parsed.TryGetValue("rejected", out var raw)) return 0;
310+
if (!parsed.TryGetValue(ResponseFields.Rejected, out var raw)) return 0;
311311
return raw switch
312312
{
313313
int i => i,

src/Packages/Audience/Tests/Runtime/ConsentSyncTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ public void SetConsent_FiresPut_WithExpectedBodyShape()
4343
var body = JsonReader.DeserializeObject(put.Body);
4444

4545
Assert.AreEqual(Constants.ConsentUrl("pk_imapik-test-key1"), put.Url);
46-
Assert.AreEqual("full", body["status"]);
47-
Assert.AreEqual(Constants.ConsentSource, body["source"]);
46+
Assert.AreEqual(ConsentLevel.Full.ToLowercaseString(), body[ConsentBodyFields.Status]);
47+
Assert.AreEqual(Constants.ConsentSource, body[ConsentBodyFields.Source]);
4848
Assert.IsTrue(body.ContainsKey("anonymousId"));
4949
Assert.IsNotNull(body["anonymousId"], "upgrade PUT must carry the current anonymousId");
5050
}
@@ -66,7 +66,7 @@ public void SetConsent_None_PutCarriesOldAnonymousId_AfterReset()
6666
var put = WaitForPut(handler);
6767
var body = JsonReader.DeserializeObject(put.Body);
6868

69-
Assert.AreEqual("none", body["status"]);
69+
Assert.AreEqual(ConsentLevel.None.ToLowercaseString(), body[ConsentBodyFields.Status]);
7070
Assert.AreEqual(seeded, body["anonymousId"],
7171
"revocation PUT must carry the id that was revoked, not null");
7272
Assert.IsFalse(File.Exists(AudiencePaths.IdentityFile(_testDir)),

src/Packages/Audience/Tests/Runtime/Transport/HttpTransportTests.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public async Task SendBatchAsync_200_DeletesFilesFromDisk()
5252
_store.Write("{\"type\":\"track\",\"eventName\":\"a\"}");
5353
_store.Write("{\"type\":\"track\",\"eventName\":\"b\"}");
5454

55-
var handler = new MockHandler(HttpStatusCode.OK, "{\"accepted\":2,\"rejected\":0}");
55+
var handler = new MockHandler(HttpStatusCode.OK, $"{{\"accepted\":2,\"{ResponseFields.Rejected}\":0}}");
5656
using var transport = new HttpTransport(_store, "pk_imapik-test-key1", handler: handler);
5757

5858
var sent = await transport.SendBatchAsync();
@@ -72,7 +72,7 @@ public async Task SendBatchAsync_200_SendsGzippedPayloadWithCorrectHeaders()
7272
string? capturedContentType = null;
7373
string? capturedContentEncoding = null;
7474
// Read body inside the callback. The request content is disposed after SendAsync returns.
75-
var handler = new MockHandler(HttpStatusCode.OK, "{\"accepted\":1,\"rejected\":0}",
75+
var handler = new MockHandler(HttpStatusCode.OK, $"{{\"accepted\":1,\"{ResponseFields.Rejected}\":0}}",
7676
onRequest: req =>
7777
{
7878
capturedKey = string.Join("", req.Headers.GetValues("x-immutable-publishable-key"));
@@ -89,7 +89,7 @@ public async Task SendBatchAsync_200_SendsGzippedPayloadWithCorrectHeaders()
8989
Assert.AreEqual(Constants.GzipEncoding, capturedContentEncoding);
9090

9191
var decompressed = DecompressGzip(capturedBody!);
92-
StringAssert.StartsWith("{\"messages\":[", decompressed);
92+
StringAssert.StartsWith($"{{\"{ResponseFields.MessagesEnvelope}\":[", decompressed);
9393
StringAssert.EndsWith("]}", decompressed);
9494
StringAssert.Contains("\"eventName\":\"test\"", decompressed);
9595
}
@@ -103,7 +103,7 @@ public async Task SendBatchAsync_200_SendsPlainJsonPayloadWithoutContentEncoding
103103
string? capturedContentType = null;
104104
int capturedContentEncodingCount = -1;
105105
string? capturedBody = null;
106-
var handler = new MockHandler(HttpStatusCode.OK, "{\"accepted\":1,\"rejected\":0}",
106+
var handler = new MockHandler(HttpStatusCode.OK, $"{{\"accepted\":1,\"{ResponseFields.Rejected}\":0}}",
107107
onRequest: req =>
108108
{
109109
capturedKey = string.Join("", req.Headers.GetValues("x-immutable-publishable-key"));
@@ -118,7 +118,7 @@ public async Task SendBatchAsync_200_SendsPlainJsonPayloadWithoutContentEncoding
118118
Assert.AreEqual("pk_imapik-test-key1", capturedKey);
119119
Assert.AreEqual(Constants.MediaTypeJson, capturedContentType);
120120
Assert.AreEqual(0, capturedContentEncodingCount, "no Content-Encoding header is permitted in v1");
121-
StringAssert.StartsWith("{\"messages\":[", capturedBody);
121+
StringAssert.StartsWith($"{{\"{ResponseFields.MessagesEnvelope}\":[", capturedBody);
122122
StringAssert.EndsWith("]}", capturedBody);
123123
StringAssert.Contains("\"eventName\":\"test\"", capturedBody);
124124
}
@@ -130,7 +130,7 @@ public async Task SendBatchAsync_200_UsesCorrectUrlForTestKey()
130130
_store.Write("{\"type\":\"track\"}");
131131

132132
HttpRequestMessage? captured = null;
133-
var handler = new MockHandler(HttpStatusCode.OK, "{\"accepted\":1,\"rejected\":0}",
133+
var handler = new MockHandler(HttpStatusCode.OK, $"{{\"accepted\":1,\"{ResponseFields.Rejected}\":0}}",
134134
onRequest: req => captured = req);
135135
using var transport = new HttpTransport(_store, "pk_imapik-test-key1", handler: handler);
136136

@@ -145,7 +145,7 @@ public async Task SendBatchAsync_200_UsesCorrectUrlForProdKey()
145145
_store.Write("{\"type\":\"track\"}");
146146

147147
HttpRequestMessage? captured = null;
148-
var handler = new MockHandler(HttpStatusCode.OK, "{\"accepted\":1,\"rejected\":0}",
148+
var handler = new MockHandler(HttpStatusCode.OK, $"{{\"accepted\":1,\"{ResponseFields.Rejected}\":0}}",
149149
onRequest: req => captured = req);
150150
using var transport = new HttpTransport(_store, "pk_imapik-prodkey", handler: handler);
151151

@@ -160,7 +160,7 @@ public async Task SendBatchAsync_BaseUrlOverride_WinsOverKeyPrefix()
160160
_store.Write("{\"type\":\"track\"}");
161161

162162
HttpRequestMessage? captured = null;
163-
var handler = new MockHandler(HttpStatusCode.OK, "{\"accepted\":1,\"rejected\":0}",
163+
var handler = new MockHandler(HttpStatusCode.OK, $"{{\"accepted\":1,\"{ResponseFields.Rejected}\":0}}",
164164
onRequest: req => captured = req);
165165
const string custom = "https://api.dev.immutable.com";
166166
// Test-prefixed key would resolve to Sandbox on its own; the
@@ -298,7 +298,7 @@ public async Task SendBatchAsync_429ThenSuccess_DeliversBatchAndClearsBackoff()
298298
return callCount == 1
299299
? new HttpResponseMessage((HttpStatusCode)429)
300300
: new HttpResponseMessage(HttpStatusCode.OK)
301-
{ Content = new StringContent("{\"accepted\":1,\"rejected\":0}") };
301+
{ Content = new StringContent($"{{\"accepted\":1,\"{ResponseFields.Rejected}\":0}}") };
302302
});
303303
AudienceError? reportedError = null;
304304
using var transport = new HttpTransport(_store, "pk_imapik-test-key1",
@@ -325,7 +325,7 @@ public async Task SendBatchAsync_200_WithRejected_DeletesFilesAndSurfacesValidat
325325
_store.Write("{\"type\":\"track\",\"eventName\":\"a\"}");
326326
_store.Write("{\"type\":\"track\",\"eventName\":\"b\"}");
327327

328-
var handler = new MockHandler(HttpStatusCode.OK, "{\"accepted\":1,\"rejected\":1}");
328+
var handler = new MockHandler(HttpStatusCode.OK, $"{{\"accepted\":1,\"{ResponseFields.Rejected}\":1}}");
329329
AudienceError? reportedError = null;
330330
using var transport = new HttpTransport(_store, "pk_imapik-test-key1",
331331
onError: e => reportedError = e, handler: handler);
@@ -343,7 +343,7 @@ public async Task SendBatchAsync_200_ZeroRejected_DoesNotFireOnError()
343343
{
344344
_store.Write("{\"type\":\"track\",\"eventName\":\"a\"}");
345345

346-
var handler = new MockHandler(HttpStatusCode.OK, "{\"accepted\":1,\"rejected\":0}");
346+
var handler = new MockHandler(HttpStatusCode.OK, $"{{\"accepted\":1,\"{ResponseFields.Rejected}\":0}}");
347347
AudienceError? reportedError = null;
348348
using var transport = new HttpTransport(_store, "pk_imapik-test-key1",
349349
onError: e => reportedError = e, handler: handler);
@@ -468,7 +468,7 @@ public async Task BackoffMs_ResetsAfterSuccess()
468468
return callCount <= 2
469469
? new HttpResponseMessage(HttpStatusCode.InternalServerError)
470470
: new HttpResponseMessage(HttpStatusCode.OK)
471-
{ Content = new StringContent("{\"accepted\":1,\"rejected\":0}") };
471+
{ Content = new StringContent($"{{\"accepted\":1,\"{ResponseFields.Rejected}\":0}}") };
472472
});
473473
using var transport = new HttpTransport(_store, "pk_imapik-test-key1",
474474
handler: handler, getUtcNow: _getUtcNow);

0 commit comments

Comments
 (0)