noz-cli/NozCli.Tests/ReplContextTests.cs
kryptomrx 0eebffe745 test: xUnit test suite and Forgejo CI pipeline
- 43 tests across SlugGuard, ReplContext navigation, NotesCache.TimeAgo
- SlugGuard moved to NozCli.Core.Security (shared between REPL and tests)
- Bug fix: FoldersInView() now returns only actual sub-folders, not note segments
  (previously cd tab-completion suggested note names as navigable folders)
- .forgejo/workflows/ci.yml: build + test on every push/PR
2026-05-16 23:53:40 +02:00

165 lines
5.7 KiB
C#

using Xunit;
using NozCli.Core.Config;
using NozCli.Core.Models;
using NozCli.Repl;
namespace NozCli.Tests;
public class ReplContextTests
{
// Builds a ReplContext with a fixed NoteCache — no API calls made.
private static ReplContext BuildCtx(params NoteInfo[] notes)
{
var cfg = new LocalConfig { ServerUrl = "http://localhost:9999", Token = "test" };
var ctx = new ReplContext(null!, cfg); // Api is null — not used in navigation
ctx.NoteCache = [.. notes];
return ctx;
}
private static NoteInfo Note(string slug, string topic, string status = "seed") =>
new(slug, $"Title {slug}", status, topic, "de", false, null);
// ── CurrentPath ──────────────────────────────────────────────────────────
[Fact]
public void CurrentPath_AtRoot_IsWave()
{
var ctx = BuildCtx();
Assert.Equal("~", ctx.CurrentPath);
}
[Fact]
public void CurrentPath_InTopic_ShowsTopic()
{
var ctx = BuildCtx(Note("psychologie/test", "psychologie"));
ctx.NavigateTo("psychologie");
Assert.Equal("~/psychologie", ctx.CurrentPath);
}
[Fact]
public void CurrentPath_InSubfolder_ShowsFullPath()
{
var ctx = BuildCtx(Note("psychologie/sub/deep", "psychologie"));
ctx.NavigateTo("psychologie");
ctx.NavigateTo("sub");
Assert.Equal("~/psychologie/sub", ctx.CurrentPath);
}
// ── FoldersInView ────────────────────────────────────────────────────────
[Fact]
public void FoldersInView_AtRoot_ReturnsDistinctTopics()
{
var ctx = BuildCtx(
Note("psychologie/a", "psychologie"),
Note("psychologie/b", "psychologie"),
Note("biosysteme/c", "biosysteme"));
var folders = ctx.FoldersInView().OrderBy(f => f).ToList();
Assert.Equal(["biosysteme", "psychologie"], folders);
}
[Fact]
public void FoldersInView_InTopic_ReturnsSubfolders()
{
var ctx = BuildCtx(
Note("psychologie/sub/note", "psychologie"),
Note("psychologie/flat-note", "psychologie"));
ctx.NavigateTo("psychologie");
var folders = ctx.FoldersInView().ToList();
Assert.Contains("sub", folders);
Assert.DoesNotContain("flat-note", folders);
}
// ── NavigateTo / NavigateUp ──────────────────────────────────────────────
[Fact]
public void NavigateTo_ValidTopic_ReturnsTrue()
{
var ctx = BuildCtx(Note("psychologie/test", "psychologie"));
Assert.True(ctx.NavigateTo("psychologie"));
Assert.Equal("psychologie", ctx.CurrentTopic);
}
[Fact]
public void NavigateTo_UnknownTopic_ReturnsFalse()
{
var ctx = BuildCtx(Note("psychologie/test", "psychologie"));
Assert.False(ctx.NavigateTo("doesnotexist"));
Assert.Null(ctx.CurrentTopic);
}
[Fact]
public void NavigateUp_FromTopic_ReturnsToRoot()
{
var ctx = BuildCtx(Note("psychologie/test", "psychologie"));
ctx.NavigateTo("psychologie");
ctx.NavigateUp();
Assert.Null(ctx.CurrentTopic);
Assert.Equal("~", ctx.CurrentPath);
}
[Fact]
public void NavigateUp_FromSubfolder_ReturnsToTopic()
{
var ctx = BuildCtx(Note("psychologie/sub/deep", "psychologie"));
ctx.NavigateTo("psychologie");
ctx.NavigateTo("sub");
ctx.NavigateUp();
Assert.Equal("psychologie", ctx.CurrentTopic);
Assert.Null(ctx.CurrentSubPath);
}
// ── SlugRelative ─────────────────────────────────────────────────────────
[Fact]
public void SlugRelative_InTopic_StripsTopicPrefix()
{
var ctx = BuildCtx(Note("psychologie/flow", "psychologie"));
ctx.NavigateTo("psychologie");
Assert.Equal("flow", ctx.SlugRelative("psychologie/flow"));
}
[Fact]
public void SlugRelative_AtRoot_ReturnsFullSlug()
{
var ctx = BuildCtx(Note("psychologie/flow", "psychologie"));
Assert.Equal("psychologie/flow", ctx.SlugRelative("psychologie/flow"));
}
// ── NotesInView ──────────────────────────────────────────────────────────
[Fact]
public void NotesInView_InTopic_FiltersCorrectly()
{
var ctx = BuildCtx(
Note("psychologie/a", "psychologie"),
Note("biosysteme/b", "biosysteme"));
ctx.NavigateTo("psychologie");
var notes = ctx.NotesInView().ToList();
Assert.Single(notes);
Assert.Equal("psychologie/a", notes[0].Slug);
}
// ── ResolveSlug ──────────────────────────────────────────────────────────
[Fact]
public void ResolveSlug_ShortName_InTopic_ResolvesToFullSlug()
{
var ctx = BuildCtx(Note("psychologie/flow", "psychologie"));
ctx.NavigateTo("psychologie");
Assert.Equal("psychologie/flow", ctx.ResolveSlug("flow"));
}
[Fact]
public void ResolveSlug_AlreadyFullSlug_ReturnsUnchanged()
{
var ctx = BuildCtx(Note("psychologie/flow", "psychologie"));
Assert.Equal("psychologie/flow", ctx.ResolveSlug("psychologie/flow"));
}
}