Skip to content

Commit b154d89

Browse files
nattb8claude
andcommitted
chore(audience-sample): update sample app for sandbox removal and TestMode
- Remove IsTestKey() (referenced removed Constants.TestKeyPrefix). - Simplify RefreshStatusBar: all keys route to production; prod-warning now shows whenever a key is set without a BaseUrl override. - Add test-mode Toggle to UXML and wire it through InitForm → BuildAudienceConfig. - Update SampleAppUi: replace SandboxBaseUrl with ExplicitBaseUrl (prod URL), add TestMode element name constant. - Update live-fire tests: rename/replace prod-warning tests to match new behaviour, replace sandbox URL with prod URL in BaseUrl-override test, update stale comments. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 886a718 commit b154d89

5 files changed

Lines changed: 50 additions & 47 deletions

File tree

examples/audience/Assets/SampleApp/Resources/AudienceSample.uxml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
<ui:VisualElement class="field placeholder-host">
7979
<ui:Label class="field-label" text="PUBLISHABLE KEY" />
8080
<ui:TextField name="publishable-key" />
81-
<ui:Label class="field-placeholder" text="pk_imapik-test-yourkey" />
81+
<ui:Label class="field-placeholder" text="pk_imapik-yourkey" />
8282
</ui:VisualElement>
8383

8484
<ui:VisualElement class="field">
@@ -99,6 +99,12 @@
9999
text="Mirror SDK internal log output into the in-page event log below." />
100100
</ui:VisualElement>
101101

102+
<ui:VisualElement class="field">
103+
<ui:Toggle name="test-mode" label="TEST MODE" />
104+
<ui:Label class="helper-text below-field"
105+
text="Tags all outbound events with test: true so the backend can filter them from production analytics." />
106+
</ui:VisualElement>
107+
102108
<ui:VisualElement name="mobile-attribution-field" class="field">
103109
<ui:Toggle name="enable-mobile-attribution" label="MOBILE ATTRIBUTION" />
104110
<ui:Label class="helper-text below-field"
@@ -120,7 +126,7 @@
120126
<ui:TextField name="base-url" />
121127
<ui:Label class="field-placeholder" text="(derived from key)" />
122128
<ui:Label class="helper-text below-field"
123-
text="Bypass the SDK's prefix-based routing and target a specific backend. Leave empty to derive from the publishable key." />
129+
text="Target a specific backend. Leave empty to use the production endpoint." />
124130
</ui:VisualElement>
125131

126132
<ui:VisualElement class="advanced-grid">

examples/audience/Assets/SampleApp/Scripts/AudienceSample.UI.cs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ private static readonly (string TabId, string PanelId)[] Tabs =
5151
// ---- UXML element fields (Setup tab) ----
5252

5353
private TextField _publishableKey, _baseUrl, _flushInterval, _flushSize;
54+
private Toggle _testMode;
5455
private DropdownField _initialConsent;
5556
private Toggle _debug, _enableMobileAttribution;
5657
private Button _btnInit, _btnFlush, _btnReset, _btnShutdown, _btnDeleteData, _btnRequestAtt;
@@ -181,6 +182,7 @@ private void BindElements()
181182
_baseUrl = Require<TextField>("base-url");
182183
_initialConsent = Require<DropdownField>("initial-consent");
183184
_debug = Require<Toggle>("debug");
185+
_testMode = Require<Toggle>("test-mode");
184186
_enableMobileAttribution = Require<Toggle>("enable-mobile-attribution");
185187
// Inject a tick Label — Unity 2021.3 runtime panels render the
186188
// checked state as a plain coloured square otherwise. USS hides
@@ -610,15 +612,10 @@ private void RefreshStatusBar()
610612
var key = (_publishableKey.value ?? "").Trim();
611613
var overrideUrl = (_baseUrl?.value ?? "").Trim();
612614
bool keyEmpty = string.IsNullOrEmpty(key);
613-
bool isTest = !keyEmpty && IsTestKey(key);
614615
bool hasOverride = !string.IsNullOrEmpty(overrideUrl);
615-
// BaseUrl override skips prefix-based routing, so the prod-warning
616-
// rule no longer applies (studio is in explicit-target mode).
617-
string? derivedFromKey = keyEmpty ? null : (isTest ? Constants.SandboxBaseUrl : Constants.ProductionBaseUrl);
618-
string? endpoint = hasOverride ? overrideUrl : derivedFromKey;
619-
bool warnState = hasOverride || (!keyEmpty && !isTest);
620-
SetStatusCell(_statusEndpoint, endpoint, warnState ? "state-warn" : "state-ok");
621-
_prodWarning.EnableInClassList("hidden", hasOverride || keyEmpty || isTest);
616+
string? endpoint = hasOverride ? overrideUrl : (keyEmpty ? null : Constants.ProductionBaseUrl);
617+
SetStatusCell(_statusEndpoint, endpoint, hasOverride ? "state-warn" : "state-ok");
618+
_prodWarning.EnableInClassList("hidden", hasOverride || keyEmpty);
622619

