using Elternbeirat.PocketBase; namespace Elternbeirat.Web.Tests; /// /// Tests the against a throwaway PocketBase container /// with a known seed (see ). Because the data is fixed, /// the tests assert on exact values, and they need no network to the live instance. /// [Collection(PocketBaseTestGroup.Name)] public sealed class PocketBaseClientTests(PocketBaseFixture pocketBase) { [Fact] public async Task Events_load_sorted_by_start() { var client = pocketBase.CreateClient(); var events = await client.GetEventsAsync(); // Two seeded events, earliest start first. events.Select(e => e.Title).ShouldBe(["Elternbeiratssitzung", "Herbstbasar"]); events.ShouldAllBe(e => e.Public); } [Fact] public async Task Event_utc_is_converted_to_berlin_wall_clock() { var client = pocketBase.CreateClient(); var events = await client.GetEventsAsync(); // The meeting is seeded as 17:30Z. October is summer time (UTC+2), so the // converter turns it into 19:30 Berlin wall-clock -- the time an editor // entered in the admin form. var meeting = events.FirstOrDefault(e => e.Title.Contains("Elternbeiratssitzung", StringComparison.Ordinal)); meeting.ShouldNotBeNull(); meeting.Start.Hour.ShouldBe(19); meeting.Start.Minute.ShouldBe(30); meeting.Start.Kind.ShouldBe(DateTimeKind.Unspecified); } [Fact] public async Task Event_without_end_maps_to_null() { var client = pocketBase.CreateClient(); var events = await client.GetEventsAsync(); var meeting = events.Single(e => e.Title == "Elternbeiratssitzung"); var basar = events.Single(e => e.Title == "Herbstbasar"); meeting.End.ShouldBeNull(); // no end seeded basar.End.ShouldNotBeNull(); // end seeded } [Fact] public async Task Posts_load_newest_first() { var client = pocketBase.CreateClient(); var posts = await client.GetPostsAsync(); // Sorted by -date: March before January. posts.Select(p => p.Title).ShouldBe(["Neuer Vorstand", "Neue Sporthalle"]); } [Fact] public async Task Post_date_is_the_berlin_calendar_day() { var client = pocketBase.CreateClient(); var posts = await client.GetPostsAsync(); // The post is seeded as 2026-03-01 23:30Z, which is 2 March 00:30 in Berlin. // The date is the Berlin day the editor picked (2 March), not the raw UTC // day (1 March) -- the time zone is applied before the day is taken. var post = posts.Single(p => p.Title == "Neuer Vorstand"); post.Date.ShouldBe(new DateOnly(2026, 3, 2)); } [Fact] public async Task Pages_exclude_non_public_records() { var client = pocketBase.CreateClient(); var pages = await client.GetPagesAsync(); // The draft (public=false) must be filtered out, the public ones present. pages.ShouldNotContain(p => p.Slug == "draft"); pages.ShouldContain(p => p.Slug == "home"); pages.ShouldContain(p => p.Slug == "contact"); } [Fact] public async Task Page_embed_is_read_as_list() { var client = pocketBase.CreateClient(); var pages = await client.GetPagesAsync(); var home = pages.Single(p => p.Slug == "home"); home.Embed.ShouldBe(["posts", "events"], ignoreOrder: true); // Home sits in the header like every other page (location is required); it // is the single source of the home nav link. home.Location.ShouldBe("header"); } [Fact] public async Task Faqs_load_from_instance() { var client = pocketBase.CreateClient(); var faqs = await client.GetFaqsAsync(); faqs.Select(f => f.Topic).ShouldBe(["mensa", "schliessfach"], ignoreOrder: true); faqs.ShouldAllBe(f => !string.IsNullOrWhiteSpace(f.Question)); } }