save progress
This commit is contained in:
26
src/Feedser/__Tests/StellaOps.Feedser.Core.Tests/AGENTS.md
Normal file
26
src/Feedser/__Tests/StellaOps.Feedser.Core.Tests/AGENTS.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Feedser Core Tests Agent Charter
|
||||
|
||||
## Mission
|
||||
Validate Feedser patch signature and function extraction behavior with deterministic unit tests.
|
||||
|
||||
## Responsibilities
|
||||
- Maintain test coverage for HunkSig extraction and function signature matching.
|
||||
- Ensure tests are deterministic and avoid time-sensitive assertions.
|
||||
- Keep fixtures minimal and offline-safe.
|
||||
|
||||
## Required Reading
|
||||
- docs/modules/feedser/architecture.md
|
||||
- docs/modules/platform/architecture-overview.md
|
||||
- docs/07_HIGH_LEVEL_ARCHITECTURE.md
|
||||
|
||||
## Definition of Done
|
||||
- Tests cover parsing, normalization, and matching behavior.
|
||||
- Tests remain deterministic across runs and environments.
|
||||
- Failures provide actionable diagnostics.
|
||||
|
||||
## Working Agreement
|
||||
- 1. Update task status to DOING/DONE in the sprint file and local TASKS.md.
|
||||
- 2. Review this charter and required docs before coding.
|
||||
- 3. Avoid DateTimeOffset.UtcNow/Guid.NewGuid in tests unless explicitly controlled.
|
||||
- 4. Keep outputs stable (ordering, timestamps, hashes).
|
||||
- 5. Revert to TODO if paused; capture context in PR notes.
|
||||
@@ -0,0 +1,780 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// FunctionSignatureExtractorTests.cs
|
||||
// Sprint: SPRINT_20251230_001_BE_backport_resolver (BP-507)
|
||||
// Task: Unit tests for function extraction patterns
|
||||
// Description: Tests for C, Go, Python, Rust function signature extraction
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using FluentAssertions;
|
||||
using StellaOps.Feedser.Core;
|
||||
using StellaOps.TestKit;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Feedser.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for function signature extraction from patch context.
|
||||
/// </summary>
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
public sealed class FunctionSignatureExtractorTests
|
||||
{
|
||||
#region C Function Tests (BP-503)
|
||||
|
||||
[Theory]
|
||||
[InlineData("void foo(int x)", "foo", "void foo(int x)")]
|
||||
[InlineData("int main(int argc, char** argv)", "main", "int main(int argc, char** argv)")]
|
||||
[InlineData("static void* helper(void)", "helper", "static void* helper(void)")]
|
||||
[InlineData("unsigned int count_items(const char* s)", "count_items", "unsigned int count_items(const char* s)")]
|
||||
[InlineData("struct Node* create_node(int value)", "create_node", "struct Node* create_node(int value)")]
|
||||
public void ExtractCFunctions_ValidDefinitions_ExtractsCorrectly(
|
||||
string line,
|
||||
string expectedName,
|
||||
string expectedSignature)
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string> { line };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"test.c");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].Name.Should().Be(expectedName);
|
||||
functions[0].Signature.Should().Contain(expectedSignature);
|
||||
functions[0].Language.Should().Be(ProgrammingLanguage.C);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("int result = calculate(x, y);", "calculate")]
|
||||
[InlineData("if (condition) {", "if")]
|
||||
[InlineData("for (int i = 0; i < n; i++) {", "for")]
|
||||
[InlineData("while (running) {", "while")]
|
||||
public void ExtractCFunctions_FalsePositives_NotExtracted(string line, string notExpected)
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string> { line };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"test.c");
|
||||
|
||||
// Assert - should not extract control flow or function calls as definitions
|
||||
functions.Should().NotContain(f => f.Name == notExpected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractCFunctions_SecurityPatch_ExtractsVulnerableFunction()
|
||||
{
|
||||
// Arrange - realistic security patch context
|
||||
var context = new List<string>
|
||||
{
|
||||
"/*",
|
||||
" * Process user input buffer",
|
||||
" */",
|
||||
"static int process_buffer(const char* input, size_t len) {"
|
||||
};
|
||||
var added = new List<string>
|
||||
{
|
||||
" if (len > MAX_BUFFER_SIZE) {",
|
||||
" return -EINVAL;",
|
||||
" }"
|
||||
};
|
||||
var removed = new List<string>
|
||||
{
|
||||
" // No bounds checking - vulnerable!"
|
||||
};
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
context,
|
||||
added,
|
||||
removed,
|
||||
"src/buffer.c");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].Name.Should().Be("process_buffer");
|
||||
functions[0].FilePath.Should().Be("src/buffer.c");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Go Function Tests (BP-504)
|
||||
|
||||
[Theory]
|
||||
[InlineData("func main() {", "main", "func main()")]
|
||||
[InlineData("func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {", "handleRequest", "func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request)")]
|
||||
[InlineData("func Process(data []byte) error {", "Process", "func Process(data []byte)")]
|
||||
[InlineData("func NewClient(addr string) (*Client, error) {", "NewClient", "func NewClient(addr string)")]
|
||||
public void ExtractGoFunctions_ValidDefinitions_ExtractsCorrectly(
|
||||
string line,
|
||||
string expectedName,
|
||||
string expectedSignatureContains)
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string> { line };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"main.go");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].Name.Should().Be(expectedName);
|
||||
functions[0].Signature.Should().Contain(expectedSignatureContains);
|
||||
functions[0].Language.Should().Be(ProgrammingLanguage.Go);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractGoFunctions_WithReceiver_ExtractsReceiver()
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string>
|
||||
{
|
||||
"func (c *Client) Connect(ctx context.Context) error {"
|
||||
};
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"client.go");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].Name.Should().Be("Connect");
|
||||
functions[0].Receiver.Should().Be("c *Client");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Python Function Tests (BP-505)
|
||||
|
||||
[Theory]
|
||||
[InlineData("def foo():", "foo", "def foo()")]
|
||||
[InlineData("def process_data(data: bytes) -> str:", "process_data", "def process_data(data: bytes)")]
|
||||
[InlineData("async def fetch_url(url: str) -> Response:", "fetch_url", "async def fetch_url(url: str)")]
|
||||
[InlineData(" def __init__(self, name: str):", "__init__", "def __init__(self, name: str)")]
|
||||
public void ExtractPythonFunctions_ValidDefinitions_ExtractsCorrectly(
|
||||
string line,
|
||||
string expectedName,
|
||||
string expectedSignatureContains)
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string> { line };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"test.py");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].Name.Should().Be(expectedName);
|
||||
functions[0].Signature.Should().Contain(expectedSignatureContains);
|
||||
functions[0].Language.Should().Be(ProgrammingLanguage.Python);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractPythonFunctions_AsyncFunction_MarkedAsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string> { "async def download(url: str) -> bytes:" };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"downloader.py");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].IsAsync.Should().BeTrue();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rust Function Tests (BP-506)
|
||||
|
||||
[Theory]
|
||||
[InlineData("fn main() {", "main", "fn main()")]
|
||||
[InlineData("pub fn new(size: usize) -> Self {", "new", "pub fn new(size: usize)")]
|
||||
[InlineData("async fn fetch(url: &str) -> Result<Response, Error> {", "fetch", "async fn fetch(url: &str)")]
|
||||
[InlineData("pub(crate) unsafe fn raw_ptr(data: *const u8) -> *mut u8 {", "raw_ptr", "fn raw_ptr(data: *const u8)")]
|
||||
public void ExtractRustFunctions_ValidDefinitions_ExtractsCorrectly(
|
||||
string line,
|
||||
string expectedName,
|
||||
string expectedSignatureContains)
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string> { line };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"lib.rs");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].Name.Should().Be(expectedName);
|
||||
functions[0].Signature.Should().Contain(expectedSignatureContains);
|
||||
functions[0].Language.Should().Be(ProgrammingLanguage.Rust);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractRustFunctions_GenericFunction_ExtractsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string>
|
||||
{
|
||||
"pub fn parse<T: FromStr>(input: &str) -> Result<T, T::Err> {"
|
||||
};
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"parser.rs");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].Name.Should().Be("parse");
|
||||
functions[0].Signature.Should().Contain("<T: FromStr>");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Language Detection Tests
|
||||
|
||||
[Theory]
|
||||
[InlineData("test.c", ProgrammingLanguage.C)]
|
||||
[InlineData("test.h", ProgrammingLanguage.C)]
|
||||
[InlineData("test.cpp", ProgrammingLanguage.Cpp)]
|
||||
[InlineData("test.hpp", ProgrammingLanguage.Cpp)]
|
||||
[InlineData("main.go", ProgrammingLanguage.Go)]
|
||||
[InlineData("script.py", ProgrammingLanguage.Python)]
|
||||
[InlineData("lib.rs", ProgrammingLanguage.Rust)]
|
||||
[InlineData("App.java", ProgrammingLanguage.Java)]
|
||||
[InlineData("index.js", ProgrammingLanguage.JavaScript)]
|
||||
[InlineData("component.ts", ProgrammingLanguage.TypeScript)]
|
||||
[InlineData("unknown.xyz", ProgrammingLanguage.Unknown)]
|
||||
public void DetectLanguage_KnownExtensions_ReturnsCorrectLanguage(
|
||||
string filePath,
|
||||
ProgrammingLanguage expectedLanguage)
|
||||
{
|
||||
// Act
|
||||
var language = FunctionSignatureExtractor.DetectLanguage(filePath);
|
||||
|
||||
// Assert
|
||||
language.Should().Be(expectedLanguage);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Edge Cases
|
||||
|
||||
[Fact]
|
||||
public void ExtractFunctions_EmptyLines_ReturnsEmpty()
|
||||
{
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
"test.c");
|
||||
|
||||
// Assert
|
||||
functions.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractFunctions_UnknownLanguage_ReturnsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string> { "FUNCTION foo()", "END FUNCTION" };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
lines,
|
||||
[],
|
||||
[],
|
||||
"test.fortran"); // Unknown extension
|
||||
|
||||
// Assert
|
||||
functions.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractFunctions_MultipleFunctions_ExtractsAll()
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string>
|
||||
{
|
||||
"def foo():",
|
||||
" pass",
|
||||
"",
|
||||
"def bar(x: int) -> int:",
|
||||
" return x * 2",
|
||||
"",
|
||||
"async def baz():",
|
||||
" await something()"
|
||||
};
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"module.py");
|
||||
|
||||
// Assert
|
||||
functions.Should().HaveCount(3);
|
||||
functions.Select(f => f.Name).Should().Contain("foo", "bar", "baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractFunctions_DuplicateFunctions_DeduplicatesBySignature()
|
||||
{
|
||||
// Arrange - same function in both context and added lines
|
||||
var context = new List<string> { "def process():" };
|
||||
var added = new List<string> { "def process():" };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
context,
|
||||
added,
|
||||
[],
|
||||
"test.py");
|
||||
|
||||
// Assert - should only extract once
|
||||
functions.Should().ContainSingle();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Java Function Tests
|
||||
|
||||
[Theory]
|
||||
[InlineData("public void process(String data) {", "process", "public void process(String data)")]
|
||||
[InlineData("private static int calculate(int x, int y) {", "calculate", "private static int calculate(int x, int y)")]
|
||||
[InlineData("protected List<String> getItems() {", "getItems", "protected List<String> getItems()")]
|
||||
public void ExtractJavaFunctions_ValidDefinitions_ExtractsCorrectly(
|
||||
string line,
|
||||
string expectedName,
|
||||
string expectedSignatureContains)
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string> { line };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"Service.java");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].Name.Should().Be(expectedName);
|
||||
functions[0].Signature.Should().Contain(expectedSignatureContains);
|
||||
functions[0].Language.Should().Be(ProgrammingLanguage.Java);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region JavaScript Function Tests
|
||||
|
||||
[Theory]
|
||||
[InlineData("function handleClick(event) {", "handleClick", "function handleClick(event)")]
|
||||
[InlineData("async function fetchData(url) {", "fetchData", "async function fetchData(url)")]
|
||||
[InlineData("export function validate(input) {", "validate", "function validate(input)")]
|
||||
public void ExtractJavaScriptFunctions_ValidDefinitions_ExtractsCorrectly(
|
||||
string line,
|
||||
string expectedName,
|
||||
string expectedSignatureContains)
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string> { line };
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"app.js");
|
||||
|
||||
// Assert
|
||||
functions.Should().ContainSingle();
|
||||
functions[0].Name.Should().Be(expectedName);
|
||||
functions[0].Signature.Should().Contain(expectedSignatureContains);
|
||||
functions[0].Language.Should().Be(ProgrammingLanguage.JavaScript);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractJavaScriptFunctions_ArrowFunction_ExtractsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var lines = new List<string>
|
||||
{
|
||||
"const processData = (input) => {",
|
||||
"const square = x => x * x;"
|
||||
};
|
||||
|
||||
// Act
|
||||
var functions = FunctionSignatureExtractor.ExtractFunctionsFromContext(
|
||||
[],
|
||||
lines,
|
||||
[],
|
||||
"utils.js");
|
||||
|
||||
// Assert
|
||||
functions.Should().HaveCount(2);
|
||||
functions.Select(f => f.Name).Should().Contain("processData", "square");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fuzzy Function Matching Tests (BP-508)
|
||||
|
||||
[Fact]
|
||||
public void ComputeMatch_IdenticalFunctions_ReturnsScore100()
|
||||
{
|
||||
// Arrange
|
||||
var source = new ExtractedFunction
|
||||
{
|
||||
Name = "process_data",
|
||||
Signature = "int process_data(char* input)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "test.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
var target = new ExtractedFunction
|
||||
{
|
||||
Name = "process_data",
|
||||
Signature = "int process_data(char* input)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "other.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = FunctionMatchingExtensions.ComputeMatch(source, target);
|
||||
|
||||
// Assert
|
||||
result.Score.Should().Be(1.0);
|
||||
result.IsStrongMatch.Should().BeTrue();
|
||||
result.ComponentScores.NameScore.Should().Be(1.0);
|
||||
result.ComponentScores.SignatureScore.Should().Be(1.0);
|
||||
result.ComponentScores.LanguageBonus.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeMatch_SimilarNames_ReturnsHighScore()
|
||||
{
|
||||
// Arrange
|
||||
var source = new ExtractedFunction
|
||||
{
|
||||
Name = "process_data",
|
||||
Signature = "int process_data(char* input)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "test.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
var target = new ExtractedFunction
|
||||
{
|
||||
Name = "processData", // CamelCase variant
|
||||
Signature = "int processData(String input)",
|
||||
Language = ProgrammingLanguage.Java,
|
||||
FilePath = "Test.java",
|
||||
Confidence = 0.85
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = FunctionMatchingExtensions.ComputeMatch(source, target);
|
||||
|
||||
// Assert - names are similar but not identical, different languages
|
||||
result.Score.Should().BeGreaterThan(0.40); // Weak match threshold
|
||||
result.ComponentScores.LanguageBonus.Should().Be(0.0); // Different languages
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeMatch_CompletelyDifferent_ReturnsLowScore()
|
||||
{
|
||||
// Arrange
|
||||
var source = new ExtractedFunction
|
||||
{
|
||||
Name = "calculate_hash",
|
||||
Signature = "uint64_t calculate_hash(const char* data, size_t len)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "hash.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
var target = new ExtractedFunction
|
||||
{
|
||||
Name = "render_widget",
|
||||
Signature = "void render_widget(Widget* w)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "ui.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = FunctionMatchingExtensions.ComputeMatch(source, target);
|
||||
|
||||
// Assert - very different functions
|
||||
result.Score.Should().BeLessThan(0.40);
|
||||
result.IsStrongMatch.Should().BeFalse();
|
||||
result.IsWeakMatch.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeMatch_CAndCppCompatible_GetsLanguageBonus()
|
||||
{
|
||||
// Arrange
|
||||
var source = new ExtractedFunction
|
||||
{
|
||||
Name = "init_buffer",
|
||||
Signature = "void init_buffer(Buffer* buf)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "buffer.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
var target = new ExtractedFunction
|
||||
{
|
||||
Name = "init_buffer",
|
||||
Signature = "void init_buffer(Buffer* buf)",
|
||||
Language = ProgrammingLanguage.Cpp,
|
||||
FilePath = "buffer.cpp",
|
||||
Confidence = 0.90
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = FunctionMatchingExtensions.ComputeMatch(source, target);
|
||||
|
||||
// Assert - C and C++ should be compatible
|
||||
result.Score.Should().Be(1.0);
|
||||
result.ComponentScores.LanguageBonus.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeMatch_JsAndTsCompatible_GetsLanguageBonus()
|
||||
{
|
||||
// Arrange
|
||||
var source = new ExtractedFunction
|
||||
{
|
||||
Name = "handleSubmit",
|
||||
Signature = "function handleSubmit(event)",
|
||||
Language = ProgrammingLanguage.JavaScript,
|
||||
FilePath = "form.js",
|
||||
Confidence = 0.80
|
||||
};
|
||||
var target = new ExtractedFunction
|
||||
{
|
||||
Name = "handleSubmit",
|
||||
Signature = "function handleSubmit(event: Event)",
|
||||
Language = ProgrammingLanguage.TypeScript,
|
||||
FilePath = "form.ts",
|
||||
Confidence = 0.80
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = FunctionMatchingExtensions.ComputeMatch(source, target);
|
||||
|
||||
// Assert - JS and TS should be compatible
|
||||
result.IsStrongMatch.Should().BeTrue();
|
||||
result.ComponentScores.LanguageBonus.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindBestMatch_MultipleCandidates_ReturnsBestMatch()
|
||||
{
|
||||
// Arrange
|
||||
var source = new ExtractedFunction
|
||||
{
|
||||
Name = "parse_config",
|
||||
Signature = "Config* parse_config(const char* path)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "config.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
|
||||
var candidates = new[]
|
||||
{
|
||||
new ExtractedFunction
|
||||
{
|
||||
Name = "render_ui",
|
||||
Signature = "void render_ui()",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "ui.c",
|
||||
Confidence = 0.90
|
||||
},
|
||||
new ExtractedFunction
|
||||
{
|
||||
Name = "parse_config", // Exact match
|
||||
Signature = "Config* parse_config(const char* path)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "config_new.c",
|
||||
Confidence = 0.90
|
||||
},
|
||||
new ExtractedFunction
|
||||
{
|
||||
Name = "parse_json",
|
||||
Signature = "Json* parse_json(const char* str)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "json.c",
|
||||
Confidence = 0.90
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = source.FindBestMatch(candidates);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.Target.Name.Should().Be("parse_config");
|
||||
result.Score.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindBestMatch_NoMatchAboveThreshold_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var source = new ExtractedFunction
|
||||
{
|
||||
Name = "very_unique_function_name",
|
||||
Signature = "void very_unique_function_name()",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "unique.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
|
||||
var candidates = new[]
|
||||
{
|
||||
new ExtractedFunction
|
||||
{
|
||||
Name = "completely_different",
|
||||
Signature = "int completely_different(int x, int y)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "other.c",
|
||||
Confidence = 0.90
|
||||
},
|
||||
new ExtractedFunction
|
||||
{
|
||||
Name = "another_function",
|
||||
Signature = "void another_function(char* s)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "another.c",
|
||||
Confidence = 0.90
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = source.FindBestMatch(candidates, minScore: 0.70);
|
||||
|
||||
// Assert
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindAllMatches_ReturnsMatchesOrderedByScore()
|
||||
{
|
||||
// Arrange
|
||||
var source = new ExtractedFunction
|
||||
{
|
||||
Name = "process",
|
||||
Signature = "void process(Data* d)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "proc.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
|
||||
var candidates = new[]
|
||||
{
|
||||
new ExtractedFunction
|
||||
{
|
||||
Name = "process", // Exact
|
||||
Signature = "void process(Data* d)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "exact.c",
|
||||
Confidence = 0.90
|
||||
},
|
||||
new ExtractedFunction
|
||||
{
|
||||
Name = "processData", // Similar
|
||||
Signature = "void processData(Data* d)",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "similar.c",
|
||||
Confidence = 0.90
|
||||
},
|
||||
new ExtractedFunction
|
||||
{
|
||||
Name = "unrelated",
|
||||
Signature = "int unrelated()",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "unrelated.c",
|
||||
Confidence = 0.90
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var results = source.FindAllMatches(candidates, minScore: 0.30);
|
||||
|
||||
// Assert
|
||||
results.Should().HaveCountGreaterThanOrEqualTo(2);
|
||||
results[0].Target.Name.Should().Be("process"); // Highest score first
|
||||
results[0].Score.Should().BeGreaterThan(results[1].Score);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("processData", "process_data", 0.60)] // Underscore vs CamelCase
|
||||
[InlineData("getData", "get_data", 0.60)]
|
||||
[InlineData("handleClick", "handle_click", 0.60)]
|
||||
public void ComputeMatch_NamingConventionVariants_ReturnsReasonableScore(
|
||||
string name1,
|
||||
string name2,
|
||||
double minExpectedScore)
|
||||
{
|
||||
// Arrange
|
||||
var source = new ExtractedFunction
|
||||
{
|
||||
Name = name1,
|
||||
Signature = $"void {name1}()",
|
||||
Language = ProgrammingLanguage.JavaScript,
|
||||
FilePath = "test.js",
|
||||
Confidence = 0.80
|
||||
};
|
||||
var target = new ExtractedFunction
|
||||
{
|
||||
Name = name2,
|
||||
Signature = $"void {name2}()",
|
||||
Language = ProgrammingLanguage.C,
|
||||
FilePath = "test.c",
|
||||
Confidence = 0.90
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = FunctionMatchingExtensions.ComputeMatch(source, target);
|
||||
|
||||
// Assert - should recognize naming convention variants
|
||||
result.Score.Should().BeGreaterThanOrEqualTo(minExpectedScore);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
10
src/Feedser/__Tests/StellaOps.Feedser.Core.Tests/TASKS.md
Normal file
10
src/Feedser/__Tests/StellaOps.Feedser.Core.Tests/TASKS.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Feedser Core Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0341-M | DONE | Maintainability audit for Feedser.Core.Tests. |
|
||||
| AUDIT-0341-T | DONE | Test coverage audit for Feedser.Core.Tests. |
|
||||
| AUDIT-0341-A | DONE | Waived (test project). |
|
||||
Reference in New Issue
Block a user