623620
var consent = _initialised ? ImmutableAudience.CurrentConsent : ConsentOrder[Mathf.Clamp(_initialConsent?.index ?? 0, 0, ConsentOrder.Length - 1)];
624621
int cIdx = Array.IndexOf(ConsentOrder, consent);
@@ -676,16 +673,18 @@ internal readonly struct InitForm
676673
public readonly string BaseUrl;
677674
public readonly ConsentLevel Consent;
678675
public readonly bool Debug;
676+
public readonly bool TestMode;
679677
public readonly bool EnableMobileAttribution;
680678
public readonly int? FlushIntervalMs;
681679
public readonly int? FlushSize;
682680

683-
public InitForm(string publishableKey, string baseUrl, ConsentLevel consent, bool debug, bool enableMobileAttribution, int? flushIntervalMs, int? flushSize)
681+
public InitForm(string publishableKey, string baseUrl, ConsentLevel consent, bool debug, bool testMode, bool enableMobileAttribution, int? flushIntervalMs, int? flushSize)
684682
{
685683
PublishableKey = publishableKey;
686684
BaseUrl = baseUrl;
687685
Consent = consent;
688686
Debug = debug;
687+
TestMode = testMode;
689688
EnableMobileAttribution = enableMobileAttribution;
690689
FlushIntervalMs = flushIntervalMs;
691690
FlushSize = flushSize;
@@ -702,6 +701,7 @@ internal InitForm CaptureInitForm()
702701
baseUrl: (_baseUrl.value ?? "").Trim(),
703702
consent: ConsentOrder[consentIdx],
704703
debug: _debug.value,
704+
testMode: _testMode.value,
705705
enableMobileAttribution: _enableMobileAttribution.value,
706706
flushIntervalMs: flushIntervalMs,
707707
flushSize: flushSize);
@@ -780,9 +780,6 @@ private bool IsAliasReady()
780780
&& (fromId != toId || (_aliasFromType.value ?? "") != (_aliasToType.value ?? ""));
781781
}
782782

