using Microsoft.Extensions.Logging.Abstractions;
using SameBeat.Application.Interfejsy;
using SameBeat.Application.Komendy;
using SameBeat.Application.Serwisy;
using SameBeat.Domain.Encje;
using SameBeat.Domain.Enumy;
using SameBeat.Infrastructure.Autoryzacja;
using Xunit;
namespace SameBeat.Tests.Unit;
///
/// Poczatek lancucha bezpieczenstwa "poczekalnia -> scena": POST /api/join musi zwrocic sekret
/// sesji JAWNY (raz), a w bazie ma zostac wylacznie jego hash -- ten hash pozniej weryfikuje
/// SerwisSceny (patrz SerwisScenyTests) przy statusie i wydawaniu tokenu sceny.
///
public class SerwisPoczekalniTests
{
private sealed class FejkRepozytoriumWydarzen : IRepozytoriumWydarzen
{
private readonly List _wydarzenia = new();
public Task PobierzOstatnieAsync(CancellationToken token = default) => Task.FromResult(_wydarzenia.LastOrDefault());
public Task ZnajdzPoIdAsync(Guid id, CancellationToken token = default) =>
Task.FromResult(_wydarzenia.SingleOrDefault(w => w.Id == id));
public void Dodaj(Wydarzenie wydarzenie) => _wydarzenia.Add(wydarzenie);
}
private sealed class FejkRepozytoriumPoczekalni : IRepozytoriumPoczekalni
{
public readonly List Uczestnicy = new();
public Task ZnajdzPoIdAsync(Guid id, CancellationToken token = default) =>
Task.FromResult(Uczestnicy.SingleOrDefault(u => u.Id == id));
public Task> PobierzOczekujacychDlaWydarzeniaAsync(Guid wydarzenieId, CancellationToken token = default) =>
Task.FromResult(Uczestnicy.Where(u => u.WydarzenieId == wydarzenieId).ToList());
public void Dodaj(UczestnikOczekujacy uczestnik) => Uczestnicy.Add(uczestnik);
}
private sealed class FejkJednostkaPracy : IJednostkaPracy
{
public Task ZapiszZmianyAsync(CancellationToken token = default) => Task.FromResult(0);
}
private sealed class FejkSerwisTokenowLiveKit : ISerwisTokenowLiveKit
{
public string WygenerujToken(string identity, string nazwaWyswietlana, RolaUzytkownika rola, string nazwaPokoju) => "token-testowy";
}
private const string PokojPoczekalnia = "impreza-poczekalnia";
private const string UrlLiveKit = "wss://przyklad.livekit.cloud";
private readonly FejkRepozytoriumWydarzen _repozytoriumWydarzen = new();
private readonly FejkRepozytoriumPoczekalni _repozytoriumPoczekalni = new();
private readonly IUslugaHaszowaniaSekretowSesji _uslugaHaszowaniaSekretowSesji = new UslugaHaszowaniaSekretowSesji();
private readonly SerwisPoczekalni _serwis;
public SerwisPoczekalniTests()
{
_serwis = new SerwisPoczekalni(
_repozytoriumWydarzen,
_repozytoriumPoczekalni,
new FejkSerwisTokenowLiveKit(),
_uslugaHaszowaniaSekretowSesji,
new FejkJednostkaPracy(),
UrlLiveKit,
PokojPoczekalnia,
NullLogger.Instance);
_repozytoriumWydarzen.Dodaj(Wydarzenie.Utworz("Testowa impreza"));
}
[Fact]
public async Task DolaczAsync_ZwracaJawnySekretIZapisujeWylacznieJegoHash()
{
var wynik = await _serwis.DolaczAsync(new DolaczKomenda("Piotr"));
Assert.True(wynik.Sukces);
var sekretJawny = wynik.Dane!.SekretSesji;
Assert.False(string.IsNullOrWhiteSpace(sekretJawny));
var uczestnik = _repozytoriumPoczekalni.Uczestnicy.Single();
// W bazie nie ma jawnego sekretu -- tylko hash, i to nie jest ten sam string co sekret.
Assert.NotNull(uczestnik.SekretSesjiHash);
Assert.NotEqual(sekretJawny, uczestnik.SekretSesjiHash);
// Ale jawny sekret zwrocony w odpowiedzi faktycznie pasuje do zapisanego hasha.
var wynikWeryfikacji = _uslugaHaszowaniaSekretowSesji.Weryfikuj(uczestnik, uczestnik.SekretSesjiHash!, sekretJawny);
Assert.Equal(WynikWeryfikacjiHasla.Poprawne, wynikWeryfikacji);
}
[Fact]
public async Task DolaczAsync_KazdeDolaczenieDostajeInnySekret()
{
var pierwszy = await _serwis.DolaczAsync(new DolaczKomenda("Ala"));
var drugi = await _serwis.DolaczAsync(new DolaczKomenda("Bartek"));
Assert.NotEqual(pierwszy.Dane!.SekretSesji, drugi.Dane!.SekretSesji);
}
}