Skip to content

refactor(audience): SDK test pinning and sample-app custom events (SDK-277) - #727

Closed
ImmutableJeffrey wants to merge 24 commits into
chore/sdk-272-pr-2-catalogues-and-test-infrafrom
chore/sdk-272-pr-3-test-pinning-and-sample-events
Closed

refactor(audience): SDK test pinning and sample-app custom events (SDK-277)#727
ImmutableJeffrey wants to merge 24 commits into
chore/sdk-272-pr-2-catalogues-and-test-infrafrom
chore/sdk-272-pr-3-test-pinning-and-sample-events

Conversation

@ImmutableJeffrey

Copy link
Copy Markdown
Contributor

Summary

  • Pins Constants.LibraryName against the name field in package.json.
  • Pins backoff assertions in transport tests against Constants.HttpBackoff*Ms instead of magic numbers.
  • Covers IdentityTypeExtensions.ParseLowercaseString cases (known platform, unknown, null, empty, mixed case fallback to Custom).
  • Parametrises DistributionPlatform casing tests across all platform values.
  • Pins AudienceErrorMessages and AudienceArgumentMessages constants and formatters to their exact wording so a reword fails the build.
  • Adds Unity-only DeviceCollectorTests and SampleAppUxmlAlignmentTests drafts; both gated by UNITY_INCLUDE_TESTS and excluded from the headless dotnet build via Audience.Tests.csproj.
  • Adds TestEventNames for per-test event-name strings.
  • Adds SampleAppCustomEvents and SampleAppCustomEventPropertyKeys for the sample-app demo catalogue's custom event names and property keys.
  • Routes typed-event field lookups in SampleAppLiveFireTests through EventPropertyKeys.
  • No behaviour change. Wire format and OnError callback contract unchanged.

Linear: SDK-277

@ImmutableJeffrey
ImmutableJeffrey requested review from a team as code owners May 3, 2026 07:17
@ImmutableJeffrey
ImmutableJeffrey force-pushed the chore/sdk-272-pr-2-catalogues-and-test-infra branch from 1d1a56d to 9c7496b Compare May 3, 2026 07:40
@ImmutableJeffrey
ImmutableJeffrey force-pushed the chore/sdk-272-pr-3-test-pinning-and-sample-events branch 2 times, most recently from 7ccd158 to 9c4da2e Compare May 3, 2026 07:43
@ImmutableJeffrey
ImmutableJeffrey force-pushed the chore/sdk-272-pr-2-catalogues-and-test-infra branch from 9c7496b to 91f1783 Compare May 3, 2026 07:53
@ImmutableJeffrey
ImmutableJeffrey force-pushed the chore/sdk-272-pr-3-test-pinning-and-sample-events branch from 9c4da2e to ba0bcf4 Compare May 3, 2026 07:54
ConstantsTests already pinned Constants.LibraryVersion against the
package.json "version" field so the two cannot silently drift; the
matching pin for Constants.LibraryName ("com.immutable.audience") and
package.json "name" was missing. Both feed context.library /
context.libraryVersion on every outgoing event, so a rename of either
side without the other would silently miscategorise events on the
backend.

Add a sibling LibraryName_MatchesPackageJson test that walks up to
the same package.json and asserts the two strings match. Reuses the
existing ReadPackageJson helper.

Follow-up to SDK-272 (centralisation of duplicated literals).
…off*Ms

HttpTransportTests pinned the backoff schedule with literal millisecond
numbers (5_000 / 10_000 / 20_000 / 40_000 / 60_000). Constants.HttpBackoff*Ms
now own those values, but the tests still hardcoded the numbers, so a
change to the constants would have flipped production behaviour while
the tests kept passing against the old expectations.

Switch every BackoffMs / NextAttemptAt assertion to derive from
Constants.HttpBackoff{1st,2nd,3rd,4th,Cap}Ms.

Follow-up to SDK-272 (centralisation of duplicated literals).
ParseLowercaseString was added in the SDK-272 stack as the inverse of
ToLowercaseString and consumed by the sample app to map wire strings
back to the enum. It had no direct test, so a typo in any of the eight
case branches or in the Custom fallback would land silently.

