Files
Wiki/test/Wiki.Application.Tests/DepositFactUseCaseTests.cs
T
claude-botandClaude Fable 5.1 6ff8d4bce6
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
store port: get and create documents; depositing a fact is a use case
The fact sink was a second adapter over the same backend. INoteStore
gains CreateAsync (DocumentExistsException on a taken path); the raw
note's shape, slug and collision numbering live in DepositFactUseCase;
GiteaRepository only moves text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 13:37:20 +02:00

72 lines
3.1 KiB
C#

using Microsoft.Extensions.Time.Testing;
using Wiki.Application.Facts;
using Wiki.Core.Model;
namespace Wiki.Application.Tests;
public class DepositFactUseCaseTests
{
private static readonly RawSource Fact = new(
Title: "UDM intercepts DNS: a follow-up",
Source: "https://example.com/thread#5",
Content: "The UDM answers every UDP/53 query itself, independent of what upstream is configured elsewhere.",
ObservedAt: new DateTimeOffset(2026, 9, 20, 14, 30, 0, TimeSpan.Zero),
Tags: ["dns", "udm"]);
private static readonly Identity By = new("agent-x", IdentityKind.Machine);
private static readonly DateTimeOffset Now = new(2026, 9, 23, 8, 0, 0, TimeSpan.Zero);
private const string ExpectedPath = "raw/2026-09-23-udm-intercepts-dns-a-follow-up.md";
private const string ExpectedText =
"---\n" +
"title: \"UDM intercepts DNS: a follow-up\"\n" +
"type: raw\n" +
"source: \"https://example.com/thread#5\"\n" +
"captured: 2026-09-23\n" +
"captured_by: agent-x\n" +
"observed: 2026-09-20T14:30:00+00:00\n" +
"tags: [dns, udm]\n" +
"---\n" +
"\n" +
"The UDM answers every UDP/53 query itself, independent of what upstream is configured elsewhere.\n";
private readonly FakeStore _store = new();
private DepositFactUseCase UseCase => new(_store, new FakeTimeProvider(Now));
[Fact]
public async Task WritesTheRawNoteAtTheDatedSlugPath()
{
var receipt = await UseCase.DepositAsync(Fact, By, CancellationToken.None);
Assert.Equal(ExpectedPath, receipt.Path);
Assert.Equal(Now, receipt.StoredAt);
Assert.Equal(ExpectedText, _store.Files[ExpectedPath]);
Assert.Equal(("raw: UDM intercepts DNS: a follow-up", ExpectedPath), _store.Created.Single());
}
[Fact]
public async Task NumbersThePathWhileItCollides()
{
_store.Files[ExpectedPath] = "already here";
_store.Files["raw/2026-09-23-udm-intercepts-dns-a-follow-up-2.md"] = "also here";
var receipt = await UseCase.DepositAsync(Fact, By, CancellationToken.None);
Assert.Equal("raw/2026-09-23-udm-intercepts-dns-a-follow-up-3.md", receipt.Path);
}
[Theory]
[InlineData("", "source", "content long enough to pass the minimum length check, easily.")]
[InlineData("title", "", "content long enough to pass the minimum length check, easily.")]
[InlineData("title", "source", "too short")]
public async Task RejectsAnInvalidFactBeforeTouchingTheStore(string title, string source, string content)
{
var invalid = new RawSource(title, source, content, Now, []);
await Assert.ThrowsAsync<ArgumentException>(() => UseCase.DepositAsync(invalid, By, CancellationToken.None));
Assert.Empty(_store.Created);
}
[Theory]
[InlineData("Talos v1.13.10: aes256k / kernel 6.18!", "talos-v1-13-10-aes256k-kernel-6-18")]
[InlineData(" ", "untitled")]
[InlineData("Ünïcödé näme", "n-c-d-n-me")]
public void SlugifiesTitles(string title, string slug) => Assert.Equal(slug, DepositFactUseCase.Slugify(title));
}