Files
SVSimServer/SVSim.UnitTests/Services/DeckCodeServiceTests.cs
gamer147 5aac24d2b9 feat(deck-builder): /deck_code mint + /deck resolve with 3-min in-memory TTL
Adds the portal pair (shadowverse-portal.com deck-builder endpoints) as
anonymous routes on the app server. The translation middleware learns a new
[NoWireEncryption] attribute that skips both AES calls but keeps the rest of
the msgpack + base64 + envelope pipeline intact, matching prod's portal wire
profile observed in data_dumps/traffic_prod_deckcode.ndjson.

Storage is a 3-minute IMemoryCache — codes are anonymous-global, 4-char
lowercase alphanumeric (matches the shortest prod sample). Foil bit is
stripped on mint to match prod's normalize-on-encode behaviour.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 09:11:21 -04:00

57 lines
1.7 KiB
C#

using Microsoft.Extensions.Caching.Memory;
using SVSim.EmulatedEntrypoint.Models.Dtos.Responses.DeckBuilder;
using SVSim.EmulatedEntrypoint.Services;
namespace SVSim.UnitTests.Services;
public class DeckCodeServiceTests
{
private static DeckCodeService NewService(out IMemoryCache cache, IRandom? random = null)
{
cache = new MemoryCache(new MemoryCacheOptions());
return new DeckCodeService(cache, random ?? new SystemRandom());
}
[Test]
public void Mint_returns_4char_lowercase_alphanumeric_code()
{
var svc = NewService(out _);
var code = svc.Mint(new DeckPayload { Clan = "1", CardID = new() { 100211010 } });
Assert.That(code, Has.Length.EqualTo(4));
Assert.That(code, Does.Match("^[a-z0-9]+$"));
}
[Test]
public void Resolve_returns_payload_when_code_unexpired()
{
var svc = NewService(out _);
var original = new DeckPayload { Clan = "4", CardID = new() { 100414020, 100414020 } };
var code = svc.Mint(original);
var resolved = svc.TryResolve(code);
Assert.That(resolved, Is.SameAs(original));
}
[Test]
public void Resolve_returns_null_for_unknown_code()
{
var svc = NewService(out _);
Assert.That(svc.TryResolve("nope"), Is.Null);
}
[Test]
public void Resolve_returns_null_after_cache_eviction()
{
// Don't sleep for the 3-minute TTL — drop the entry directly to simulate expiry.
var svc = NewService(out var cache);
var code = svc.Mint(new DeckPayload { Clan = "1", CardID = new() { 100211010 } });
cache.Remove(DeckCodeService.CacheKey(code));
Assert.That(svc.TryResolve(code), Is.Null);
}
}