chore: init project
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
using System.Net;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Xunit;
|
||||
|
||||
namespace InboxIntel.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Test host that disables startup auto-migration so the factory boots without
|
||||
/// a live PostgreSQL instance. The auth tests below never touch the database
|
||||
/// (the challenge happens in middleware before any controller runs).
|
||||
/// </summary>
|
||||
public class TestAppFactory : WebApplicationFactory<Program>
|
||||
{
|
||||
protected override IHost CreateHost(IHostBuilder builder)
|
||||
{
|
||||
builder.ConfigureHostConfiguration(cfg => cfg.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["Database:AutoMigrate"] = "false"
|
||||
}));
|
||||
return base.CreateHost(builder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smoke tests proving the host boots and authorization is enforced. A fuller
|
||||
/// suite would swap PostgreSQL for a Testcontainers instance and the Gmail
|
||||
/// client for a fake, then exercise sync -> analytics end to end.
|
||||
/// </summary>
|
||||
public class AuthEndpointsTests : IClassFixture<TestAppFactory>
|
||||
{
|
||||
private readonly TestAppFactory _factory;
|
||||
public AuthEndpointsTests(TestAppFactory factory) => _factory = factory;
|
||||
|
||||
[Fact]
|
||||
public async Task Protected_endpoint_challenges_when_anonymous()
|
||||
{
|
||||
var client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false });
|
||||
var resp = await client.GetAsync("/api/v1/analytics/dashboard");
|
||||
// Unauthenticated -> redirect to Google challenge (302) or 401.
|
||||
resp.StatusCode.Should().BeOneOf(HttpStatusCode.Found, HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Login_endpoint_is_anonymous()
|
||||
{
|
||||
var client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false });
|
||||
var resp = await client.GetAsync("/api/v1/auth/login");
|
||||
resp.StatusCode.Should().Be(HttpStatusCode.Redirect); // 302 to Google
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\InboxIntel.Api\InboxIntel.Api.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,41 @@
|
||||
using FluentAssertions;
|
||||
using InboxIntel.Application.Search;
|
||||
using Xunit;
|
||||
|
||||
namespace InboxIntel.UnitTests;
|
||||
|
||||
public class GmailQueryParserTests
|
||||
{
|
||||
[Fact]
|
||||
public void Parses_operators_and_free_text()
|
||||
{
|
||||
var r = GmailQueryParser.Parse("from:github.com is:unread has:attachment quarterly report");
|
||||
|
||||
r.Sender.Should().Be("github.com");
|
||||
r.IsUnread.Should().BeTrue();
|
||||
r.HasAttachments.Should().BeTrue();
|
||||
r.Query.Should().Be("quarterly report");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parses_date_range()
|
||||
{
|
||||
var r = GmailQueryParser.Parse("after:2025-01-01 before:2025-02-01");
|
||||
r.From.Should().Be(new DateOnly(2025, 1, 1));
|
||||
r.To.Should().Be(new DateOnly(2025, 2, 1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Empty_query_yields_empty_filters()
|
||||
{
|
||||
var r = GmailQueryParser.Parse(" ");
|
||||
r.Sender.Should().BeNull();
|
||||
r.Query.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Is_read_sets_unread_false()
|
||||
{
|
||||
GmailQueryParser.Parse("is:read").IsUnread.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\InboxIntel.Application\InboxIntel.Application.csproj" />
|
||||
<ProjectReference Include="..\..\src\InboxIntel.Infrastructure\InboxIntel.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,28 @@
|
||||
using FluentAssertions;
|
||||
using InboxIntel.Infrastructure.Gmail;
|
||||
using Xunit;
|
||||
|
||||
namespace InboxIntel.UnitTests;
|
||||
|
||||
public class UnsubscribeExtractionTests
|
||||
{
|
||||
[Fact]
|
||||
public void Prefers_http_link()
|
||||
{
|
||||
var raw = "<mailto:unsub@x.com>, <https://x.com/unsub?id=42>";
|
||||
GmailMessageParser.ExtractUnsubscribeTarget(raw).Should().Be("https://x.com/unsub?id=42");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Falls_back_to_mailto()
|
||||
{
|
||||
var raw = "<mailto:unsubscribe@news.example.com>";
|
||||
GmailMessageParser.ExtractUnsubscribeTarget(raw).Should().Be("mailto:unsubscribe@news.example.com");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Null_when_absent()
|
||||
{
|
||||
GmailMessageParser.ExtractUnsubscribeTarget(null).Should().BeNull();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user