• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Aldaviva / GamesDoneQuickCalendarFactory / 16208924000

11 Jul 2025 12:28AM UTC coverage: 76.483% (-2.0%) from 78.475%
16208924000

push

github

Aldaviva
Cleared local package cache of unofficial development build of Unfucked.DI

124 of 194 branches covered (63.92%)

361 of 472 relevant lines covered (76.48%)

90.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.32
/GamesDoneQuickCalendarFactory/Program.cs
1
using Bom.Squad;
2
using GamesDoneQuickCalendarFactory;
3
using GamesDoneQuickCalendarFactory.Data;
4
using GamesDoneQuickCalendarFactory.Services;
5
using Ical.Net.Serialization;
6
using Microsoft.AspNetCore.Http.Headers;
7
using Microsoft.AspNetCore.HttpOverrides;
8
using Microsoft.AspNetCore.Mvc;
9
using Microsoft.AspNetCore.OutputCaching;
10
using Microsoft.Net.Http.Headers;
11
using NodaTime;
12
using System.Net.Mime;
13
using System.Text;
14
using System.Text.RegularExpressions;
15
using Unfucked;
16
using Unfucked.HTTP;
17

18
BomSquad.DefuseUtf8Bom();
10✔
19

20
Encoding             responseEncoding     = Encoding.UTF8;
10✔
21
MediaTypeHeaderValue icalendarContentType = new("text/calendar") { Charset = responseEncoding.WebName };
10✔
22
string[]             varyHeaderValue      = [HeaderNames.AcceptEncoding];
10✔
23

24
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
10✔
25
builder.Host
10✔
26
    .UseWindowsService()
10✔
27
    .UseSystemd();
10✔
28

29
builder.Logging.AddUnfuckedConsole();
10✔
30

31
builder.Configuration.AlsoSearchForJsonFilesInExecutableDirectory();
10✔
32

33
// GZIP response compression is handled by Apache httpd, not Kestrel, per https://learn.microsoft.com/en-us/aspnet/core/performance/response-compression?view=aspnetcore-8.0#when-to-use-response-compression-middleware
34
builder.Services
10✔
35
    .Configure<Configuration>(builder.Configuration)
10✔
36
    .AddOutputCache()
10✔
37
    .AddSingleton<ICalendarGenerator, CalendarGenerator>()
10✔
38
    .AddSingleton<IEventDownloader, EventDownloader>()
10✔
39
    .AddSingleton<IGdqClient, GdqClient>()
10✔
40
    .AddSingleton<ICalendarPoller, CalendarPoller>()
10✔
41
    .AddSingleton<IGoogleCalendarSynchronizer, GoogleCalendarSynchronizer>()
10✔
42
    .AddSingleton<IClock>(SystemClock.Instance)
10✔
43
    .AddSingleton<HttpClient>(new UnfuckedHttpClient(new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromHours(1) }) { Timeout = TimeSpan.FromSeconds(30) });
10✔
44

45
builder.Services.AddSingleton(await State.load("state.json"));
10✔
46

47
await using WebApplication webApp = builder.Build();
10✔
48

49
webApp
10✔
50
    .UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto })
10✔
51
    .UseOutputCache()
10✔
52
    .Use(async (context, next) => {
10✔
53
        ICalendarPoller calendarPoller  = webApp.Services.GetRequiredService<ICalendarPoller>();
10✔
54
        ResponseHeaders responseHeaders = context.Response.GetTypedHeaders();
10✔
55
        responseHeaders.CacheControl               = new CacheControlHeaderValue { Public = true, MaxAge = calendarPoller.getPollingInterval() }; // longer cache when no event running
10✔
56
        context.Response.Headers[HeaderNames.Vary] = varyHeaderValue;
10✔
57

10✔
58
        if (await calendarPoller.mostRecentlyPolledCalendar.ResultOrNullForException() is { } mostRecentlyPolledCalendar) {
10✔
59
            responseHeaders.ETag         = mostRecentlyPolledCalendar.etag;
10✔
60
            responseHeaders.LastModified = mostRecentlyPolledCalendar.dateModified;
10✔
61
        }
10✔
62
        await next();
10✔
63
    });
20✔
64

65
webApp.MapGet("/", [OutputCache] async Task ([FromServices] ICalendarPoller calendarPoller, HttpResponse response) => {
10✔
66
    try {
10✔
67
        if (await calendarPoller.mostRecentlyPolledCalendar is { } mostRecentlyPolledCalendar) {
4✔
68
            response.GetTypedHeaders().ContentType = icalendarContentType;
4✔
69
            await new CalendarSerializer().SerializeAsync(mostRecentlyPolledCalendar.calendar, response.Body, responseEncoding);
4✔
70
        } else {
10✔
71
            response.StatusCode = StatusCodes.Status204NoContent;
×
72
        }
10✔
73
    } catch (Exception e) when (e is not OutOfMemoryException) {
4✔
74
        response.StatusCode  = StatusCodes.Status500InternalServerError;
×
75
        response.ContentType = MediaTypeNames.Text.Plain;
×
76
        await using StreamWriter bodyWriter = new(response.Body, responseEncoding);
×
77
        await bodyWriter.WriteAsync(e.ToString());
×
78
    }
×
79
});
14✔
80

81
webApp.MapGet("/badge.json", [OutputCache] async ([FromServices] IEventDownloader eventDownloader) =>
10✔
82
await eventDownloader.downloadSchedule() is { } schedule
16✔
83
    ? new ShieldsBadgeResponse(
16✔
84
        label: shortNamePattern().Replace(schedule.shortTitle, " ").ToLower(), // add spaces to abbreviation
16✔
85
        message: $"{schedule.runs.Count} {(schedule.runs.Count == 1 ? "run" : "runs")}",
16✔
86
        color: "success",
16✔
87
        logoSvg: Resources.gdqDpadBadgeLogo)
16✔
88
    : new ShieldsBadgeResponse("gdq", "no event now", "inactive", false, Resources.gdqDpadBadgeLogo));
16✔
89

90
await webApp.Services.GetRequiredService<IGoogleCalendarSynchronizer>().start();
10✔
91

92
await webApp.RunAsync();
10✔
93

94
internal partial class Program {
95

96
    [GeneratedRegex(@"(?<=\D)(?=\d)|(?<=[a-z])(?=[A-Z])")]
97
    private static partial Regex shortNamePattern();
98

99
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc