Skip to content

Commit 7d8891b

Browse files
committed
refactor: add tests for InitCommand
1 parent 1130da7 commit 7d8891b

23 files changed

+742
-212
lines changed

src/Application/Bootloader/VariableBootloader.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ final class VariableBootloader extends Bootloader
2424
public function defineSingletons(): array
2525
{
2626
return [
27-
ConfigVariableProvider::class => static fn() => new ConfigVariableProvider(),
27+
ConfigVariableProvider::class => ConfigVariableProvider::class,
2828
VariablesParserPlugin::class => VariablesParserPlugin::class,
2929

3030
VariableProviderInterface::class => static function (
@@ -35,9 +35,12 @@ public function defineSingletons(): array
3535
$envFileName = null;
3636

3737
if ($dirs->getEnvFilePath() !== null) {
38-
$envFilePath = (string) $dirs->getEnvFilePath();
38+
$envFilePath = (string) ($dirs->getEnvFilePath()->isFile() ?
39+
$dirs->getEnvFilePath()->parent() :
40+
$dirs->getEnvFilePath());
3941
$envFileName = $dirs->getEnvFilePath()->name();
4042
}
43+
4144
return new CompositeVariableProvider(
4245
$configVariableProvider,
4346

src/Config/Import/Source/Url/UrlImportSource.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Butschster\ContextGenerator\Lib\HttpClient\HttpClientInterface;
1313
use Butschster\ContextGenerator\Lib\Variable\VariableResolver;
1414
use Psr\Log\LoggerInterface;
15+
use Spiral\Core\Container;
1516
use Symfony\Component\Yaml\Yaml;
1617

1718
/**
@@ -24,7 +25,7 @@ final class UrlImportSource extends AbstractImportSource
2425

2526
public function __construct(
2627
private readonly HttpClientInterface $httpClient,
27-
private readonly VariableResolver $variables,
28+
private readonly Container $container,
2829
?LoggerInterface $logger = null,
2930
) {
3031
parent::__construct($logger);
@@ -61,7 +62,7 @@ public function load(SourceConfigInterface $config): array
6162

6263
try {
6364
$url = $config->url;
64-
$headers = $this->variables->resolve($config->headers);
65+
$headers = $this->container->get(VariableResolver::class)->resolve($config->headers);
6566

6667
$this->logger->debug('Loading URL import', [
6768
'url' => $url,

src/Console/GenerateCommand.php

+85-73
Original file line numberDiff line numberDiff line change
@@ -66,100 +66,112 @@ public function __invoke(Container $container, DirectoriesInterface $dirs): int
6666
->withOutputPath($this->workDir)
6767
->withEnvFile($this->envFileName);
6868

69+
$container->getBinder('root')->bind(
70+
DirectoriesInterface::class,
71+
$dirs,
72+
);
73+
6974
return $container->runScope(
7075
bindings: new Scope(
71-
name: AppScope::Compiler,
7276
bindings: [
7377
DirectoriesInterface::class => $dirs,
7478
],
7579
),
76-
scope: function (
77-
DocumentCompiler $compiler,
78-
ConfigurationProvider $configProvider,
79-
): int {
80-
try {
81-
// Get the appropriate loader based on options provided
82-
if ($this->inlineJson !== null) {
83-
$this->logger->info('Using inline JSON configuration...');
84-
$loader = $configProvider->fromString($this->inlineJson);
85-
} elseif ($this->configPath !== null) {
86-
$this->logger->info(\sprintf('Loading configuration from %s...', $this->configPath));
87-
$loader = $configProvider->fromPath($this->configPath);
88-
} else {
89-
$this->logger->info('Loading configuration from default location...');
90-
$loader = $configProvider->fromDefaultLocation();
91-
}
92-
} catch (ConfigLoaderException $e) {
93-
$this->logger->error('Failed to load configuration', [
94-
'error' => $e->getMessage(),
95-
]);
96-
97-
if ($this->asJson) {
98-
$this->output->writeln(\json_encode([
99-
'status' => 'error',
100-
'message' => 'Failed to load configuration',
80+
scope: fn(Container $container): int => $container->runScope(
81+
bindings: new Scope(
82+
name: AppScope::Compiler,
83+
bindings: [
84+
DirectoriesInterface::class => $dirs,
85+
],
86+
),
87+
scope: function (
88+
DocumentCompiler $compiler,
89+
ConfigurationProvider $configProvider,
90+
): int {
91+
try {
92+
// Get the appropriate loader based on options provided
93+
if ($this->inlineJson !== null) {
94+
$this->logger->info('Using inline JSON configuration...');
95+
$loader = $configProvider->fromString($this->inlineJson);
96+
} elseif ($this->configPath !== null) {
97+
$this->logger->info(\sprintf('Loading configuration from %s...', $this->configPath));
98+
$loader = $configProvider->fromPath($this->configPath);
99+
} else {
100+
$this->logger->info('Loading configuration from default location...');
101+
$loader = $configProvider->fromDefaultLocation();
102+
}
103+
} catch (ConfigLoaderException $e) {
104+
$this->logger->error('Failed to load configuration', [
101105
'error' => $e->getMessage(),
102-
]));
103-
} else {
104-
$this->output->error(\sprintf('Failed to load configuration: %s', $e->getMessage()));
106+
]);
107+
108+
if ($this->asJson) {
109+
$this->output->writeln(\json_encode([
110+
'status' => 'error',
111+
'message' => 'Failed to load configuration',
112+
'error' => $e->getMessage(),
113+
]));
114+
} else {
115+
$this->output->error(\sprintf('Failed to load configuration: %s', $e->getMessage()));
116+
}
117+
118+
return Command::FAILURE;
105119
}
106120

107-
return Command::FAILURE;
108-
}
121+
// Create the renderer for consistent output formatting
122+
$renderer = new GenerateCommandRenderer($this->output);
123+
124+
// Display summary header
125+
$this->output->writeln('');
109126

110-
// Create the renderer for consistent output formatting
111-
$renderer = new GenerateCommandRenderer($this->output);
127+
$config = new ConfigRegistryAccessor($loader->load());
112128

113-
// Display summary header
114-
$this->output->writeln('');
129+
$imports = $config->getImports();
130+
if ($imports !== null) {
131+
$renderer->renderImports($imports);
132+
}
115133

116-
$config = new ConfigRegistryAccessor($loader->load());
134+
if ($config->getDocuments() === null || $config->getDocuments()->getItems() === []) {
135+
if ($this->asJson) {
136+
$this->output->writeln(\json_encode([
137+
'status' => 'success',
138+
'message' => 'No documents found in configuration.',
139+
]));
140+
} else {
141+
$this->output->warning('No documents found in configuration.');
142+
}
143+
return Command::SUCCESS;
144+
}
117145

118-
$imports = $config->getImports();
119-
if ($imports !== null) {
120-
$renderer->renderImports($imports);
121-
}
146+
$result = [];
147+
148+
foreach ($config->getDocuments() as $document) {
149+
$this->logger->info(\sprintf('Compiling %s...', $document->description));
150+
151+
$compiledDocument = $compiler->compile($document);
152+
if (!$this->asJson) {
153+
$renderer->renderCompilationResult($document, $compiledDocument);
154+
} else {
155+
$result[] = [
156+
'output_path' => $compiledDocument->outputPath,
157+
'context_path' => $compiledDocument->contextPath,
158+
];
159+
}
160+
}
122161

123-
if ($config->getDocuments() === null || $config->getDocuments()->getItems() === []) {
124162
if ($this->asJson) {
125163
$this->output->writeln(\json_encode([
126164
'status' => 'success',
127-
'message' => 'No documents found in configuration.',
165+
'message' => 'Documents compiled successfully',
166+
'result' => $result,
128167
]));
129168
} else {
130-
$this->output->warning('No documents found in configuration.');
131-
}
132-
return Command::SUCCESS;
133-
}
134-
135-
$result = [];
136-
137-
foreach ($config->getDocuments() as $document) {
138-
$this->logger->info(\sprintf('Compiling %s...', $document->description));
139-
140-
$compiledDocument = $compiler->compile($document);
141-
if (!$this->asJson) {
142-
$renderer->renderCompilationResult($document, $compiledDocument);
143-
} else {
144-
$result[] = [
145-
'output_path' => $compiledDocument->outputPath,
146-
'context_path' => $compiledDocument->contextPath,
147-
];
169+
$this->output->writeln('');
148170
}
149-
}
150-
151-
if ($this->asJson) {
152-
$this->output->writeln(\json_encode([
153-
'status' => 'success',
154-
'message' => 'Documents compiled successfully',
155-
'result' => $result,
156-
]));
157-
} else {
158-
$this->output->writeln('');
159-
}
160171

161-
return Command::SUCCESS;
162-
},
172+
return Command::SUCCESS;
173+
},
174+
),
163175
);
164176
}
165177
}

src/McpServer/Console/MCPServerCommand.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,15 @@ public function __invoke(
8181
},
8282
);
8383

84-
$container->getBinder('root')->bind(
84+
$binder = $container->getBinder('root');
85+
$binder->bind(
8586
HasPrefixLoggerInterface::class,
8687
$logger,
8788
);
89+
$binder->bind(
90+
DirectoriesInterface::class,
91+
$dirs,
92+
);
8893

8994
$logger->info('Starting MCP server...');
9095

src/McpServer/Prompt/Extension/TemplateResolver.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Mcp\Types\PromptMessage;
1313
use Mcp\Types\TextContent;
1414
use Psr\Log\LoggerInterface;
15+
use Spiral\Core\Container;
1516

1617
/**
1718
* Resolves templates in the inheritance chain.
@@ -21,7 +22,7 @@
2122
{
2223
public function __construct(
2324
private PromptProviderInterface $promptProvider,
24-
private VariableResolver $variableResolver,
25+
private Container $container,
2526
private ?LoggerInterface $logger = null,
2627
) {}
2728

@@ -122,7 +123,9 @@ private function substituteMessages(array $messages, array $arguments): array
122123
$variableProvider = new PromptExtensionVariableProvider($arguments);
123124

124125
// Create a resolver with the variable provider
125-
$resolver = $this->variableResolver->with(new VariableReplacementProcessor($variableProvider, $this->logger));
126+
$resolver = $this->container
127+
->get(VariableResolver::class)
128+
->with(new VariableReplacementProcessor($variableProvider, $this->logger));
126129

127130
foreach ($messages as $message) {
128131
\assert($message->content instanceof TextContent);

src/McpServer/Prompt/PromptRegistry.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Butschster\ContextGenerator\Config\Registry\RegistryInterface;
88
use Butschster\ContextGenerator\McpServer\Prompt\Extension\PromptDefinition;
99
use Spiral\Core\Attribute\Singleton;
10+
use Spiral\Core\Container;
1011

1112
/**
1213
* Registry for storing prompt configurations.
@@ -20,7 +21,7 @@ final class PromptRegistry implements RegistryInterface, PromptProviderInterface
2021
private array $prompts = [];
2122

2223
public function __construct(
23-
private readonly PromptMessageProcessor $promptMessageProcessor,
24+
private readonly Container $container,
2425
) {}
2526

2627
public function register(PromptDefinition $prompt): void
@@ -42,7 +43,7 @@ public function get(string $name, array $arguments = []): PromptDefinition
4243
);
4344
}
4445

45-
return $this->promptMessageProcessor->process($this->prompts[$name], $arguments);
46+
return $this->container->get(PromptMessageProcessor::class)->process($this->prompts[$name], $arguments);
4647
}
4748

4849
public function has(string $name): bool
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
variables:
2+
base_title: "Base Configuration"
3+
base_content: "This content comes from the base configuration"
4+
5+
# This config will be imported by another one
6+
documents:
7+
- description: "Base document"
8+
outputPath: "base.md"
9+
sources:
10+
- type: text
11+
description: "Base content"
12+
content: |
13+
# ${base_title}
14+
15+
${base_content}
16+
tag: "base"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"documents": [
3+
{
4+
"description": "JSON configuration test",
5+
"outputPath": "json-test.md",
6+
"sources": [
7+
{
8+
"type": "text",
9+
"description": "JSON content",
10+
"content": "This content comes from a JSON configuration",
11+
"tag": "json_content"
12+
}
13+
]
14+
}
15+
]
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace dir2;
6+
7+
final readonly class Test2Class {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foo baz
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import:
2+
- path: "tests/fixtures/Console/GenerateCommand/base-config.yaml"
3+
description: "Importing base configuration"
4+
5+
variables:
6+
imported_title: "Imported Configuration"
7+
8+
documents:
9+
- description: "Importing document"
10+
outputPath: "import.md"
11+
sources:
12+
- type: text
13+
description: "Import content"
14+
content: |
15+
# ${imported_title}
16+
17+
This document imports another configuration.
18+
tag: "import"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"documents": [
3+
{
4+
"description": "Inline JSON test",
5+
"outputPath": "inline.md",
6+
"sources": [
7+
{
8+
"type": "text",
9+
"description": "Inline content",
10+
"content": "This content comes from inline JSON",
11+
"tag": "inline_content"
12+
}
13+
]
14+
}
15+
]
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
documents:
2+
- description: "Invalid configuration"
3+
# Missing required outputPath field
4+
sources:
5+
- type: text
6+
description: "Text content"
7+
content: "This is a text source"
8+
tag: "text_source"

0 commit comments

Comments
 (0)