using Halo.Core.Auth; namespace Halo.Core.Tests; public class SecretsTests { [Test] public void Zwei_Geheimnisse_sind_nie_gleich() { var gesehen = new HashSet(); for (var i = 0; i < 200; i++) Assert.That(gesehen.Add(Secrets.Create()), Is.True); } [Test] public void Das_Geheimnis_ist_URL_sicher() { for (var i = 0; i < 50; i++) { var s = Secrets.Create(); Assert.That(s, Does.Not.Contain("+").And.Not.Contain("/").And.Not.Contain("=")); } } [Test] public void Der_Fingerabdruck_ist_gleichbleibend_und_unterscheidet() { var a = Secrets.Create(); var b = Secrets.Create(); // Zweimal gerechnet, nicht zweimal derselbe Ausdruck: geprüft wird, // dass die Funktion bei gleicher Eingabe gleich antwortet. var ersterLauf = Secrets.Fingerprint(a); var zweiterLauf = Secrets.Fingerprint(a); Assert.That(zweiterLauf, Is.EqualTo(ersterLauf)); Assert.That(Secrets.Fingerprint(b), Is.Not.EqualTo(ersterLauf)); Assert.That(ersterLauf, Has.Length.EqualTo(64)); } /// Der Fingerabdruck darf das Geheimnis nicht enthalten. [Test] public void Der_Fingerabdruck_verraet_das_Geheimnis_nicht() { var s = Secrets.Create(); Assert.That(Secrets.Fingerprint(s), Does.Not.Contain(s)); } } public class AuthCodeTests { private static readonly DateTimeOffset Jetzt = new(2026, 10, 3, 12, 0, 0, TimeSpan.Zero); private static AuthCode Angelegt(DateTimeOffset? läuftAb = null) => new() { Code = AuthCode.NewCode(), HandleFingerprint = Secrets.Fingerprint(Secrets.Create()), DeviceName = "Wohnzimmer", DeviceId = "tv-1", CreatedAt = Jetzt, ExpiresAt = läuftAb ?? Jetzt + AuthCode.Lifetime, }; [Test] public void Der_Code_hat_immer_sechs_Ziffern() { for (var i = 0; i < 500; i++) { var code = AuthCode.NewCode(); Assert.That(code, Has.Length.EqualTo(6)); Assert.That(code, Does.Match("^[0-9]{6}$")); } } /// Führende Nullen dürfen nicht wegfallen — „000123" ist ein gültiger Code. [Test] public void Fuehrende_Nullen_bleiben_erhalten() { var mitNull = Enumerable.Range(0, 20_000).Select(_ => AuthCode.NewCode()).Any(c => c[0] == '0'); Assert.That(mitNull, Is.True, "In 20 000 Ziehungen kam kein Code mit führender Null vor"); } [Test] public void Ohne_Bestaetigung_gibt_es_kein_Token() { Assert.That(Angelegt().CanBeRedeemed(Jetzt), Is.False); } [Test] public void Nach_Bestaetigung_gibt_es_ein_Token() { var code = Angelegt(); code.ApprovedByUserId = Guid.CreateVersion7(); Assert.That(code.CanBeRedeemed(Jetzt), Is.True); } /// Einmal abgeholt ist abgeholt. [Test] public void Ein_verbrauchter_Code_gibt_kein_zweites_Token() { var code = Angelegt(); code.ApprovedByUserId = Guid.CreateVersion7(); code.ConsumedAt = Jetzt; Assert.That(code.CanBeRedeemed(Jetzt), Is.False); } [Test] public void Ein_abgelaufener_Code_gibt_kein_Token_auch_wenn_bestaetigt() { var code = Angelegt(läuftAb: Jetzt); code.ApprovedByUserId = Guid.CreateVersion7(); Assert.That(code.IsExpired(Jetzt), Is.True); Assert.That(code.CanBeRedeemed(Jetzt), Is.False); } [Test] public void Die_Gueltigkeit_ist_kurz() { Assert.That(AuthCode.Lifetime, Is.LessThanOrEqualTo(TimeSpan.FromMinutes(10))); } }