Skip to content

Commit da03d32

Browse files
committed
updated tests.
1 parent f09971c commit da03d32

19 files changed

Lines changed: 2360 additions & 62 deletions

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ composer.lock
2828
.phpunit.cache
2929
examples/test.log
3030

31-
coverage.xml
31+
coverage*

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
},
2626
"autoload-dev": {
2727
"psr-4": {
28-
"Tests\\": "tests/"
28+
"Tests\\": "tests/",
29+
"Neuron\\Core\\": "../core/src/Core/"
2930
}
3031
},
3132
"bin": [

src/Cli/Commands/Core/VersionCommand.php

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,24 @@
33
namespace Neuron\Cli\Commands\Core;
44

55
use Neuron\Cli\Commands\Command;
6+
use Neuron\Core\System\IFileSystem;
7+
use Neuron\Core\System\RealFileSystem;
68

79
/**
810
* Version command - displays version information for Neuron components
911
*/
1012
class VersionCommand extends Command
1113
{
14+
private IFileSystem $fs;
15+
16+
/**
17+
* @param IFileSystem|null $fs File system implementation (null = use real file system)
18+
*/
19+
public function __construct( ?IFileSystem $fs = null )
20+
{
21+
$this->fs = $fs ?? new RealFileSystem();
22+
}
23+
1224
/**
1325
* @inheritDoc
1426
*/
@@ -159,57 +171,68 @@ private function showComponentVersion( string $componentName ): void
159171

160172
/**
161173
* Get the CLI component version
162-
*
174+
*
163175
* @return string
164176
*/
165177
private function getCliVersion(): string
166178
{
167179
$composerJson = dirname( __DIR__, 4 ) . '/composer.json';
168-
169-
if( file_exists( $composerJson ) )
180+
181+
if( $this->fs->fileExists( $composerJson ) )
170182
{
171-
$composer = json_decode( file_get_contents( $composerJson ), true );
172-
return $composer['version'] ?? '0.1.0';
183+
$content = $this->fs->readFile( $composerJson );
184+
if( $content !== false )
185+
{
186+
$composer = json_decode( $content, true );
187+
return $composer['version'] ?? '0.1.0';
188+
}
173189
}
174-
190+
175191
return '0.1.0';
176192
}
177193

178194
/**
179195
* Get information about installed Neuron components
180-
*
196+
*
181197
* @return array
182198
*/
183199
private function getInstalledComponents(): array
184200
{
185201
$components = [];
186-
202+
187203
// Find vendor directory
188204
$vendorDir = $this->findVendorDirectory();
189-
205+
190206
if( !$vendorDir )
191207
{
192208
return $components;
193209
}
194-
210+
195211
// Load from composer's installed.json
196212
$installedJson = $vendorDir . '/composer/installed.json';
197-
198-
if( !file_exists( $installedJson ) )
213+
214+
if( !$this->fs->fileExists( $installedJson ) )
199215
{
200216
return $components;
201217
}
202-
203-
$installed = json_decode( file_get_contents( $installedJson ), true );
204-
218+
219+
$content = $this->fs->readFile( $installedJson );
220+
221+
if( $content === false )
222+
{
223+
return $components;
224+
}
225+
226+
$installed = json_decode( $content, true );
227+
205228
if( !$installed )
206229
{
207230
return $components;
208231
}
209-
232+
210233
// Handle both composer 1.x and 2.x formats
211234
$packages = isset( $installed['packages'] ) ? $installed['packages'] : $installed;
212-
235+
213236
foreach( $packages as $package )
214237
{
215238
// Only include neuron-php packages
@@ -218,35 +241,37 @@ private function getInstalledComponents(): array
218241
$components[$package['name']] = $package;
219242
}
220243
}
221-
244+
222245
// Sort by name
223246
ksort( $components );
224-
247+
225248
return $components;
226249
}
227250

228251
/**
229252
* Find the vendor directory
230-
*
253+
*
231254
* @return string|null
232255
*/
233256
private function findVendorDirectory(): ?string
234257
{
258+
$cwd = $this->fs->getcwd();
259+
235260
$locations = [
236261
dirname( __DIR__, 4 ) . '/vendor',
237262
dirname( __DIR__, 5 ) . '/vendor',
238-
getcwd() . '/vendor',
263+
$cwd . '/vendor',
239264
];
240-
265+
241266
foreach( $locations as $location )
242267
{
243-
$path = realpath( $location );
244-
if( $path && is_dir( $path ) )
268+
$path = $this->fs->realpath( $location );
269+
if( $path && $this->fs->isDir( $path ) )
245270
{
246271
return $path;
247272
}
248273
}
249-
274+
250275
return null;
251276
}
252277
}

src/Cli/Console/Input.php

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@ class Input
1414
private array $arguments = [];
1515
private array $options = [];
1616
private array $rawArguments = [];
17-
17+
private StreamInterface $stream;
18+
1819
/**
1920
* @param array $argv Command-line arguments (without script name)
21+
* @param StreamInterface|null $stream Stream for interactive I/O (null = use standard streams)
2022
*/
21-
public function __construct( array $argv = [] )
23+
public function __construct( array $argv = [], ?StreamInterface $stream = null )
2224
{
2325
$this->argv = $argv;
26+
$this->stream = $stream ?? new StandardStream();
2427
$this->parseRaw();
2528
}
2629

@@ -239,28 +242,28 @@ public function getArgv(): array
239242

240243
/**
241244
* Check if input is interactive (connected to a TTY)
242-
*
245+
*
243246
* @return bool
244247
*/
245248
public function isInteractive(): bool
246249
{
247-
return posix_isatty( STDIN );
250+
return $this->stream->isInteractive();
248251
}
249252

250253
/**
251254
* Read a line from standard input
252-
*
255+
*
253256
* @param string $prompt Optional prompt to display
254257
* @return string|false
255258
*/
256259
public function readLine( string $prompt = '' ): string|false
257260
{
258261
if( $prompt )
259262
{
260-
echo $prompt;
263+
$this->stream->write( $prompt );
261264
}
262-
263-
return fgets( STDIN );
265+
266+
return $this->stream->read();
264267
}
265268

266269
/**
@@ -386,8 +389,8 @@ public function choice( string $question, array $choices, mixed $default = null,
386389
}
387390

388391
// Display the question
389-
echo $question . PHP_EOL;
390-
392+
$this->stream->write( $question . PHP_EOL );
393+
391394
// Display choices
392395
$index = 1;
393396
$indexMap = [];
@@ -396,7 +399,7 @@ public function choice( string $question, array $choices, mixed $default = null,
396399
$indexMap[$index] = $key;
397400
$isDefault = ( $default !== null && ( $key === $default || $value === $default ) );
398401
$marker = $isDefault ? ' (default)' : '';
399-
echo " [{$index}] {$value}{$marker}" . PHP_EOL;
402+
$this->stream->write( " [{$index}] {$value}{$marker}" . PHP_EOL );
400403
$index++;
401404
}
402405

@@ -448,7 +451,7 @@ public function choice( string $question, array $choices, mixed $default = null,
448451
if( $choice === null && $default === null )
449452
{
450453
// Invalid choice and no default, ask again
451-
echo "Invalid choice. Please try again." . PHP_EOL;
454+
$this->stream->write( "Invalid choice. Please try again." . PHP_EOL );
452455
return $this->choice( $question, $choices, $default, $allowMultiple );
453456
}
454457

src/Cli/Console/StandardStream.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Neuron\Cli\Console;
4+
5+
/**
6+
* Standard stream implementation using real STDIN/STDOUT.
7+
*
8+
* This is the production implementation that wraps PHP's standard input/output streams.
9+
* Uses STDIN for reading and STDOUT for writing, providing real terminal interaction.
10+
*/
11+
class StandardStream implements StreamInterface
12+
{
13+
/**
14+
* @param resource $inputStream Input stream resource (default: STDIN)
15+
* @param resource $outputStream Output stream resource (default: STDOUT)
16+
*/
17+
public function __construct(
18+
private mixed $inputStream = STDIN,
19+
private mixed $outputStream = STDOUT
20+
) {
21+
}
22+
23+
/**
24+
* @inheritDoc
25+
*/
26+
public function read(): string|false
27+
{
28+
return fgets( $this->inputStream );
29+
}
30+
31+
/**
32+
* @inheritDoc
33+
*/
34+
public function write( string $data ): void
35+
{
36+
fwrite( $this->outputStream, $data );
37+
}
38+
39+
/**
40+
* @inheritDoc
41+
*/
42+
public function isInteractive(): bool
43+
{
44+
return posix_isatty( $this->inputStream );
45+
}
46+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Neuron\Cli\Console;
4+
5+
/**
6+
* Interface for input/output stream abstraction.
7+
*
8+
* Provides a testable abstraction over STDIN/STDOUT for interactive CLI operations.
9+
* Implementations can be real streams for production or mock streams for testing.
10+
*/
11+
interface StreamInterface
12+
{
13+
/**
14+
* Read a line from the input stream
15+
*
16+
* @return string|false Returns the read line or false on EOF
17+
*/
18+
public function read(): string|false;
19+
20+
/**
21+
* Write data to the output stream
22+
*
23+
* @param string $data Data to write
24+
* @return void
25+
*/
26+
public function write( string $data ): void;
27+
28+
/**
29+
* Check if the stream is connected to an interactive terminal
30+
*
31+
* @return bool True if interactive (TTY), false otherwise
32+
*/
33+
public function isInteractive(): bool;
34+
}

0 commit comments

Comments
 (0)