Add three parametrised cases:
- Each known enum value maps from its lowercase wire form.
- Mixed-case ("Steam", "STEAM", "Passport") still resolves
  via ToLowerInvariant, matching the documented behaviour.
- null, empty, and unknown values fall back to Custom (the parser
  never throws so producer code always gets a usable enum).

Follow-up to SDK-272 (centralisation of duplicated literals).
…l platforms

Init's lowercase-normalisation tests covered "Steam" / "STEAM" / "steam"
but not the other four public DistributionPlatforms values (Epic, GOG,
Itch, Standalone). Adding a sixth platform would land without coverage.

Add a parametrised Init_LowercasesDistributionPlatform_AcrossAllPublicValues
test that takes each public DistributionPlatforms constant, uppercases
it, runs Init, and asserts the canonical lowercase form is restored.

Follow-up to SDK-272 (centralisation of duplicated literals).
…sages

The two centralised message catalogues had no direct unit tests.
Behavioural tests observed wording loosely via Does.Contain assertions,
which let typos and partial rewords through.

Add MessagesTests.cs with one fixture per catalogue. Each constant /
formatter has an exact-string assertion. A reword anywhere in the
catalogue now fails the build.

Follow-up to SDK-272 (centralisation of duplicated literals).
@ImmutableJeffrey
ImmutableJeffrey force-pushed the chore/sdk-272-pr-2-catalogues-and-test-infra branch from 91f1783 to 427f963 Compare May 3, 2026 07:58
@ImmutableJeffrey
ImmutableJeffrey force-pushed the chore/sdk-272-pr-3-test-pinning-and-sample-events branch 4 times, most recently from cf5c443 to ea260bf Compare May 3, 2026 19:06
…ent drafts

Two Unity-only test fixtures the dotnet test runner cannot reach (DeviceCollector depends on
UnityEngine.SystemInfo / Application; SampleAppUxml needs Unity test framework gating). Both run
under the Unity Test Framework once the project is opened in the editor.

