From 5b48e0bfdb798e5cb33090d5496ffe60498d233c Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Sat, 12 Sep 2026 14:30:50 +0100 Subject: [PATCH] GetOpenIdConfiguration_returns_ok against testing host --- src/Yavsc.Org.Tests/Mandatory/BatchTests.cs | 49 +++++++++++++++-- src/Yavsc.Org.Tests/Mandatory/Remoting.cs | 24 +++++++++ .../Smoke/AccountSmokeTests.cs | 19 +------ src/Yavsc.Org.Tests/Smoke/SmokeTestBase.cs | 52 ------------------- src/Yavsc.Org.Tests/WebServerFixture.cs | 1 + 5 files changed, 70 insertions(+), 75 deletions(-) delete mode 100644 src/Yavsc.Org.Tests/Smoke/SmokeTestBase.cs diff --git a/src/Yavsc.Org.Tests/Mandatory/BatchTests.cs b/src/Yavsc.Org.Tests/Mandatory/BatchTests.cs index 35bb5aa59..daa2bf1fe 100644 --- a/src/Yavsc.Org.Tests/Mandatory/BatchTests.cs +++ b/src/Yavsc.Org.Tests/Mandatory/BatchTests.cs @@ -10,7 +10,7 @@ namespace Yavsc.Org.Tests { [Collection("Yavsc Server")] [Trait("regression", "oui")] - public class BaseTestContext: IClassFixture, IDisposable + public class BaseTestContext : IClassFixture, IDisposable { public readonly WebServerFixture _serverFixture; private readonly ITestOutputHelper _output; @@ -21,6 +21,45 @@ namespace Yavsc.Org.Tests this._output = output; } + public HttpClient CreateHttpClient() + { + return new HttpClient(new BypassSslValidationHandler()) + { + BaseAddress = new Uri(this._serverFixture.HttpsAuthority ?? throw new InvalidOperationException("Missing HttpsAuthority")) + }; + } + + /// + /// Issue a GET against on the + /// in-memory test server. Returns the raw HttpResponseMessage + /// without following redirects — the test asserts on the first + /// hop, not the eventual page. + /// + protected static async Task GetRaw( + HttpClient client, string relativePath) + { + Assert.NotNull(client); + var request = new HttpRequestMessage(HttpMethod.Get, relativePath); + return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + } + /// + /// Smoke assertion: a GET on + /// returns 2xx (page served) or 3xx (redirect to login) or + /// 401/403 (anonymous rejected by [Authorize]). Anything else + /// — 404 (route missing), 5xx (server crash), connection + /// refused (host not started) — fails the test. + /// + protected static async Task AssertResponds( + HttpClient client, string relativePath) + { + var response = await GetRaw(client, relativePath); + var status = (int)response.StatusCode; + Assert.True( + status >= 200 && status < 400 || status == 401 || status == 403, + $"GET {relativePath} returned {status} {response.StatusCode}, " + + "expected 2xx/3xx (page or redirect) or 401/403 (auth required)."); + } + // FIXME write a scenario from an empty database [Fact] public void GitClone() { @@ -36,7 +75,7 @@ namespace Yavsc.Org.Tests var firstProject = dbContext.Project.Include(p => p.Repository).FirstOrDefault( p => p.Name == "Yavsc" ); - Assert.NotNull (firstProject); + Assert.NotNull(firstProject); var di = new DirectoryInfo(_serverFixture.SiteSettings.GitRepository); if (!di.Exists) di.Create(); @@ -44,7 +83,7 @@ namespace Yavsc.Org.Tests clone.Launch(firstProject); gitRepo = di.FullName; } - string gitRepo=null; + string gitRepo = null; private IConfigurationRoot configurationRoot; @@ -57,9 +96,9 @@ namespace Yavsc.Org.Tests public void Dispose() { - if (gitRepo!=null) + if (gitRepo != null) { - Directory.Delete(Path.Combine(gitRepo,"yavsc"), true); + Directory.Delete(Path.Combine(gitRepo, "yavsc"), true); } } } diff --git a/src/Yavsc.Org.Tests/Mandatory/Remoting.cs b/src/Yavsc.Org.Tests/Mandatory/Remoting.cs index be5b716f4..125d6f75e 100644 --- a/src/Yavsc.Org.Tests/Mandatory/Remoting.cs +++ b/src/Yavsc.Org.Tests/Mandatory/Remoting.cs @@ -69,6 +69,29 @@ namespace Yavsc.Org.Tests } + [Fact] + public async Task GetSignin_returns_a_page() + { + using var client = new HttpClient(new BypassSslValidationHandler()) + { + BaseAddress = new Uri(this._serverFixture.HttpsAuthority ?? throw new InvalidOperationException("Missing HttpsAuthority")) + }; + await AssertResponds(client, "/signin"); + } + + + + [Fact] + public async Task GetOpenIdConfiguration_returns_ok() + { + using var client = _serverFixture.CreateHttpClient(); + var response = await GetRaw(client, "/.well-known/openid-configuration"); + var payload = await response.Content.ReadAsStringAsync(); + + Assert.True( + response.IsSuccessStatusCode, + $"GET /.well-known/openid-configuration returned {(int)response.StatusCode} {response.StatusCode}. Body: {payload}"); + } public static IEnumerable GetLoginIntentData() { return new object[][] { new object[] { "testuser", "test" } }; @@ -124,4 +147,5 @@ namespace Yavsc.Org.Tests return true; } } + } diff --git a/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs b/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs index 327c2db4e..9b24582a3 100644 --- a/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs +++ b/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs @@ -18,7 +18,7 @@ namespace Yavsc.Org.Tests.Smoke; /// entire pipeline (routing + Razor + IdentityServer + EF + DI) /// is wired correctly end-to-end. /// -public class AccountSmokeTests : SmokeTestBase, IClassFixture +public class AccountSmokeTests : IClassFixture { private readonly TestWebApplicationFactory _factory; @@ -27,24 +27,7 @@ public class AccountSmokeTests : SmokeTestBase, IClassFixture -/// Base for the smoke tests covering the production hosts -/// (Yavsc.Org / Yavsc.Api / Yavsc.Blogs). One smoke test per -/// bounded context (BC): each test hits one GET endpoint and -/// asserts a 2xx or 3xx status, with no follow-up redirect. -/// Together they satisfy the 'Tests d'intégration smoke par BC' -/// item of Jalon 0 in ROADMAP.md. -/// -/// Status code policy: -/// - 200 OK : endpoint serves a page. -/// - 302 / 301 : endpoint requires auth and redirects to login -/// (acceptable smoke signal: routing + middleware are wired). -/// - 401 / 403 : endpoint exists but rejects anonymous (acceptable -/// for API smoke tests where the smoke is "the host boots"). -/// Anything else (404, 500, connection refused) is a failure. -/// -public abstract class SmokeTestBase -{ - /// - /// Issue a GET against on the - /// in-memory test server. Returns the raw HttpResponseMessage - /// without following redirects — the test asserts on the first - /// hop, not the eventual page. - /// - protected static async Task GetRaw( - HttpClient client, string relativePath) - { - Assert.NotNull(client); - var request = new HttpRequestMessage(HttpMethod.Get, relativePath); - return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - } - - /// - /// Smoke assertion: a GET on - /// returns 2xx (page served) or 3xx (redirect to login) or - /// 401/403 (anonymous rejected by [Authorize]). Anything else - /// — 404 (route missing), 5xx (server crash), connection - /// refused (host not started) — fails the test. - /// - protected static async Task AssertResponds( - HttpClient client, string relativePath) - { - var response = await GetRaw(client, relativePath); - var status = (int)response.StatusCode; - Assert.True( - status >= 200 && status < 400 || status == 401 || status == 403, - $"GET {relativePath} returned {status} {response.StatusCode}, " + - "expected 2xx/3xx (page or redirect) or 401/403 (auth required)."); - } -} diff --git a/src/Yavsc.Org.Tests/WebServerFixture.cs b/src/Yavsc.Org.Tests/WebServerFixture.cs index f82771ec6..9115af234 100644 --- a/src/Yavsc.Org.Tests/WebServerFixture.cs +++ b/src/Yavsc.Org.Tests/WebServerFixture.cs @@ -78,6 +78,7 @@ public sealed class WebServerFixture : WebHostFixture public RecordingSmtpClientFactory? SmtpClientFactory { get; private set; } public ILogger? Logger { get; internal set; } + public string? HttpsAuthority => Addresses.FirstOrDefault(u => u.StartsWith("https:")); protected override WebApplication BuildApp(WebApplicationBuilder builder) { var authority = $"https://localhost:{_httpsPort}";