test(blogs): GET /api/v1/blog returns 200 with empty list
First behavioural test for the Yavsc.Blogs API surface. Sends a GET on the blog index with the X-Test-Role auth bypass and asserts the response is 200 with an empty JSON array — the in-memory ApplicationDbContext has no rows, and BlogSpotService.Index returns an empty enumeration. While here, fix a routing miss: AddControllers() in the test fixture was only scanning the test assembly, so BlogApiController was never registered. Add the Yavsc.Blogs application part explicitly. Without this, every request to /api/v1/blog came back as 404 — the same symptom PostIt was seeing in production. The POST flow lands in the next commit, once BlogApiController is made to accept JSON (it currently requires multipart/form-data because of Request.Form.Files).
This commit is contained in:
parent
8c38bab45a
commit
9f5a1505e3
2 changed files with 72 additions and 7 deletions
65
src/Yavsc.Blogs.Tests/BlogApiTests.cs
Normal file
65
src/Yavsc.Blogs.Tests/BlogApiTests.cs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Yavsc.Blogs.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Behavioural tests for <c>BlogApiController</c>. Built on the
|
||||||
|
/// <see cref="BlogsWebServerFixture"/> scaffold: in-memory
|
||||||
|
/// <c>ApplicationDbContext</c>, real <c>BlogSpotService</c>,
|
||||||
|
/// <c>X-Test-Role</c> for the <c>[Authorize("BlogScope")]</c>
|
||||||
|
/// attribute.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
|
||||||
|
{
|
||||||
|
private readonly BlogsWebServerFixture _fixture;
|
||||||
|
|
||||||
|
public BlogApiTests(BlogsWebServerFixture fixture)
|
||||||
|
{
|
||||||
|
_fixture = fixture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The fixture's <c>WebApplication</c> is bound to
|
||||||
|
/// <c>https://localhost:<random></c> via
|
||||||
|
/// <see cref="WebHostFixture.Addresses"/>. We pick the first
|
||||||
|
/// https URL and append the controller route
|
||||||
|
/// (<c>/api/v1/blog</c>, matching the production
|
||||||
|
/// <c>[Route(APIPrefix + "/blog")]</c>).</summary>
|
||||||
|
private string BlogsUrl =>
|
||||||
|
_fixture.Addresses.First(a => a.StartsWith("https://")) + "/api/v1/blog";
|
||||||
|
|
||||||
|
private HttpClient NewClient()
|
||||||
|
{
|
||||||
|
// The fixture's self-signed certificate is not in the user's
|
||||||
|
// trust store, so we accept anything (same pattern as
|
||||||
|
// Yavsc.Org.Tests' BypassSslValidationHandler).
|
||||||
|
var handler = new HttpClientHandler
|
||||||
|
{
|
||||||
|
ServerCertificateCustomValidationCallback = (_, _, _, _) => true
|
||||||
|
};
|
||||||
|
var http = new HttpClient(handler)
|
||||||
|
{
|
||||||
|
BaseAddress = new Uri(_fixture.Addresses.First(a => a.StartsWith("https://")))
|
||||||
|
};
|
||||||
|
http.DefaultRequestHeaders.Add(TestAuthPolicyProvider.HeaderName, TestAuthPolicyProvider.AdminRole);
|
||||||
|
return http;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetBlogs_returns_200_with_empty_list_when_no_posts()
|
||||||
|
{
|
||||||
|
using var http = NewClient();
|
||||||
|
|
||||||
|
var response = await http.GetAsync("/api/v1/blog");
|
||||||
|
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
// Empty table → empty JSON array. We compare as a JsonDocument
|
||||||
|
// so a future change in formatting (whitespace, indentation)
|
||||||
|
// doesn't break the assertion.
|
||||||
|
using var doc = JsonDocument.Parse(body);
|
||||||
|
Assert.Equal(JsonValueKind.Array, doc.RootElement.ValueKind);
|
||||||
|
Assert.Equal(0, doc.RootElement.GetArrayLength());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Mvc.Testing;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Yavsc.Blogs.Controllers;
|
||||||
using Yavsc;
|
|
||||||
using Yavsc.Models;
|
using Yavsc.Models;
|
||||||
using Yavsc.Server.Services;
|
|
||||||
using Yavsc.Services;
|
using Yavsc.Services;
|
||||||
using Yavsc.Tests.Shared;
|
using Yavsc.Tests.Shared;
|
||||||
|
|
||||||
|
|
@ -59,9 +56,12 @@ public sealed class BlogsWebServerFixture : WebHostFixture
|
||||||
// IFileSystemAuthManager).
|
// IFileSystemAuthManager).
|
||||||
builder.Services.AddScoped<BlogSpotService>();
|
builder.Services.AddScoped<BlogSpotService>();
|
||||||
|
|
||||||
// The BlogApiController is reached through MVC, so register
|
// The BlogApiController is reached through MVC. AddControllers()
|
||||||
// MVC + the BlogScope authorization policy.
|
// by default scans the test assembly only; we explicitly add the
|
||||||
builder.Services.AddControllers();
|
// Yavsc.Blogs application part so the controller is discovered
|
||||||
|
// and routed.
|
||||||
|
builder.Services.AddControllers()
|
||||||
|
.AddApplicationPart(typeof(BlogApiController).Assembly);
|
||||||
builder.Services.AddAuthorization(opt =>
|
builder.Services.AddAuthorization(opt =>
|
||||||
{
|
{
|
||||||
// Mirror the production "BlogScope" policy: any
|
// Mirror the production "BlogScope" policy: any
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue