save progress

This commit is contained in:
StellaOps Bot
2026-01-02 21:06:27 +02:00
parent f46bde5575
commit 3f197814c5
441 changed files with 21545 additions and 4306 deletions

View 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.

View File

@@ -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
}

View 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). |