Skip to content

Commit ecbc922

Browse files
committed
Tests for debug logger
1 parent 353a5c4 commit ecbc922

14 files changed

+193
-27
lines changed

app/config.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
$globalDir = $c->get('phpschoolGlobalDir');
9999

100100
if ($c->get('debugMode')) {
101-
return new ConsoleLogger($c->get(OutputInterface::class));
101+
return new ConsoleLogger($c->get(OutputInterface::class), $c->get(Color::class));
102102
}
103103

104104
return new Logger("$globalDir/logs/$appName.log");

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"php-school/keylighter": "^0.8.4",
3131
"nikic/php-parser": "^4.0",
3232
"guzzlehttp/guzzle": "^7.2",
33-
"psr/log": "^1.1"
33+
"psr/log": "^1.1",
34+
"ext-json": "*"
3435
},
3536
"require-dev": {
3637
"composer/composer": "^2.0",

src/Application.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,9 @@ public function run(): int
223223
{
224224
$args = $_SERVER['argv'] ?? [];
225225

226-
$debug = array_reduce($args, function (bool $debugEnabled, string $arg) {
227-
return $debugEnabled || $arg === '--debug';
228-
}, false);
226+
$debug = any($args, function (string $arg) {
227+
return $arg === '--debug';
228+
});
229229

230230
$args = array_values(array_filter($args, function (string $arg) {
231231
return $arg !== '--debug';

src/ExerciseRenderer.php

-2
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ public function __construct(
8282
*/
8383
public function __invoke(CliMenu $menu): void
8484
{
85-
$menu->close();
86-
8785
$item = $menu->getSelectedItem();
8886
$exercise = $this->exerciseRepository->findByName($item->getText());
8987
$exercises = $this->exerciseRepository->findAll();

src/Factory/MenuFactory.php

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public function __invoke(ContainerInterface $c): CliMenu
5959
$builder->addItem(
6060
$exercise->getName(),
6161
function (CliMenu $menu) use ($exerciseRenderer, $eventDispatcher, $exercise) {
62+
$menu->close();
6263
$this->dispatchExerciseSelectedEvent($eventDispatcher, $exercise);
6364
$exerciseRenderer->__invoke($menu);
6465
},

src/Logger/ConsoleLogger.php

+19-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpSchool\PhpWorkshop\Logger;
66

7+
use Colors\Color;
78
use PhpSchool\PhpWorkshop\Output\OutputInterface;
89
use Psr\Log\AbstractLogger;
910
use Psr\Log\LoggerInterface;
@@ -15,19 +16,29 @@ class ConsoleLogger extends AbstractLogger implements LoggerInterface
1516
*/
1617
private $output;
1718

18-
public function __construct(OutputInterface $output)
19+
/**
20+
* @var Color
21+
*/
22+
private $color;
23+
24+
public function __construct(OutputInterface $output, Color $color)
1925
{
2026
$this->output = $output;
27+
$this->color = $color;
2128
}
2229

2330
public function log($level, $message, array $context = []): void
2431
{
25-
$this->output->writeLine(sprintf(
26-
"Time: %s, Level: %s, Message: %s, Context: %s",
27-
(new \DateTime())->format('d-m-y H:i:s'),
28-
$level,
29-
$message,
30-
json_encode($context)
31-
));
32+
$parts = [
33+
sprintf(
34+
'%s - %s - %s',
35+
$this->color->fg('yellow', (new \DateTime())->format('H:i:s')),
36+
$this->color->bg('red', strtoupper($level)),
37+
$this->color->fg('red', $message)
38+
),
39+
json_encode($context, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
40+
];
41+
42+
$this->output->writeLine(implode("\n", $parts));
3243
}
3344
}

src/functions.php

+11
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,14 @@ function collect(array $array): Collection
7373
return new Collection($array);
7474
}
7575
}
76+
77+
78+
if (!function_exists('any')) {
79+
80+
function any(array $values, callable $cb): bool
81+
{
82+
return array_reduce($values, function (bool $carry, $value) use ($cb) {
83+
return $carry || $cb($value);
84+
}, false);
85+
}
86+
}

test/ApplicationTest.php

+24-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
use PhpSchool\PhpWorkshopTest\Asset\MockEventDispatcher;
1616
use Psr\Log\LoggerInterface;
1717
use Psr\Log\NullLogger;
18+
use PhpSchool\PhpWorkshop\Logger\ConsoleLogger;
19+
use PhpSchool\PhpWorkshop\Logger\Logger;
20+
use PHPUnit\Framework\TestCase;
1821

1922
class ApplicationTest extends BaseTest
2023
{
@@ -53,7 +56,7 @@ public function testEventListenersFromLocalAndWorkshopConfigAreMerged(): void
5356
$rp->setAccessible(true);
5457
$rp->setValue($app, $frameworkFile);
5558

56-
$container = $rm->invoke($app);
59+
$container = $rm->invoke($app, false);
5760

5861
$eventListeners = $container->get('eventListeners');
5962

@@ -162,8 +165,26 @@ public function testConfigureReturnsSameContainerInstance(): void
162165
self::assertSame($application->configure(), $application->configure());
163166
}
164167

165-
public function tearDown(): void
168+
public function testDebugFlagSwitchesLoggerToConsoleLogger(): void
166169
{
167-
parent::tearDown();
170+
$app = new Application('My workshop', __DIR__ . '/../app/config.php');
171+
172+
$frameworkFile = sprintf('%s/%s', sys_get_temp_dir(), uniqid($this->getName(), true));
173+
file_put_contents($frameworkFile, '<?php return []; ');
174+
175+
$rp = new \ReflectionProperty(Application::class, 'frameworkConfigLocation');
176+
$rp->setAccessible(true);
177+
$rp->setValue($app, $frameworkFile);
178+
179+
$rm = new \ReflectionMethod($app, 'getContainer');
180+
$rm->setAccessible(true);
181+
182+
$container = $rm->invoke($app, true);
183+
184+
$container->set('phpschoolGlobalDir', $this->getTemporaryDirectory());
185+
$container->set('appName', 'my-workshop');
186+
187+
$logger = $container->get(LoggerInterface::class);
188+
self::assertInstanceOf(ConsoleLogger::class, $logger);
168189
}
169190
}

test/BaseTest.php

+2-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@ abstract class BaseTest extends TestCase
1616
public function getTemporaryDirectory(): string
1717
{
1818
if (!$this->tempDirectory) {
19-
$tempDirectory = System::tempDir($this->getName());
20-
mkdir($tempDirectory, 0777, true);
21-
22-
$this->tempDirectory = realpath($tempDirectory);
19+
$this->tempDirectory = System::tempDir($this->getName());
20+
mkdir($this->tempDirectory, 0777, true);
2321
}
2422

2523
return $this->tempDirectory;

test/ExerciseRendererTest.php

-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ public function testExerciseRendererSetsCurrentExerciseAndRendersExercise(): voi
3333
->method('getSelectedItem')
3434
->willReturn($item);
3535

36-
$menu
37-
->expects($this->once())
38-
->method('close');
39-
4036
$exercise1 = $this->createMock(ExerciseInterface::class);
4137
$exercise2 = $this->createMock(ExerciseInterface::class);
4238
$exercises = [$exercise1, $exercise2];

test/Factory/MenuFactoryTest.php

+81-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace PhpSchool\PhpWorkshopTest\Factory;
44

5+
use PhpSchool\CliMenu\MenuItem\SelectableItem;
6+
use PhpSchool\PhpWorkshop\Event\EventInterface;
57
use Psr\Container\ContainerInterface;
68
use PhpSchool\CliMenu\CliMenu;
79
use PhpSchool\PhpWorkshop\Command\CreditsCommand;
@@ -68,6 +70,84 @@ public function testFactoryReturnsInstance(): void
6870

6971

7072
$factory = new MenuFactory();
71-
$this->assertInstanceOf(CliMenu::class, $factory($container));
73+
74+
$factory($container);
75+
}
76+
77+
public function testSelectExercise(): void
78+
{
79+
$container = $this->createMock(ContainerInterface::class);
80+
$userStateSerializer = $this->createMock(UserStateSerializer::class);
81+
$userStateSerializer
82+
->expects($this->once())
83+
->method('deSerialize')
84+
->willReturn(new UserState());
85+
86+
$exerciseRepository = $this->createMock(ExerciseRepository::class);
87+
$exercise = $this->createMock(ExerciseInterface::class);
88+
$exercise
89+
->method('getName')
90+
->willReturn('Exercise');
91+
$exerciseRepository
92+
->expects($this->once())
93+
->method('findAll')
94+
->willReturn([$exercise]);
95+
96+
$terminal = $this->createMock(Terminal::class);
97+
$terminal
98+
->method('getWidth')
99+
->willReturn(70);
100+
101+
$eventDispatcher = $this->createMock(EventDispatcher::class);
102+
$eventDispatcher
103+
->expects(self::exactly(2))
104+
->method('dispatch')
105+
->withConsecutive(
106+
[
107+
self::callback(function ($event) {
108+
return $event instanceof EventInterface && $event->getName() === 'exercise.selected';
109+
})
110+
],
111+
[
112+
self::callback(function ($event) {
113+
return $event instanceof EventInterface && $event->getName() === 'exercise.selected.exercise';
114+
})
115+
]
116+
);
117+
118+
$exerciseRenderer = $this->createMock(ExerciseRenderer::class);
119+
$exerciseRenderer->expects(self::once())
120+
->method('__invoke')
121+
->with(self::isInstanceOf(CliMenu::class));
122+
123+
$services = [
124+
UserStateSerializer::class => $userStateSerializer,
125+
ExerciseRepository::class => $exerciseRepository,
126+
ExerciseRenderer::class => $exerciseRenderer,
127+
HelpCommand::class => $this->createMock(HelpCommand::class),
128+
CreditsCommand::class => $this->createMock(CreditsCommand::class),
129+
ResetProgress::class => $this->createMock(ResetProgress::class),
130+
'workshopLogo' => 'LOGO',
131+
'bgColour' => 'black',
132+
'fgColour' => 'green',
133+
'workshopTitle' => 'TITLE',
134+
WorkshopType::class => WorkshopType::STANDARD(),
135+
EventDispatcher::class => $eventDispatcher,
136+
Terminal::class => $terminal
137+
];
138+
139+
$container
140+
->method('get')
141+
->willReturnCallback(function ($name) use ($services) {
142+
return $services[$name];
143+
});
144+
145+
146+
$factory = new MenuFactory();
147+
148+
$menu = $factory($container);
149+
150+
$firstExercise = $menu->getItemByIndex(6);
151+
$menu->executeAsSelected($firstExercise);
72152
}
73153
}

test/FunctionsTest.php

+11
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,15 @@ public function camelCaseToKebabCaseProvider(): array
4040
]
4141
];
4242
}
43+
44+
public function testAny(): void
45+
{
46+
self::assertEquals(true, any([1, 2, 3, 10, 11], function (int $num) {
47+
return $num > 10;
48+
}));
49+
50+
self::assertEquals(false, any([1, 2, 3, 10, 11], function (int $num) {
51+
return $num > 11;
52+
}));
53+
}
4354
}

