using System.Net;
using System.Net.Http.Json;
using Flaro.Api.Endpoints;
namespace Flaro.Api.Tests;
/// Anmelden am Fernseher, ohne ein Passwort zu tippen.
public class CodeSignInTests
{
private static Task CodeAnfordern(FlaroServer server) =>
server.Client.PostAsJsonAsync("/api/v1/auth/code", new CodeRequest("Wohnzimmer", "tv-1"));
[Test]
public async Task Der_ganze_Weg_vom_Bildschirm_zum_Token()
{
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
// 1. Der Fernseher fordert an und zeigt den Code.
var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync();
Assert.That(angefordert!.Code, Does.Match("^[0-9]{6}$"));
// 2. Noch hat niemand bestätigt.
var wartend = await server.Client.GetFromJsonAsync($"/api/v1/auth/code/{angefordert.Handle}");
Assert.That(wartend!.State, Is.EqualTo("pending"));
Assert.That(wartend.Login, Is.Null);
// 3. Am Telefon bestätigen.
var bestätigt = await server.AlsAngemeldeter(telefon.Token)
.PostAsync($"/api/v1/auth/code/{angefordert.Code}", null);
bestätigt.EnsureSuccessStatusCode();
// 4. Der Fernseher holt sein Token ab.
var fertig = await server.Client.GetFromJsonAsync($"/api/v1/auth/code/{angefordert.Handle}");
Assert.That(fertig!.State, Is.EqualTo("approved"));
Assert.That(fertig.Login!.Token, Is.Not.Empty);
// Und es gilt auch wirklich.
var ich = await server.AlsAngemeldeter(fertig.Login.Token)
.GetFromJsonAsync("/api/v1/auth/me");
Assert.That(ich!.UserName, Is.EqualTo("guido"));
}
/// Der Code allein darf niemals ein Token herausgeben.
///
///
/// Die wichtigste Prüfung dieser Datei. Gäbe der Server auf einen
/// erratenen Abholschein hin Token heraus, wären sechsstellige Codes ein
/// Türschloss mit einer Million Schlüsseln — in Minuten durchprobiert, und
/// am Ende stünde ein fremdes Gerät in der Mediathek.
///
[Test]
public async Task Ein_fremder_Abholschein_bekommt_nichts()
{
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var echt = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync();
await server.AlsAngemeldeter(telefon.Token).PostAsync($"/api/v1/auth/code/{echt!.Code}", null);
// Der Code ist bestätigt — aber der Abholschein stimmt nicht.
var erraten = await server.Client.GetFromJsonAsync("/api/v1/auth/code/ausgedacht");
Assert.That(erraten!.State, Is.EqualTo("expired"));
Assert.That(erraten.Login, Is.Null);
}
/// Auch der Code selbst taugt nicht als Abholschein.
[Test]
public async Task Der_Code_ist_kein_Abholschein()
{
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var echt = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync();
await server.AlsAngemeldeter(telefon.Token).PostAsync($"/api/v1/auth/code/{echt!.Code}", null);
var mitCode = await server.Client.GetFromJsonAsync($"/api/v1/auth/code/{echt.Code}");
Assert.That(mitCode!.Login, Is.Null);
}
[Test]
public async Task Ein_abgeholtes_Token_gibt_es_kein_zweites_Mal()
{
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync();
await server.AlsAngemeldeter(telefon.Token).PostAsync($"/api/v1/auth/code/{angefordert!.Code}", null);
var erstesMal = await server.Client.GetFromJsonAsync($"/api/v1/auth/code/{angefordert.Handle}");
var zweitesMal = await server.Client.GetFromJsonAsync($"/api/v1/auth/code/{angefordert.Handle}");
Assert.That(erstesMal!.State, Is.EqualTo("approved"));
Assert.That(zweitesMal!.State, Is.EqualTo("expired"));
Assert.That(zweitesMal.Login, Is.Null);
}
/// Bestätigen darf nur, wer angemeldet ist.
[Test]
public async Task Ein_Unangemeldeter_kann_nichts_bestaetigen()
{
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync();
// **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 versuch = await server.OhneGedaechtnis().PostAsync($"/api/v1/auth/code/{angefordert!.Code}", null);
Assert.That(versuch.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
var danach = await server.Client.GetFromJsonAsync($"/api/v1/auth/code/{angefordert.Handle}");
Assert.That(danach!.State, Is.EqualTo("pending"));
}
[Test]
public async Task Ein_erfundener_Code_laesst_sich_nicht_bestaetigen()
{
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var versuch = await server.AlsAngemeldeter(telefon.Token).PostAsync("/api/v1/auth/code/000000", null);
Assert.That(versuch.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
/// Am Telefon soll stehen, was man da hereinlässt.
[Test]
public async Task Die_Bestaetigung_nennt_das_Geraet()
{
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync();
var antwort = await server.AlsAngemeldeter(telefon.Token)
.PostAsync($"/api/v1/auth/code/{angefordert!.Code}", null);
Assert.That(await antwort.Content.ReadAsStringAsync(), Does.Contain("Wohnzimmer"));
}
}