Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ public static class LlmIntentClassifier
{
public static (bool IsActionable, string? ActionIntent) Classify(string message)
{
if (string.IsNullOrWhiteSpace(message))
return (false, null);

var lower = message.ToLowerInvariant();

// Card creation — explicit commands and natural language
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,84 @@ public void Classify_NonActionable_ShouldReturnFalse(string message)

#endregion

#region Edge Cases — Input Extremes

[Fact]
public void Classify_NullInput_ReturnsNotActionable()
{
var (isActionable, actionIntent) = LlmIntentClassifier.Classify(null!);

isActionable.Should().BeFalse();
actionIntent.Should().BeNull();
}
Comment on lines +147 to +154
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

While it's good to document current behavior, a public static method like Classify should ideally be more robust and not throw a NullReferenceException on null input. It would be better to handle null (and whitespace) gracefully by returning (false, null). I recommend updating LlmIntentClassifier.Classify to handle this and changing this test to assert the graceful handling instead of the exception. This prevents potential unhandled exceptions in the application.

    [Fact]
    public void Classify_NullInput_ReturnsNotActionable()
    {
        // A null guard should be in place for public methods.
        var (isActionable, actionIntent) = LlmIntentClassifier.Classify(null!);

        isActionable.Should().BeFalse();
        actionIntent.Should().BeNull();
    }


[Fact]
public void Classify_VeryLongString_ReturnsNotActionable()
{
var longMessage = new string('x', 50_000);

var (isActionable, actionIntent) = LlmIntentClassifier.Classify(longMessage);

isActionable.Should().BeFalse();
actionIntent.Should().BeNull();
}

[Fact]
public void Classify_VeryLongStringContainingPattern_StillMatches()
{
var longMessage = new string('x', 25_000) + " create card for testing " + new string('x', 25_000);

var (isActionable, actionIntent) = LlmIntentClassifier.Classify(longMessage);

isActionable.Should().BeTrue();
actionIntent.Should().Be("card.create");
}

[Theory]
[InlineData(" ")]
[InlineData("\t\t")]
[InlineData("\n\n\n")]
public void Classify_WhitespaceOnly_ReturnsNotActionable(string message)
{
var (isActionable, actionIntent) = LlmIntentClassifier.Classify(message);

isActionable.Should().BeFalse();
actionIntent.Should().BeNull();
}

[Theory]
[InlineData("Hello! @#$%^&*() special chars")]
[InlineData("Unicode: \u00e9\u00e8\u00ea\u00eb\u00fc\u00f6\u00e4")]
[InlineData("<script>alert('xss')</script>")]
[InlineData("SELECT * FROM cards; DROP TABLE boards;")]
public void Classify_SpecialCharacters_WithoutPattern_ReturnsNotActionable(string message)
{
var (isActionable, actionIntent) = LlmIntentClassifier.Classify(message);

isActionable.Should().BeFalse();
actionIntent.Should().BeNull();
}

[Fact]
public void Classify_PatternWithSpecialCharsSurrounding_StillMatches()
{
var (isActionable, actionIntent) = LlmIntentClassifier.Classify("!!! create card !!! @#$ testing");

isActionable.Should().BeTrue();
actionIntent.Should().Be("card.create");
}

[Fact]
public void Classify_PatternWithNewlines_StillMatches()
{
var (isActionable, actionIntent) = LlmIntentClassifier.Classify("line 1\ncreate card for testing\nline 3");

isActionable.Should().BeTrue();
actionIntent.Should().Be("card.create");
}

#endregion

#region Known Gaps — Natural Language Misses (Documents #570/#571)

/// <summary>
Expand Down
Loading