using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Flaro.Api.Endpoints;
namespace Flaro.Api.Tests;
public class SignInTests
{
[Test]
public async Task Mit_richtigem_Passwort_gibt_es_ein_Token()
{
using var server = new FlaroServer();
var anmeldung = await server.SetUpAndSignIn();
Assert.That(anmeldung.Token, Is.Not.Empty);
Assert.That(anmeldung.User.UserName, Is.EqualTo("guido"));
}
[Test]
public async Task Mit_falschem_Passwort_gibt_es_keines()
{
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var antwort = await server.Client.PostAsJsonAsync("/api/v1/auth/login",
new LoginRequest("guido", "falsch-aber-lang", "iPhone", "geraet-1"));
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}
/// Unbekannter Name und falsches Passwort sind nicht zu unterscheiden.
[Test]
public async Task Ein_unbekannter_Name_antwortet_wie_ein_falsches_Passwort()
{
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var unbekannt = await server.Client.PostAsJsonAsync("/api/v1/auth/login",
new LoginRequest("gibtesnicht", "irgendein-passwort", "iPhone", "geraet-1"));
var falsch = await server.Client.PostAsJsonAsync("/api/v1/auth/login",
new LoginRequest("guido", "irgendein-passwort", "iPhone", "geraet-1"));
Assert.That(unbekannt.StatusCode, Is.EqualTo(falsch.StatusCode));
// Nur die Begründung vergleichen, nicht den ganzen Körper: darin steht
// je Anfrage eine neue Ablaufkennung, die nichts über das Konto sagt.
static async Task Begründung(HttpResponseMessage antwort) =>
(await antwort.Content.ReadFromJsonAsync())!
.RootElement.GetProperty("detail").GetString();
Assert.That(await Begründung(unbekannt), Is.EqualTo(await Begründung(falsch)));
}
[Test]
public async Task Mit_Token_beantwortet_der_Server_wer_man_ist()
{
using var server = new FlaroServer();
var anmeldung = await server.SetUpAndSignIn();
var ich = await server.AlsAngemeldeter(anmeldung.Token)
.GetFromJsonAsync("/api/v1/auth/me");
Assert.That(ich!.Id, Is.EqualTo(anmeldung.User.Id));
Assert.That(ich.IsAdministrator, Is.True);
}
[Test]
public async Task Ohne_Token_antwortet_er_nicht()
{
using var server = new FlaroServer();
await server.SetUpAndSignIn();
// **Ohne Gedächtnis, weil der Client nach der Anmeldung ein Cookie
// hält.** Sonst prüfte dieser Test nicht den Unangemeldeten, sondern
// den Angemeldeten — und bestünde nie wieder.
var antwort = await server.OhneGedaechtnis().GetAsync("/api/v1/auth/me");
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}
[Test]
public async Task Ein_erfundenes_Token_gilt_nicht()
{
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var antwort = await server.AlsAngemeldeter("ausgedacht-aber-lang-genug").GetAsync("/api/v1/auth/me");
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}
/// Nach dem Abmelden gilt das Token sofort nicht mehr.
///
///
/// Der Grund, warum hier keine JWT stehen: ein signiertes Token gälte bis
/// zum Ablauf weiter, und für ein verlorenes Telefon ist das der falsche
/// Satz.
///
[Test]
public async Task Abmelden_entwertet_das_Token_augenblicklich()
{
using var server = new FlaroServer();
var anmeldung = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(anmeldung.Token);
(await client.PostAsync("/api/v1/auth/logout", null)).EnsureSuccessStatusCode();
var danach = await client.GetAsync("/api/v1/auth/me");
Assert.That(danach.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}
/// Dasselbe Gerät ersetzt seinen Eintrag, statt einen zweiten anzulegen.
[Test]
public async Task Eine_erneute_Anmeldung_desselben_Geraets_entwertet_das_alte_Token()
{
using var server = new FlaroServer();
var erste = await server.SetUpAndSignIn();
var zweite = await server.SignIn("guido", "ein-langes-passwort", "iPhone", "geraet-1");
Assert.That(zweite.Token, Is.Not.EqualTo(erste.Token));
var mitAltem = await server.AlsAngemeldeter(erste.Token).GetAsync("/api/v1/auth/me");
Assert.That(mitAltem.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
var mitNeuem = await server.AlsAngemeldeter(zweite.Token).GetAsync("/api/v1/auth/me");
Assert.That(mitNeuem.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
/// Ein zweites Gerät meldet das erste nicht ab.
[Test]
public async Task Ein_anderes_Geraet_laesst_das_erste_in_Ruhe()
{
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var fernseher = await server.SignIn("guido", "ein-langes-passwort", "Apple TV", "geraet-2");
foreach (var token in new[] { telefon.Token, fernseher.Token })
{
var antwort = await server.AlsAngemeldeter(token).GetAsync("/api/v1/auth/me");
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
}
}