DeviceCollectorTests (src/Packages/Audience/Tests/Editor/, excluded from the headless dotnet build
by Audience.Tests.csproj's Compile Remove="Editor/**/*.cs" rule) pin DeviceCollector's emitted key
sets against GameLaunchPropertyKeys and ContextKeys, assert that no unknown keys leak in either
direction, and verify every string-typed value is capped at Constants.MaxFieldLength.

SampleAppUxmlAlignmentTests (examples/audience/Assets/SampleApp/Tests/ Runtime/, gated by the
existing UNITY_INCLUDE_TESTS define on the SampleApp.Tests asmdef) reads
Resources/AudienceSample.uxml as XML and asserts every SampleAppUi name and Css constant that is
slug-shaped (lowercase / dashes / no spaces) appears as a name= or class= attribute somewhere in the
markup. Runtime-only CSS toggles (state-warn, copied, narrow, has-value, etc.) are filtered by the
slug-shape heuristic so the test only flags constants that look like they should map directly to
UXML.

Both files compile cleanly. They will not run under dotnet test. Run them via Unity Test Runner.

Follow-up to SDK-272 (centralisation of duplicated literals).
Per the user's "everything random goes in a constant" stance, this is
applied against the previous session's recommendation that scenario
descriptors read better inline. Recording the override on the user's
explicit request.

Follow-up to SDK-272 (centralisation of duplicated literals).
…leAppCustomEvents

The sample-app demo catalogue (sign_up, sign_in, email_acquired,
wishlist_add, wishlist_remove, game_page_viewed, link_clicked,
screen_viewed) had its event-name strings inline at every call site.
A rename touches the catalogue, the screen_viewed Track call, and
each Unity live-fire test that drives the catalogue UI.

Move the eight names into a new internal SampleAppCustomEvents class
under the sample-app Scripts assembly. Update AudienceSample.Events.cs,
AudienceSample.cs (screen_viewed Track), and SampleAppLiveFireTests.cs
(button and field lookups by event name) to reference the constants.

Per the user's "everything random goes in a constant" stance,
applied against the previous session's recommendation that the demo
content read better inline. Recording the override on the user's
explicit request.

Follow-up to SDK-272 (centralisation of duplicated literals).
…mpleAppCustomEventPropertyKeys

The sample-app demo catalogue, screen_viewed Track call, and the
typed-event live-fire tests had property keys (method, source,
gameId, platform, gameName, slug, url, label, path) inline at every
call site. Each key duplicates between the catalogue's EventField
definition and the live-fire test's UI lookup.

Add SampleAppCustomEventPropertyKeys alongside SampleAppCustomEvents
(mirrors the SDK's EventPropertyKeys naming) and reference the
constants from AudienceSample.Events.cs, AudienceSample.cs (the
screen_viewed props dictionary), and SampleAppLiveFireTests.cs (the
TypedEventField field-name lookups).

Includes gameName and slug beyond the user's listed seven keys: same
inline-property-key category, same migration treatment.

Per the user's "everything random goes in a constant" stance,
applied against the previous session's recommendation that the demo
content read better inline. Recording the override on the user's
explicit request.

Follow-up to SDK-272 (centralisation of duplicated literals).
…xtures

The SDK test suite repeated the same identifier and payload literals
across MessageBuilderTests, TypedEventTests, JsonTests,
ImmutableAudienceTests, and DeleteDataTests. The pairs that appeared
both as inputs and as assertion targets were the worst offenders
(anon-42, user-42, user-99, from-id, to-id) since a typo flipping
one side would silently pass on the other.

Add TestFixtures alongside TestDefaults (which already centralises
config defaults) and reference TestFixtures.X across the five files.

Constants cover anonymous IDs (1, 42, 123), user IDs (42, 99), alias
endpoints (from / to), identity values keyed to IdentityType.Custom /
Steam / Passport (player-42 / player_steam / player_passport),
Resource event payload values (gold currency, quest_reward itemType,
main_quest_01 itemId), Purchase event payload values (gem_pack_01
itemId, Starter Gem Pack itemName, txn_abc123 transactionId), and
the MilestoneReached name (first_boss_defeated).

Includes main_quest_01, player_passport, anon-123, and
first_boss_defeated beyond the user's listed values: same per-fixture
data category, same migration treatment. tutorial_complete from the
user's list does not appear in the test suite (the milestone fixture
uses first_boss_defeated); skipped on that basis.

Per the user's "everything random goes in a constant" stance,
applied against the previous session's recommendation that scenario
descriptors read better inline. Recording the override on the user's
explicit request.

Follow-up to SDK-272 (centralisation of duplicated literals).
TestEventNames.cs (SHA 6d237a3a) was committed without its .meta file.
Unity generates one on project open, but the convention across the
test folder is to commit them so the GUID is stable across machines
(mirrors TestDefaults.cs.meta and TestFixtures.cs.meta).

Follow-up to SDK-272 (centralisation of duplicated literals).
…enarios

Adds named event-name constants for the EventQueue scenario tests
(IntervalFlush, DisposeTest), placeholder constants for transport /
queue scaffolding where the event name is just filler (PlaceholderA,
PlaceholderB, PlaceholderTest, PlaceholderTrack, PlaceholderIgnored,
PlaceholderEvt), and BlockedPrefix for the OfflineResilienceTests
$"blocked_{i}" templated string.

Migrates inline references in:
- MessageBuilderTests ("evt" placeholders, six sites)
- EventQueueTests (Msg() helper calls, six sites)
- HttpTransportTests (WireFixture.Track event-name args, eight sites)
- OfflineResilienceTests ($"blocked_{i}" templated literal, three sites)

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
…d lookups in live-fire tests

SampleAppLiveFireTests filled the Resource, Purchase, and
MilestoneReached typed-event forms by looking up TextFields with
inline property-name strings ("currency", "amount", "value", "name").
The SDK already centralises these as EventPropertyKeys.Currency /
.Amount / .Value / .Name, made visible to the sample-app tests
through the existing InternalsVisibleTo grant.

Reference EventPropertyKeys.X from the five lookup sites instead of
re-typing the wire-format strings.

Discovered during the sample-app demo property-key centralisation
pass; kept separate because these keys belong to the SDK's typed-event
surface, not the sample-app demo catalogue.

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
Adds:
- Track properties: CustomPropKeyRecipeId, CraftingRecipeIronSword
- Distribution platform fixture: ProviderValue
- IdentityType fallback fixture: UnknownProvider
- Identity persistence fixtures: PreExistingId, PreExistingIdFromLastLaunch
- ConsentStore corruption fixture: NotAnInt
- Stress test userId: UserRaceStress
- DeleteData generic userId: SomeUser
- Templated prefix for GzipTests $"anon-{i}": AnonIdPrefix
- Minimal placeholders for MessageBuilder Identify / Alias arg slots
  (GenericUserId, GenericFromId, GenericToId, GenericFromType,
  GenericToType)

Migrates inline references in:
- DeleteDataTests
- ImmutableAudienceTests (Track props, race-stress userId, provider value)
- Core/IdentityTests (pre-existing-id fixtures)
- IdentityTypeTests (TestCase attribute now references the const)
- Core/ConsentStoreTests (corrupt-file fixture)
- Utility/GzipTests (anon-{i} templated prefix)
- Events/MessageBuilderTests (Identify / Alias placeholder slots)

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
Adds SampleAppLiveFireFixtures alongside SampleAppLiveFireTests with
three constants (MilestoneSmokeName, GameId, LinkUrl) shared across
five inline call sites in the live-fire batch. Retargeting the smoke
suite (e.g. switching the game id slug) is now a one-line change
instead of a five-line touch.

Migrates inline references in SampleAppLiveFireTests for the
typed-event Resource / Purchase / MilestoneReached fills and the
sample-app custom catalogue fills (WishlistAdd, WishlistRemove,
GamePageViewed, LinkClicked).

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
…AppUi.LogPayloadKeys

The sample-app builds Dictionary<string, object> echo payloads for
each RunAndLog "Ok" row (Track outcomes, Identify / Alias outcomes,
OnError rows, Init config echo). Each key was inline at the
construction site, so a rename (e.g. "publishableKey" to "key")
would touch six call sites today and grow as the demo expands.

Adds a LogPayloadKeys sub-class to SampleAppUi with eighteen
constants covering the existing payload schema (Event, Overload,
Effects, Id, Accepted, From, To, Code, Message, Consent, Debug,
FlushIntervalSeconds, FlushSize, PackageVersion,
ShutdownFlushTimeoutMs, PublishableKey, PersistentDataPath) plus a
nested OverloadValues for the "typed" / "string" enum-like values
written under LogPayloadKeys.Overload.

Migrates AudienceSample.cs to reference the constants. UXML element
names (in the existing SampleAppUi sub-classes) are unchanged.

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
…Messages

AudienceSample.cs had inline status strings at every RunAndLog "Ok"
return and AppendLog call (SDK stopped, anonymous ID regenerated
queue cleared, queue flushed, erasure request dispatched, backend
acknowledged, plus two interpolated messages for track-dropped and
flushInterval-clamped warnings).

Adds five plain-string constants and two format-string constants
(suffixed Fmt, used with string.Format) to the existing
SampleAppUi.Messages catalogue alongside the consent-flow messages
already centralised there. Migrates seven inline references in
AudienceSample.cs.

The two interpolated status strings used em-dashes; replaced with
commas in the centralised form to match the project glyph rule.
NoActiveIdentity (already in Messages) had the same em-dash
artefact and gets the same comma replacement.

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
…fixtures

HttpTransportTests had inline literals for the malformed-body
diagnostic ("not-json"), the empty JSON object body ("{}"), and the
exception messages thrown by MockHandler factories ("connection
refused", "Request timed out", "simulated").

Adds five file-local consts (MalformedResponseBody, EmptyJsonObjectBody,
ConnectionRefusedMessage, RequestTimedOutMessage,
SimulatedCancellationMessage) at the top of the fixture and migrates
six call sites. The empty body "" is left inline since "the body is
truly empty" carries the meaning at the call site.

NUnit assertions never check the exception messages, but pinning the
"what failure was simulated" intent keeps the fixture's catalogue of
network failure modes visible in one place.

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
…CSS classes

AudienceSample.UI.cs RefreshStatusBar set status-cell classes via
inline strings ("state-warn", "state-ok", and "dim" five times) even
though SampleAppUi.Css.StateWarn / .StateOk / .Dim already exist for
exactly this purpose.

Migrates seven call sites to the constants. CSS-side renames now
need to touch one place (Css.Dim) instead of grep-and-replace across
the SetStatusCell call sites.

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
…AppUi.ButtonText.CopiedFlash

AudienceSample.UI.cs:368 set the click-to-copy flash via inline
"Copied!". Distinct from the existing SampleAppUi.ButtonText.Copied
("Copied", no exclamation) used as the persistent button label after
copying. Both strings can drift independently today.

Adds ButtonText.CopiedFlash with a comment noting the distinction
and migrates the inline reference. The two consts now sit next to
each other so a future translation pass would catch both.

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
JsonTests and JsonReaderTests both used the same literals to verify
round-trip behaviour: each input dictionary in JsonTests had a
matching encoded form in JsonReaderTests, and vice versa. The
single string pair (key / hello), the all-escapes string (val /
"say \"hi\"\nback\\slash\ttab"), and the nested object (outer /
inner / value) appeared duplicated in both files.

Adds JsonRoundTripFixtures with the raw key, raw value, and
exact-encoded form for each of the three pairs. Migrates six
references across the two files.

Renaming any fixture (e.g. swapping "hello" for "world") now needs
one edit and both tests stay coherent.

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
…enario values

Adds:
- Currency code fixtures: UsdCurrency ("USD"), EurCurrency ("EUR"),
  used across Purchase / Resource typed-event tests
- Progression world fixture: ProgressionWorldTutorial ("tutorial"),
  three uses across TypedEventTests and ImmutableAudienceTests
- Exception fixture: ContextProviderBoomMessage ("boom"),
  for the ContextProvider sabotage test
- Disk-block sentinel: DiskBlockerContent ("blocker"),
  written to the queue directory path so creation fails
- DistributionPlatform mixed-case fixtures:
  DistributionPlatformSteamCased ("Steam"),
  DistributionPlatformSteamUppercase ("STEAM"),
  for Init's lowercase-normalisation test
- Application.platform fixture: PlatformWindows ("WindowsPlayer"),
  for the GameLaunch.Platform test
- Generic alias endpoint fixtures: GenericAliasFromId ("fromId"),
  GenericAliasToId ("toId"), GenericAliasFromShort ("from"),
  for Alias argument-validation tests where the value is filler

Migrates inline references in:
- Events/TypedEventTests (seven Purchase / Resource currency and
  Progression world fills)
- ImmutableAudienceTests (boom, USD x2, fromId/toId, from, tutorial,
  Steam, STEAM, WindowsPlayer)
- OfflineResilienceTests (blocker)

IdentityTypeTests "Steam" / "STEAM" left inline; the wire-format
casing fixtures stay self-contained inside that test until the
IdentityType wire-format extraction lands. MessagesTests "USD" /
"usd" left inline; the literals there appear inside expected error
strings being verified, so centralising would defeat the test's
own pinning.

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
…s file-local consts

ConstantsTests had inline literals for the publishable-key fixtures
the BaseUrl resolution tests pass through Constants.BaseUrl
("pk_imapik-test-abc", "pk_imapik-prod-abc"), the custom override
URL ("https://api.dev.immutable.com"), and the package.json
discovery-walk components ("src", "Packages", "Audience",
"package.json", and the JSON field names "version" / "name").

Adds nine file-local consts at the top of the fixture (matching
the PublishableKeyPrefixTests TestPrefixKey / NonTestKey precedent)
and migrates eleven references across the BaseUrl tests, the
package.json LibraryVersion / LibraryName tests, and the
ReadPackageJson upward-walk helper.

Per the user's "everything random goes in a constant" stance.

Follow-up to SDK-272 (centralisation of duplicated literals).
@ImmutableJeffrey
ImmutableJeffrey force-pushed the chore/sdk-272-pr-3-test-pinning-and-sample-events branch from ea260bf to 4bacd74 Compare May 3, 2026 19:15
…d-sample-polish

refactor(audience): test fixtures and sample-app polish (SDK-277)
@nattb8 nattb8 closed this Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants