Initial commit to new repository
This commit is contained in:
279
src/AxCopilot.Tests/Core/FuzzyEngineTests.cs
Normal file
279
src/AxCopilot.Tests/Core/FuzzyEngineTests.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
using FluentAssertions;
|
||||
using AxCopilot.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Core;
|
||||
|
||||
public class FuzzyEngineTests
|
||||
{
|
||||
// ─── CalculateScore 기본 매칭 ────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void CalculateScore_ExactMatch_ReturnsHighestScore()
|
||||
{
|
||||
var score = FuzzyEngine.CalculateScore("notepad", "notepad", 0);
|
||||
score.Should().BeGreaterThanOrEqualTo(1000);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateScore_PrefixMatch_ReturnsHighScore()
|
||||
{
|
||||
var score = FuzzyEngine.CalculateScore("note", "notepad", 0);
|
||||
score.Should().BeGreaterThanOrEqualTo(800);
|
||||
score.Should().BeLessThan(1000);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateScore_ContainsMatch_ReturnsMediumScore()
|
||||
{
|
||||
var score = FuzzyEngine.CalculateScore("pad", "notepad", 0);
|
||||
score.Should().BeGreaterThanOrEqualTo(600);
|
||||
score.Should().BeLessThan(800);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateScore_NoMatch_ReturnsZero()
|
||||
{
|
||||
var score = FuzzyEngine.CalculateScore("xyz", "notepad", 0);
|
||||
score.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateScore_EmptyQuery_ReturnsZero()
|
||||
{
|
||||
var score = FuzzyEngine.CalculateScore("", "notepad", 0);
|
||||
score.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateScore_BaseScore_AddsToResult()
|
||||
{
|
||||
var scoreWithBase = FuzzyEngine.CalculateScore("notepad", "notepad", 100);
|
||||
var scoreWithout = FuzzyEngine.CalculateScore("notepad", "notepad", 0);
|
||||
scoreWithBase.Should().Be(scoreWithout + 100);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateScore_ExactBeforePrefix()
|
||||
{
|
||||
var exact = FuzzyEngine.CalculateScore("note", "note", 0);
|
||||
var prefix = FuzzyEngine.CalculateScore("not", "note", 0);
|
||||
exact.Should().BeGreaterThan(prefix);
|
||||
}
|
||||
|
||||
// ─── FuzzyMatch ──────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void FuzzyMatch_AllCharsPresent_ReturnsPositive()
|
||||
{
|
||||
var score = FuzzyEngine.FuzzyMatch("ntpd", "notepad");
|
||||
score.Should().BePositive();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuzzyMatch_CharsMissing_ReturnsZero()
|
||||
{
|
||||
var score = FuzzyEngine.FuzzyMatch("xyz", "notepad");
|
||||
score.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuzzyMatch_ConsecutiveCharsScoreHigher()
|
||||
{
|
||||
var consecutive = FuzzyEngine.FuzzyMatch("not", "notepad");
|
||||
var scattered = FuzzyEngine.FuzzyMatch("ntp", "notepad");
|
||||
consecutive.Should().BeGreaterThan(scattered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuzzyMatch_EmptyQuery_ReturnsPositive()
|
||||
{
|
||||
var score = FuzzyEngine.FuzzyMatch("", "notepad");
|
||||
score.Should().BeGreaterThanOrEqualTo(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuzzyMatch_FullMatch_ReturnsHighScore()
|
||||
{
|
||||
var full = FuzzyEngine.FuzzyMatch("abcde", "abcde");
|
||||
var partial = FuzzyEngine.FuzzyMatch("ace", "abcde");
|
||||
full.Should().BeGreaterThan(partial);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuzzyMatch_MinimumScoreGuaranteed()
|
||||
{
|
||||
var score = FuzzyEngine.FuzzyMatch("ntpd", "notepad");
|
||||
score.Should().BeGreaterThanOrEqualTo(50);
|
||||
}
|
||||
|
||||
// ─── 한글 자모 분리 ─────────────────────────────────────────────────────
|
||||
|
||||
[Theory]
|
||||
[InlineData("가", "ㄱㅏ")]
|
||||
[InlineData("한", "ㅎㅏㄴ")]
|
||||
[InlineData("글", "ㄱㅡㄹ")]
|
||||
[InlineData("abc", "abc")]
|
||||
[InlineData("가a나", "ㄱㅏaㄴㅏ")]
|
||||
public void DecomposeToJamo_ReturnsCorrectJamo(string input, string expected)
|
||||
{
|
||||
FuzzyEngine.DecomposeToJamo(input).Should().Be(expected);
|
||||
}
|
||||
|
||||
// ─── 자모 기반 포함 검색 ────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void JamoContainsScore_MiddleWord_ReturnsPositive()
|
||||
{
|
||||
// "모장" → "메모장" (자모 분리 후 연속 매칭)
|
||||
var score = FuzzyEngine.JamoContainsScore("메모장", "모장");
|
||||
score.Should().BePositive();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JamoContainsScore_NoMatch_ReturnsZero()
|
||||
{
|
||||
var score = FuzzyEngine.JamoContainsScore("메모장", "가나");
|
||||
score.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JamoContainsScore_SubsequenceMatch_ReturnsPositive()
|
||||
{
|
||||
// "메장" → 메-모-장에서 비연속 자모 매칭
|
||||
var score = FuzzyEngine.JamoContainsScore("메모장", "메장");
|
||||
score.Should().BePositive();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JamoContainsScore_NonKorean_ReturnsZero()
|
||||
{
|
||||
var score = FuzzyEngine.JamoContainsScore("notepad", "pad");
|
||||
score.Should().Be(0); // 영어는 Contains에서 이미 처리
|
||||
}
|
||||
|
||||
// ─── 한글 초성 검색 ──────────────────────────────────────────────────────
|
||||
|
||||
[Theory]
|
||||
[InlineData("ㄴ", true)]
|
||||
[InlineData("ㄴㅌ", true)]
|
||||
[InlineData("ㄱㄴㄷ", true)]
|
||||
public void IsChosung_ValidChosung_ReturnsTrue(string text, bool expected)
|
||||
{
|
||||
FuzzyEngine.IsChosung(text).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("notepad", false)]
|
||||
[InlineData("노트패드", false)]
|
||||
[InlineData("a", false)]
|
||||
[InlineData("ㄴa", false)]
|
||||
public void IsChosung_NonChosung_ReturnsFalse(string text, bool expected)
|
||||
{
|
||||
FuzzyEngine.IsChosung(text).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetChosung_HangulChar_ReturnsCorrectChosung()
|
||||
{
|
||||
FuzzyEngine.GetChosung('나').Should().Be('ㄴ');
|
||||
FuzzyEngine.GetChosung('가').Should().Be('ㄱ');
|
||||
FuzzyEngine.GetChosung('하').Should().Be('ㅎ');
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetChosung_NonHangul_ReturnsNull()
|
||||
{
|
||||
FuzzyEngine.GetChosung('a').Should().Be('\0');
|
||||
FuzzyEngine.GetChosung('1').Should().Be('\0');
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContainsChosung_ConsecutiveMatch_ReturnsTrue()
|
||||
{
|
||||
FuzzyEngine.ContainsChosung("노트패드", "ㄴㅌ").Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContainsChosung_NonMatchingChosung_ReturnsFalse()
|
||||
{
|
||||
FuzzyEngine.ContainsChosung("노트패드", "ㅅ").Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContainsChosung_PartialMatch_ReturnsTrue()
|
||||
{
|
||||
FuzzyEngine.ContainsChosung("계산기", "ㄱㅅ").Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContainsChosung_QueryLongerThanTarget_ReturnsFalse()
|
||||
{
|
||||
FuzzyEngine.ContainsChosung("가", "ㄱㄴㄷㄹ").Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContainsChosung_NonConsecutive_ReturnsTrue()
|
||||
{
|
||||
// "ㅁㅊ" → 메모장(ㅁㅁㅈ) — 안 맞음 (ㅊ가 없으므로)
|
||||
FuzzyEngine.ContainsChosung("메모장", "ㅁㅊ").Should().BeFalse();
|
||||
|
||||
// "ㅁㅈ" → 메모장(ㅁㅁㅈ) — 비연속 매칭
|
||||
FuzzyEngine.ContainsChosung("메모장", "ㅁㅈ").Should().BeTrue();
|
||||
}
|
||||
|
||||
// ─── 초성 점수 매칭 ─────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void ChosungMatchScore_PureChosung_Consecutive()
|
||||
{
|
||||
var score = FuzzyEngine.ChosungMatchScore("계산기", "ㄱㅅ");
|
||||
score.Should().BeGreaterThanOrEqualTo(500);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChosungMatchScore_PureChosung_Subsequence()
|
||||
{
|
||||
// "ㅁㅈ" → 메모장 (ㅁ...ㅈ 비연속)
|
||||
var score = FuzzyEngine.ChosungMatchScore("메모장", "ㅁㅈ");
|
||||
score.Should().BePositive();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChosungMatchScore_MixedQuery()
|
||||
{
|
||||
// "ㅁ장" → 혼합: ㅁ은 초성, 장은 완성형
|
||||
var score = FuzzyEngine.ChosungMatchScore("메모장", "ㅁ장");
|
||||
score.Should().BePositive();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChosungMatchScore_NoMatch_ReturnsZero()
|
||||
{
|
||||
var score = FuzzyEngine.ChosungMatchScore("메모장", "ㅋㅋ");
|
||||
score.Should().Be(0);
|
||||
}
|
||||
|
||||
// ─── 통합 점수 우선순위 ─────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void CalculateScore_ScoreHierarchy()
|
||||
{
|
||||
// 정확 > 시작 > 포함 > 자모포함 > 초성 > fuzzy
|
||||
var exact = FuzzyEngine.CalculateScore("메모장", "메모장", 0);
|
||||
var prefix = FuzzyEngine.CalculateScore("메모", "메모장", 0);
|
||||
var contains = FuzzyEngine.CalculateScore("모장", "메모장", 0);
|
||||
|
||||
exact.Should().BeGreaterThan(prefix);
|
||||
prefix.Should().BeGreaterThan(contains);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateScore_JamoBeforeChosung()
|
||||
{
|
||||
// "모장" (자모 포함) > "ㅁㅈ" (초성 비연속)
|
||||
var jamo = FuzzyEngine.CalculateScore("모장", "메모장", 0);
|
||||
var chosung = FuzzyEngine.CalculateScore("ㅁㅈ", "메모장", 0);
|
||||
jamo.Should().BeGreaterThan(chosung);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user