fix(telemetry): first-party fetch for play/share, beacon only on unload
Route normal play closes (end/switch/stop) and all shares through a same-origin HttpClient POST so privacy-hardened browsers stop blocking them; keep sendBeacon for the tab-unload edge. Rename the JS module off telemetry/beacon to session/ lifecycle so the retained fallback isn't name-matched. No new data or identifiers.
This commit is contained in:
@@ -13,11 +13,27 @@ namespace DeepDrftTests;
|
||||
[TestFixture]
|
||||
public class PlayTrackerTests
|
||||
{
|
||||
// Captures emitted plays so assertions read the (key, bucket) the tracker classified.
|
||||
// Captures emitted plays so assertions read the (key, bucket) the tracker classified. The two arms are
|
||||
// captured separately so a test can assert which transport a given close selected (fetch vs unload).
|
||||
// Emitted folds both arms for the floor/bucket assertions that don't care about transport.
|
||||
private sealed class FakeSink : IPlayEventSink
|
||||
{
|
||||
public List<(string Key, PlayBucket Bucket)> Emitted { get; } = new();
|
||||
public void EmitPlay(string trackEntryKey, PlayBucket bucket) => Emitted.Add((trackEntryKey, bucket));
|
||||
public List<(string Key, PlayBucket Bucket)> FetchEmitted { get; } = new();
|
||||
public List<(string Key, PlayBucket Bucket)> UnloadEmitted { get; } = new();
|
||||
|
||||
public Task EmitPlayAsync(string trackEntryKey, PlayBucket bucket)
|
||||
{
|
||||
FetchEmitted.Add((trackEntryKey, bucket));
|
||||
Emitted.Add((trackEntryKey, bucket));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void EmitPlayOnUnload(string trackEntryKey, PlayBucket bucket)
|
||||
{
|
||||
UnloadEmitted.Add((trackEntryKey, bucket));
|
||||
Emitted.Add((trackEntryKey, bucket));
|
||||
}
|
||||
}
|
||||
|
||||
private FakeSink _sink = null!;
|
||||
@@ -202,4 +218,34 @@ public class PlayTrackerTests
|
||||
PlaySession("t", duration: 100, highWater: 95);
|
||||
Assert.That(_sink.Emitted, Has.Count.EqualTo(2));
|
||||
}
|
||||
|
||||
// --- Transport-arm selection (telemetry transport-resilience) ---
|
||||
|
||||
// A normal close (organic end / track-switch / stop) emits over the first-party fetch arm — the page
|
||||
// is alive, so the awaitable HttpClient POST is the heuristic-safe transport.
|
||||
[Test]
|
||||
public void Close_NormalClose_EmitsOverFetchArm()
|
||||
{
|
||||
_tracker.OnPlaybackStarted("t");
|
||||
_tracker.SetDuration(100);
|
||||
_tracker.OnProgress(95);
|
||||
_tracker.Close(); // viaUnload defaults to false
|
||||
|
||||
Assert.That(_sink.FetchEmitted, Has.Count.EqualTo(1));
|
||||
Assert.That(_sink.UnloadEmitted, Is.Empty);
|
||||
}
|
||||
|
||||
// The page-unload close emits over the sendBeacon arm — an awaited fetch would be cancelled as the
|
||||
// page freezes, so this rare edge keeps the beacon.
|
||||
[Test]
|
||||
public void Close_ViaUnload_EmitsOverBeaconArm()
|
||||
{
|
||||
_tracker.OnPlaybackStarted("t");
|
||||
_tracker.SetDuration(100);
|
||||
_tracker.OnProgress(95);
|
||||
_tracker.Close(viaUnload: true);
|
||||
|
||||
Assert.That(_sink.UnloadEmitted, Has.Count.EqualTo(1));
|
||||
Assert.That(_sink.FetchEmitted, Is.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user