Skip to content

Commit cc7449e

Browse files
renemadsenclaude
andcommitted
test: add unit tests for push notification infrastructure
Tests cover DeviceTokenService (register/upsert/unregister), PushNotificationService (no-op when Firebase unconfigured), and integration verification that push triggers fire after absence/handover create/approve/reject. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cf4cc18 commit cc7449e

File tree

4 files changed

+602
-0
lines changed

4 files changed

+602
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System.Linq;
2+
using System.Threading.Tasks;
3+
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.Extensions.Logging;
5+
using NSubstitute;
6+
using NUnit.Framework;
7+
using TimePlanning.Pn.Infrastructure.Models.DeviceToken;
8+
using TimePlanning.Pn.Services.DeviceTokenService;
9+
10+
namespace TimePlanning.Pn.Test;
11+
12+
[TestFixture]
13+
public class DeviceTokenServiceTests
14+
{
15+
private DeviceTokenDbContext _dbContext = null!;
16+
private DeviceTokenService _service = null!;
17+
18+
[SetUp]
19+
public void SetUp()
20+
{
21+
var options = new DbContextOptionsBuilder<DeviceTokenDbContext>()
22+
.UseInMemoryDatabase(databaseName: $"DeviceTokenTestDb_{TestContext.CurrentContext.Test.Name}")
23+
.Options;
24+
_dbContext = new DeviceTokenDbContext(options);
25+
_service = new DeviceTokenService(
26+
_dbContext,
27+
Substitute.For<ILogger<DeviceTokenService>>());
28+
}
29+
30+
[TearDown]
31+
public void TearDown()
32+
{
33+
_dbContext.Database.EnsureDeleted();
34+
_dbContext.Dispose();
35+
}
36+
37+
[Test]
38+
public async Task RegisterAsync_NewToken_IsStored()
39+
{
40+
// Act
41+
var result = await _service.RegisterAsync(42, "fcm-token-abc", "android");
42+
43+
// Assert
44+
Assert.That(result.Success, Is.True);
45+
46+
var stored = await _dbContext.DeviceTokens.SingleAsync();
47+
Assert.That(stored.SdkSiteId, Is.EqualTo(42));
48+
Assert.That(stored.Token, Is.EqualTo("fcm-token-abc"));
49+
Assert.That(stored.Platform, Is.EqualTo("android"));
50+
Assert.That(stored.WorkflowState, Is.EqualTo("created"));
51+
}
52+
53+
[Test]
54+
public async Task RegisterAsync_SameTokenTwice_UpsertsWithoutDuplicate()
55+
{
56+
// Arrange
57+
await _service.RegisterAsync(1, "dup-token", "android");
58+
59+
// Act — re-register same token with different site
60+
var result = await _service.RegisterAsync(2, "dup-token", "ios");
61+
62+
// Assert
63+
Assert.That(result.Success, Is.True);
64+
Assert.That(await _dbContext.DeviceTokens.CountAsync(), Is.EqualTo(1));
65+
66+
var stored = await _dbContext.DeviceTokens.SingleAsync();
67+
Assert.That(stored.SdkSiteId, Is.EqualTo(2));
68+
Assert.That(stored.Platform, Is.EqualTo("ios"));
69+
}
70+
71+
[Test]
72+
public async Task UnregisterAsync_ExistingToken_IsRemoved()
73+
{
74+
// Arrange
75+
await _service.RegisterAsync(1, "remove-me", "android");
76+
Assert.That(await _dbContext.DeviceTokens.CountAsync(), Is.EqualTo(1));
77+
78+
// Act
79+
var result = await _service.UnregisterAsync("remove-me");
80+
81+
// Assert
82+
Assert.That(result.Success, Is.True);
83+
Assert.That(await _dbContext.DeviceTokens.CountAsync(), Is.EqualTo(0));
84+
}
85+
86+
[Test]
87+
public async Task UnregisterAsync_NonExistentToken_SucceedsWithoutError()
88+
{
89+
// Act
90+
var result = await _service.UnregisterAsync("does-not-exist");
91+
92+
// Assert
93+
Assert.That(result.Success, Is.True);
94+
}
95+
96+
[Test]
97+
public async Task RegisterAsync_EmptyToken_FailsGracefully()
98+
{
99+
// The DB has a [Required] Token column. With InMemory provider the constraint
100+
// isn't enforced at the DB level, but we still verify the service doesn't crash
101+
// and that an empty-string token is handled.
102+
var result = await _service.RegisterAsync(1, "", "android");
103+
104+
// The service doesn't explicitly validate empty tokens — it succeeds but stores
105+
// an empty string. This test documents the current behaviour; a future guard
106+
// could make this return Success=false.
107+
Assert.That(result.Success, Is.True);
108+
}
109+
}

0 commit comments

Comments
 (0)