test/Logger/ConsoleLoggerTest.php

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshopTest\Logger;
4+
5+
use PhpSchool\CliMenu\Util\StringUtil;
6+
use PhpSchool\PhpWorkshop\Logger\ConsoleLogger;
7+
use PhpSchool\PhpWorkshop\Utils\StringUtils;
8+
use PhpSchool\PhpWorkshopTest\ContainerAwareTest;
9+
use Psr\Log\LoggerInterface;
10+
11+
class ConsoleLoggerTest extends ContainerAwareTest
12+
{
13+
public function setUp(): void
14+
{
15+
parent::setUp();
16+
17+
$this->container->set('phpschoolGlobalDir', $this->getTemporaryDirectory());
18+
$this->container->set('appName', 'my-workshop');
19+
$this->container->set('debugMode', true);
20+
}
21+
22+
public function testConsoleLoggerIsCreatedIfDebugModeEnable(): void
23+
{
24+
$this->assertInstanceOf(ConsoleLogger::class, $this->container->get(LoggerInterface::class));
25+
}
26+
27+
public function testLoggerWithContext(): void
28+
{
29+
$logger = $this->container->get(LoggerInterface::class);
30+
$logger->critical('Failed to copy file', ['exercise' => 'my-exercise']);
31+
32+
$out = StringUtil::stripAnsiEscapeSequence($this->getActualOutputForAssertion());
33+
34+
$match = '/\d{2}\:\d{2}\:\d{2} - CRITICAL - Failed to copy file\n{\n "exercise": "my-exercise"\n}/';
35+
$this->assertMatchesRegularExpression($match, $out);
36+
}
37+
}

test/Logger/LoggerTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public function setUp(): void
1515

1616
$this->container->set('phpschoolGlobalDir', $this->getTemporaryDirectory());
1717
$this->container->set('appName', 'my-workshop');
18+
$this->container->set('debugMode', false);
1819
}
1920

2021
public function testLoggerDoesNotCreateFileIfNoMessageIsLogged(): void

0 commit comments

Comments
 (0)