using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SameBeat.Infrastructure.BazaDanych;
using Xunit;
namespace SameBeat.Tests.Integration;
///
/// Pierwszy prawdziwy test integracyjny projektu (ETAP 7) -- uruchamia CALA aplikacje przez
/// WebApplicationFactory<Program> (TestServer, in-memory, bez prawdziwego portu/sieci) i sprawdza,
/// ze NaglowkiBezpieczenstwaMiddleware faktycznie dokleja naglowki bezpieczenstwa do prawdziwej
/// odpowiedzi HTTP -- nie tylko "middleware istnieje w kodzie".
///
/// Srodowisko "Testing" (nie "Development") -- zeby ominac blok w Program.cs, ktory przy starcie
/// probuje polaczyc sie z prawdziwym Postgresem (migracje/seed); KontekstBazyDanych jest tu dodatkowo
/// podmieniony na EF Core InMemory, zeby test dzialal bez wzgledu na to, czy jakakolwiek baza danych
/// jest osiagalna w srodowisku, w ktorym leci "dotnet test".
///
public class NaglowkiBezpieczenstwaTests : IClassFixture>
{
private readonly WebApplicationFactory _fabryka;
public NaglowkiBezpieczenstwaTests(WebApplicationFactory fabryka)
{
_fabryka = fabryka.WithWebHostBuilder(builder =>
{
builder.UseEnvironment("Testing");
builder.ConfigureServices(services =>
{
var deskryptorKontekstu = services.SingleOrDefault(s => s.ServiceType == typeof(DbContextOptions));
if (deskryptorKontekstu is not null)
{
services.Remove(deskryptorKontekstu);
}
services.AddDbContext(opcje =>
opcje.UseInMemoryDatabase("NaglowkiBezpieczenstwaTests"));
});
});
}
[Fact]
public async Task Odpowiedz_ApiHealth_ZawieraNaglowkiBezpieczenstwa()
{
var klient = _fabryka.CreateClient();
var odpowiedz = await klient.GetAsync("/api/health");
Assert.True(odpowiedz.Headers.TryGetValues("X-Content-Type-Options", out var nosniff));
Assert.Equal("nosniff", Assert.Single(nosniff!));
Assert.True(odpowiedz.Headers.TryGetValues("X-Frame-Options", out var frameOptions));
Assert.Equal("DENY", Assert.Single(frameOptions!));
Assert.True(odpowiedz.Headers.TryGetValues("Referrer-Policy", out var referrerPolicy));
Assert.Equal("strict-origin-when-cross-origin", Assert.Single(referrerPolicy!));
Assert.True(odpowiedz.Headers.TryGetValues("Content-Security-Policy", out var cspWartosci));
var csp = Assert.Single(cspWartosci!);
Assert.Contains("default-src 'self'", csp);
Assert.Contains("script-src 'self'", csp);
Assert.Contains("frame-ancestors 'none'", csp);
// KLUCZOWE: script-src NIGDY nie moze dostac 'unsafe-inline' (to byloby otwarcie na XSS) --
// sprawdzamy caly token, zeby nie dac sie zludzic np. obecnosci "style-src ... 'unsafe-inline'".
Assert.DoesNotContain("script-src 'self' 'unsafe-inline'", csp);
}
[Fact]
public async Task Odpowiedz_ApiHealth_MaPoprawnyStatusOK()
{
var klient = _fabryka.CreateClient();
var odpowiedz = await klient.GetAsync("/api/health");
odpowiedz.EnsureSuccessStatusCode();
}
}