783-
private static bool IsTestKey(string? key) =>
784-
!string.IsNullOrEmpty(key) && key!.StartsWith(Constants.TestKeyPrefix, StringComparison.Ordinal);
785-
786783
private static void FlashCopied(VisualElement ve)
787784
{
788785
ve.AddToClassList("copied");

examples/audience/Assets/SampleApp/Scripts/AudienceSample.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,9 +331,8 @@ private void OnSdkStateChanged()
331331
// ---- Config builders ----
332332

333333
// Maps the captured Setup form to AudienceConfig. BaseUrl null → SDK
334-
// derives the endpoint from the publishable key prefix (test → sandbox,
335-
// else production). The flushInterval clamp emits a warn row when
336-
// the user requests <1s.
334+
// targets the production endpoint. The flushInterval clamp emits a warn
335+
// row when the user requests <1s.
337336
private AudienceConfig BuildAudienceConfig(InitForm form, Action<AudienceError> onError)
338337
{
339338
var config = new AudienceConfig
@@ -342,6 +341,7 @@ private AudienceConfig BuildAudienceConfig(InitForm form, Action<AudienceError>
342341
BaseUrl = string.IsNullOrEmpty(form.BaseUrl) ? null : form.BaseUrl,
343342
Consent = form.Consent,
344343
Debug = form.Debug,
344+
TestMode = form.TestMode,
345345
EnableMobileAttribution = form.EnableMobileAttribution,
346346
OnError = onError,
347347
};
@@ -364,6 +364,7 @@ private static Dictionary<string, object> BuildConfigEcho(AudienceConfig config)
364364
{
365365
["consent"] = config.Consent.ToString(),
366366
["debug"] = config.Debug,
367+
["testMode"] = config.TestMode,
367368
["enableMobileAttribution"] = config.EnableMobileAttribution,
368369
["flushIntervalSeconds"] = config.FlushIntervalSeconds,
369370
["flushSize"] = config.FlushSize,
@@ -377,7 +378,7 @@ private static Dictionary<string, object> BuildConfigEcho(AudienceConfig config)
377378
return echo;
378379
}
379380

380-
// Keeps the pk_imapik-test- / pk_imapik- prefix visible; masks the rest.
381+
// Keeps the pk_imapik- prefix visible; masks the rest.
381382
// Caller must guard against null/empty; signature non-nullable so the
382383
// dictionary insertion in BuildInitConfigEcho doesn't trip CS8601.
383384
private static string RedactPublishableKey(string key)

examples/audience/Assets/SampleApp/Tests/Runtime/SampleAppLiveFireTests.cs

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,13 @@ public void SetUp()
4545
System.IO.Directory.Delete(sdkDir, recursive: true);
4646

4747
// Unity's bundled Mono runtime ships a curated root-CA set that
48-
// does not include the chain api.sandbox.immutable.com presents,
49-
// so HttpClient under Mono2x raises "SSL connection could not be
50-
// established" on every Flush. The cert is valid; only Mono's
51-
// verification fails. IL2CPP uses the OS CA store and is fine.
48+
// may not include the full chain the backend presents, so
49+
// HttpClient under Mono2x can raise "SSL connection could not be
50+
// established". The cert is valid; only Mono's verification fails.
51+
// IL2CPP uses the OS CA store and is fine.
5252
//
53-
// Bypass cert validation IN THE TEST PROCESS ONLY so the same
54-
// suite exercises both backends. Production SDK code is
55-
// untouched. Acceptable risk: this test process talks only to
56-
// sandbox; live-fire payloads carry no real user data.
53+
// Bypass cert validation IN THE TEST PROCESS ONLY. Production SDK
54+
// code is untouched.
5755
System.Net.ServicePointManager.ServerCertificateValidationCallback =
5856
(_, _, _, _) => true;
5957

@@ -331,7 +329,7 @@ public IEnumerator Reset_RegeneratesAnonymousIdAndAcceptsTrack()
331329
{
332330
// Reset clears identity + queue and rolls a new anonymousId. A
333331
// following Track must serialise with the new anonymousId and
334-
// round-trip to sandbox without errors.
332+
// round-trip to the backend without errors.
335333
yield return LoadAndInit();
336334

337335
_root!.Q<Button>(SampleAppUi.Buttons.TypedEvent("progression")).Click();
@@ -495,12 +493,11 @@ public IEnumerator TypedEvent_LinkClicked_FlushReportsOk()
495493
[UnityTest]
496494
public IEnumerator Init_WithBaseUrlOverride_FlushReportsOk()
497495
{
498-
// Explicit BaseUrl skips the publishable-key prefix routing in
499-
// Constants. Same target endpoint here, but the override branch is
500-
// a different config setup path that IL2CPP could strip independently.
496+
// Explicit BaseUrl exercises the override code path independently
497+
// of the default-production path — IL2CPP could strip either branch.
501498
yield return LoadAndInit(configure: root =>
502499
{
503-
root.Q<TextField>(SampleAppUi.Setup.BaseUrl).value = SampleAppUi.SandboxBaseUrl;
500+
root.Q<TextField>(SampleAppUi.Setup.BaseUrl).value = SampleAppUi.ExplicitBaseUrl;
504501
});
505502

506503
_root!.Q<Button>(SampleAppUi.Buttons.TypedEvent("progression")).Click();
@@ -739,29 +736,30 @@ public IEnumerator IdentityPanel_PopulatesUserIdAfterIdentify()
739736
}
740737

741738
[UnityTest]
742-
public IEnumerator ProdWarning_HiddenForTestKey()
739+
public IEnumerator ProdWarning_VisibleForAnyKey()
743740
{
744-
// The default env-var key is a test key (pk_imapik-test-…). The
745-
// prod-warning banner should stay hidden after RefreshStatusBar.
741+
// All keys route to production. The prod-warning banner must be
742+
// visible whenever a key is entered without a BaseUrl override.
746743
yield return LoadSceneOnly();
747744
_root!.Q<TextField>(SampleAppUi.Setup.PublishableKey).value = _key;
748745
yield return null;
749746

750-
Assert.IsTrue(_root.Q<Label>(SampleAppUi.ProdWarning).ClassListContains(SampleAppUi.HiddenClass),
751-
"prod-warning should be hidden when the publishable-key is a test key");
747+
Assert.IsFalse(_root.Q<Label>(SampleAppUi.ProdWarning).ClassListContains(SampleAppUi.HiddenClass),
748+
"prod-warning should be visible when a publishable-key is entered");
752749
}
753750

754751
[UnityTest]
755-
public IEnumerator ProdWarning_VisibleForNonTestKey()
752+
public IEnumerator ProdWarning_HiddenWhenBaseUrlOverrideSet()
756753
{
757-
// A key without the "test-" segment looks production. Don't actually
758-
// Init so we don't live-fire to prod; just verify the warning UI.
754+
// A BaseUrl override puts the studio in explicit-target mode, so
755+
// the prod-warning is suppressed.
759756
yield return LoadSceneOnly();
760-
_root!.Q<TextField>(SampleAppUi.Setup.PublishableKey).value = "pk_imapik-fakeprod-zzzz";
757+
_root!.Q<TextField>(SampleAppUi.Setup.PublishableKey).value = _key;
758+
_root!.Q<TextField>(SampleAppUi.Setup.BaseUrl).value = SampleAppUi.ExplicitBaseUrl;
761759
yield return null;
762760

763-
Assert.IsFalse(_root.Q<Label>(SampleAppUi.ProdWarning).ClassListContains(SampleAppUi.HiddenClass),
764-
"prod-warning should be visible when the publishable-key looks like a prod key");
761+
Assert.IsTrue(_root.Q<Label>(SampleAppUi.ProdWarning).ClassListContains(SampleAppUi.HiddenClass),
762+
"prod-warning should be hidden when a BaseUrl override is set");
765763
}
766764
}
767765
}

examples/audience/Assets/SampleApp/Tests/Runtime/SampleAppUi.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,18 @@ internal static class SampleAppUi
1919
// Scene asset name registered in EditorBuildSettings.
2020
internal const string SceneName = "SampleApp";
2121

22-
// The env var that carries the sandbox publishable key into the
23-
// built player at test time. Test runs inject it on the Unity CLI;
24-
// CI wires it from the AUDIENCE_TEST_PUBLISHABLE_KEY repo secret.
22+
// The env var that carries the publishable key into the built player at
23+
// test time. Test runs inject it on the Unity CLI; CI wires it from
24+
// the AUDIENCE_TEST_PUBLISHABLE_KEY repo secret.
2525
internal const string EnvKey = "AUDIENCE_TEST_PUBLISHABLE_KEY";
2626

2727
// Mirrors AudiencePaths.RootDirName — the SDK persists consent /
2828
// identity / queue under <persistentDataPath>/imtbl_audience. SetUp
2929
// wipes this between tests so on-disk state can't leak.
3030
internal const string SdkPersistedDirName = "imtbl_audience";
3131

32-
// Mirrors Constants.SandboxBaseUrl — used by the BaseUrl-override test.
33-
internal const string SandboxBaseUrl = "https://api.sandbox.immutable.com";
32+
// Used by the BaseUrl-override test to exercise the explicit-target code path.
33+
internal const string ExplicitBaseUrl = "https://api.immutable.com";
3434

3535
// ---- UXML element names ----
3636
// All names verified against examples/audience/Assets/SampleApp/Resources/AudienceSample.uxml.
@@ -41,6 +41,7 @@ internal static class Setup
4141
internal const string InitialConsent = "initial-consent";
4242
internal const string BaseUrl = "base-url";
4343
internal const string Debug = "debug";
44+
internal const string TestMode = "test-mode";
4445
internal const string FlushInterval = "flush-interval";
4546
internal const string FlushSize = "flush-size";
4647
}

0 commit comments

Comments
 (0)