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(); } }