Add PHP Analyzer Plugin and Composer Lock Data Handling
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implemented the PhpAnalyzerPlugin to analyze PHP projects. - Created ComposerLockData class to represent data from composer.lock files. - Developed ComposerLockReader to load and parse composer.lock files asynchronously. - Introduced ComposerPackage class to encapsulate package details. - Added PhpPackage class to represent PHP packages with metadata and evidence. - Implemented PhpPackageCollector to gather packages from ComposerLockData. - Created PhpLanguageAnalyzer to perform analysis and emit results. - Added capability signals for known PHP frameworks and CMS. - Developed unit tests for the PHP language analyzer and its components. - Included sample composer.lock and expected output for testing. - Updated project files for the new PHP analyzer library and tests.
This commit is contained in:
@@ -749,6 +749,8 @@ public sealed class CommandHandlersTests
|
||||
new[] { "impact", "impact " },
|
||||
forceRefresh: false,
|
||||
timeoutSeconds: 0,
|
||||
outputFormat: AdvisoryOutputFormat.Table,
|
||||
outputPath: null,
|
||||
verbose: false,
|
||||
cancellationToken: CancellationToken.None);
|
||||
|
||||
@@ -777,6 +779,104 @@ public sealed class CommandHandlersTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAdviseRunAsync_WritesMarkdownWithCitations()
|
||||
{
|
||||
var originalExit = Environment.ExitCode;
|
||||
var originalConsole = AnsiConsole.Console;
|
||||
using var tempDir = new TempDirectory();
|
||||
var outputPath = Path.Combine(tempDir.Path, "advisory.md");
|
||||
var testConsole = new TestConsole();
|
||||
|
||||
try
|
||||
{
|
||||
Environment.ExitCode = 0;
|
||||
AnsiConsole.Console = testConsole;
|
||||
|
||||
var planResponse = new AdvisoryPipelinePlanResponseModel
|
||||
{
|
||||
TaskType = AdvisoryAiTaskType.Summary.ToString(),
|
||||
CacheKey = "cache-markdown",
|
||||
PromptTemplate = "prompts/advisory/summary.liquid",
|
||||
Budget = new AdvisoryTaskBudgetModel
|
||||
{
|
||||
PromptTokens = 256,
|
||||
CompletionTokens = 64
|
||||
},
|
||||
Chunks = Array.Empty<PipelineChunkSummaryModel>(),
|
||||
Vectors = Array.Empty<PipelineVectorSummaryModel>(),
|
||||
Metadata = new Dictionary<string, string>()
|
||||
};
|
||||
|
||||
var outputResponse = new AdvisoryPipelineOutputModel
|
||||
{
|
||||
CacheKey = planResponse.CacheKey,
|
||||
TaskType = planResponse.TaskType,
|
||||
Profile = "default",
|
||||
Prompt = "Sanitized prompt",
|
||||
Response = "Rendered summary body.",
|
||||
Citations = new[]
|
||||
{
|
||||
new AdvisoryOutputCitationModel { Index = 1, DocumentId = "doc-9", ChunkId = "chunk-9" }
|
||||
},
|
||||
Metadata = new Dictionary<string, string>(),
|
||||
Guardrail = new AdvisoryOutputGuardrailModel
|
||||
{
|
||||
Blocked = false,
|
||||
SanitizedPrompt = "Sanitized prompt",
|
||||
Violations = Array.Empty<AdvisoryOutputGuardrailViolationModel>(),
|
||||
Metadata = new Dictionary<string, string>()
|
||||
},
|
||||
Provenance = new AdvisoryOutputProvenanceModel
|
||||
{
|
||||
InputDigest = "sha256:markdown-in",
|
||||
OutputHash = "sha256:markdown-out",
|
||||
Signatures = Array.Empty<string>()
|
||||
},
|
||||
GeneratedAtUtc = DateTimeOffset.Parse("2025-11-06T12:00:00Z", CultureInfo.InvariantCulture),
|
||||
PlanFromCache = false
|
||||
};
|
||||
|
||||
var backend = new StubBackendClient(new JobTriggerResult(true, "ok", null, null))
|
||||
{
|
||||
AdvisoryPlanResponse = planResponse,
|
||||
AdvisoryOutputResponse = outputResponse
|
||||
};
|
||||
|
||||
var provider = BuildServiceProvider(backend);
|
||||
|
||||
await CommandHandlers.HandleAdviseRunAsync(
|
||||
provider,
|
||||
AdvisoryAiTaskType.Summary,
|
||||
"ADV-4",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"default",
|
||||
Array.Empty<string>(),
|
||||
forceRefresh: false,
|
||||
timeoutSeconds: 0,
|
||||
outputFormat: AdvisoryOutputFormat.Markdown,
|
||||
outputPath: outputPath,
|
||||
verbose: false,
|
||||
cancellationToken: CancellationToken.None);
|
||||
|
||||
var markdown = await File.ReadAllTextAsync(outputPath);
|
||||
Assert.Contains("Citations", markdown, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("doc-9", markdown, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("chunk-9", markdown, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.True(File.Exists(outputPath));
|
||||
Assert.Contains("Rendered summary body", markdown, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Equal(0, Environment.ExitCode);
|
||||
Assert.Contains("Citations", testConsole.Output, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
finally
|
||||
{
|
||||
AnsiConsole.Console = originalConsole;
|
||||
Environment.ExitCode = originalExit;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAdviseRunAsync_ReturnsGuardrailExitCodeOnBlock()
|
||||
{
|
||||
@@ -855,6 +955,8 @@ public sealed class CommandHandlersTests
|
||||
Array.Empty<string>(),
|
||||
forceRefresh: true,
|
||||
timeoutSeconds: 0,
|
||||
outputFormat: AdvisoryOutputFormat.Table,
|
||||
outputPath: null,
|
||||
verbose: false,
|
||||
cancellationToken: CancellationToken.None);
|
||||
|
||||
@@ -913,6 +1015,8 @@ public sealed class CommandHandlersTests
|
||||
Array.Empty<string>(),
|
||||
forceRefresh: false,
|
||||
timeoutSeconds: 0,
|
||||
outputFormat: AdvisoryOutputFormat.Table,
|
||||
outputPath: null,
|
||||
verbose: false,
|
||||
cancellationToken: CancellationToken.None);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user