noz-cli/NozCli.Tests/SlugGuardTests.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

68 lines
2.8 KiB
C#

using Xunit;
using NozCli.Core.Security;
namespace NozCli.Tests;
public class SlugGuardTests
{
// ── IsValid ──────────────────────────────────────────────────────────────
[Theory]
[InlineData("simple-note", true)]
[InlineData("psychologie/test", true)]
[InlineData("biosysteme/sub/deep", true)]
[InlineData("note123", true)]
[InlineData("a", true)]
public void IsValid_AcceptsValidSlugs(string slug, bool expected)
=> Assert.Equal(expected, SlugGuard.IsValid(slug));
[Theory]
[InlineData("../../etc/passwd", false)] // path traversal
[InlineData("../escape", false)] // path traversal
[InlineData("note/../other", false)] // embedded traversal
[InlineData("UPPERCASE", false)] // uppercase not allowed
[InlineData("has space", false)] // spaces not allowed
[InlineData("", false)] // empty
[InlineData(" ", false)] // whitespace
[InlineData("note//double", false)] // double slash
[InlineData("/leading-slash", false)] // leading slash
[InlineData("trailing/", false)] // trailing slash
[InlineData("Sonderzeichen!", false)] // special chars
public void IsValid_RejectsInvalidSlugs(string slug, bool expected)
=> Assert.Equal(expected, SlugGuard.IsValid(slug));
// ── ToSafePath ───────────────────────────────────────────────────────────
[Fact]
public void ToSafePath_ValidSlug_ReturnsPathInsideBase()
{
var baseDir = Path.Combine(Path.GetTempPath(), "noz-test");
var result = SlugGuard.ToSafePath(baseDir, "psychologie/test");
Assert.NotNull(result);
Assert.StartsWith(baseDir, result);
Assert.EndsWith(".md", result);
Assert.Contains("psychologie", result);
}
[Theory]
[InlineData("../../etc/passwd")]
[InlineData("../escape")]
[InlineData("sub/../../outside")]
public void ToSafePath_TraversalSlug_ReturnsNull(string slug)
{
var baseDir = Path.Combine(Path.GetTempPath(), "noz-test");
Assert.Null(SlugGuard.ToSafePath(baseDir, slug));
}
[Fact]
public void ToSafePath_NestedSlug_BuildsCorrectPath()
{
var baseDir = "/tmp/vault";
var result = SlugGuard.ToSafePath(baseDir, "a/b/c");
Assert.NotNull(result);
var expected = Path.Combine("/tmp/vault", "a", "b", "c") + ".md";
Assert.Equal(expected, result);
}
}