teamsiso/src/tests/TeamsISO.App.Tests/Services/OutputNameTemplateTests.cs

108 lines
4 KiB
C#

using FluentAssertions;
using TeamsISO.App.Services;
namespace TeamsISO.App.Tests.Services;
/// <summary>
/// Token-expansion + sanitization tests for <see cref="OutputNameTemplate"/>.
/// We don't touch <see cref="OutputNameTemplate.Get"/> / <see cref="OutputNameTemplate.Set"/>
/// here because those round-trip through %LOCALAPPDATA% file IO; the file-IO
/// path is exercised at integration test time. The token expander is pure and
/// easy to cover.
/// </summary>
public class OutputNameTemplateTests
{
private static readonly Guid TestId = new("11223344-5566-7788-99aa-bbccddeeff00");
[Fact]
public void Render_DefaultTemplate_ProducesGuidPrefix()
{
var name = OutputNameTemplate.Render(OutputNameTemplate.DefaultTemplate, TestId, "Jane");
// Default is "TEAMSISO_{guid}" → first 8 hex of TestId, uppercase.
name.Should().Be("TEAMSISO_11223344");
}
[Fact]
public void Render_NameToken_UsesSanitizedDisplayName()
{
var name = OutputNameTemplate.Render("TEAMSISO_{name}", TestId, "Jane Doe");
name.Should().Be("TEAMSISO_Jane_Doe", because: "spaces become underscores in the sanitizer");
}
[Fact]
public void Render_NameToken_StripsSpecialCharacters()
{
// NDI accepts more chars than we allow, but we keep it conservative
// (alphanumeric + underscore + hyphen + period). Anything else is dropped
// or converted to underscore (whitespace).
var name = OutputNameTemplate.Render("{name}", TestId, "Jane (PM) — Lead!");
// Expect: "Jane_PM__Lead" — parens dropped, em-dash dropped, exclamation dropped.
// Whitespace runs collapse into adjacent underscores.
name.Should().NotContain("(");
name.Should().NotContain(")");
name.Should().NotContain("—");
name.Should().NotContain("!");
name.Should().Contain("Jane");
name.Should().Contain("Lead");
}
[Fact]
public void Render_GuidToken_IsUppercaseFirst8()
{
var name = OutputNameTemplate.Render("{guid}", TestId, "Jane");
name.Should().Be("11223344");
}
[Fact]
public void Render_MachineToken_UsesEnvironmentMachineName()
{
var name = OutputNameTemplate.Render("{machine}", TestId, "Jane");
// Sanitization may transform spaces in machine names, so just assert non-empty
// and that it contains the machine name's alphanumeric-ish chars.
name.Should().NotBeNullOrEmpty();
// MachineName itself is sanitized in render — equality check would be brittle.
}
[Fact]
public void Render_TimestampToken_HasExpectedShape()
{
var name = OutputNameTemplate.Render("session_{timestamp}", TestId, "Jane");
// yyyyMMdd_HHmmss is 15 chars + underscore separator = 16.
// Combined with "session_" prefix → length should be at least 23.
name.Should().StartWith("session_");
name.Length.Should().BeGreaterThan("session_".Length + 14);
}
[Fact]
public void Render_MultipleTokens_AllExpand()
{
var name = OutputNameTemplate.Render("{name}_{guid}_{machine}", TestId, "Jane");
name.Should().StartWith("Jane_11223344_");
name.Should().NotContain("{");
name.Should().NotContain("}");
}
[Fact]
public void Render_TemplateWithNoTokens_PassesThrough()
{
var name = OutputNameTemplate.Render("STATIC_NAME", TestId, "Jane");
name.Should().Be("STATIC_NAME");
}
[Fact]
public void Render_EmptyDisplayName_DegradesToEmptyToken()
{
var name = OutputNameTemplate.Render("PFX_{name}", TestId, "");
name.Should().Be("PFX_");
}
[Theory]
[InlineData("Jane123")]
[InlineData("Jane-Doe")]
[InlineData("Jane.PM")]
public void Render_AllowedCharactersPreserved(string displayName)
{
var name = OutputNameTemplate.Render("{name}", TestId, displayName);
name.Should().Be(displayName, because: "alphanumeric, underscore, hyphen, period are all valid NDI chars");
}
}