Skip to content

Commit

Permalink
unit tests & lint
Browse files Browse the repository at this point in the history
  • Loading branch information
leventcz authored May 19, 2024
1 parent db0ee6d commit 1bc4836
Show file tree
Hide file tree
Showing 12 changed files with 330 additions and 69 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"require-dev": {
"laravel/pint": "^1.15",
"mockery/mockery": "^1.6",
"pestphp/pest": "^2.34",
"phpstan/phpstan": "^1.10"
},
Expand Down
4 changes: 2 additions & 2 deletions src/Commands/TopCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public function handle(GuiBuilder $guiBuilder): void
->hideCursor()
->render();

Loop::addPeriodicTimer(0.5, fn() => $guiBuilder->moveToTop()->render());
Loop::addPeriodicTimer(1, fn() => $this->feed($guiBuilder));
Loop::addPeriodicTimer(0.5, fn () => $guiBuilder->moveToTop()->render());
Loop::addPeriodicTimer(1, fn () => $this->feed($guiBuilder));

pcntl_signal(SIGINT, function () use ($guiBuilder) {
$guiBuilder
Expand Down
8 changes: 6 additions & 2 deletions src/Repositories/RedisRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Leventcz\Top\Repositories;

use Illuminate\Config\Repository as Config;
use Illuminate\Contracts\Redis\Factory as RedisFactory;
use Illuminate\Redis\Connections\Connection;
use Leventcz\Top\Contracts\Repository;
Expand All @@ -17,7 +18,8 @@
readonly class RedisRepository implements Repository
{
public function __construct(
private RedisFactory $factory
private RedisFactory $factory,
private Config $config
) {
}

Expand Down Expand Up @@ -227,6 +229,8 @@ private function buildKeys(int $timestamp): array

private function connection(): Connection
{
return $this->factory->connection(config('top.connection'));
$connection = $this->config->get('top.connection');

return $this->factory->connection($connection);
}
}
21 changes: 21 additions & 0 deletions tests/Data/EventCounter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

use Leventcz\Top\Data\EventCounter;

it('counts events', function () {
$counter = new EventCounter();
$counter->add('foo');
$counter->add('foo', 4);
$counter->add('bar', 2);
$counter->add('bar', 4);

expect($counter->get())->toBe(['foo' => 5, 'bar' => 6]);
});

it('flushes events', function () {
$counter = new EventCounter();
$counter->add('foo', 123);
$counter->flush();

expect($counter->get())->toBe([]);
});
5 changes: 0 additions & 5 deletions tests/Feature/ExampleTest.php

This file was deleted.

45 changes: 0 additions & 45 deletions tests/Pest.php

This file was deleted.

131 changes: 131 additions & 0 deletions tests/Repositories/RedisRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

use Illuminate\Config\Repository;
use Illuminate\Contracts\Redis\Factory as RedisFactory;
use Illuminate\Redis\Connections\Connection;
use Leventcz\Top\Data\CacheSummary;
use Leventcz\Top\Data\DatabaseSummary;
use Leventcz\Top\Data\EventCounter;
use Leventcz\Top\Data\HandledRequest;
use Leventcz\Top\Data\RequestSummary;
use Leventcz\Top\Data\RouteCollection;
use Leventcz\Top\Repositories\RedisRepository;

beforeEach(function () {
$this->redisFactory = Mockery::mock(RedisFactory::class);
$this->connection = Mockery::mock(Connection::class);
$this->config = Mockery::mock(Repository::class);

$this->redisFactory
->shouldReceive('connection')
->andReturn($this->connection);
$this->config
->shouldReceive('get')
->andReturn('top.connection');

$this->repository = new RedisRepository($this->redisFactory, $this->config);
});

afterEach(function () {
Mockery::close();
});

it('saves handled request and events to redis', function () {
$request = new HandledRequest('GET', '/test', 100, 200, 1622505600);
$eventCounter = new EventCounter(['some-event' => 5, 'another-event' => 10]);
$pipe = Mockery::spy();

$this
->connection
->shouldReceive('pipeline')
->once()
->with(Mockery::on(function ($callback) use ($pipe) {
$callback($pipe);

return true;
}))
->andReturnNull();

$this->repository->save($request, $eventCounter);

$key = "top-requests:$request->timestamp";
$routeKey = "$request->method:$request->uri:data";

foreach ($eventCounter->get() as $event => $times) {
$pipe
->shouldHaveReceived('hIncrBy')
->with($key, "$routeKey:$event", $times)
->once();
}

$pipe
->shouldHaveReceived('hIncrBy')
->with($key, "$routeKey:hits", 1)
->once();

$pipe
->shouldHaveReceived('hIncrBy')
->with($key, "$routeKey:memory", $request->memory)
->once();

$pipe
->shouldHaveReceived('hIncrBy')
->with($key, "$routeKey:duration", $request->duration)
->once();

$pipe
->shouldHaveReceived('expire')
->with($key, 10)
->once();
});

it('fetches request summary from redis', function () {
$this
->connection
->shouldReceive('eval')
->once()
->andReturn(json_encode(['averageRequestPerSecond' => 1, 'averageMemoryUsage' => 2, 'averageDuration' => 3]));

expect($this->repository->getRequestSummary())
->toBeInstanceOf(RequestSummary::class);
});

it('fetches database summary from redis', function () {
$this
->connection
->shouldReceive('eval')
->once()
->andReturn(json_encode(['averageQueryPerSecond' => 1, 'averageQueryDuration' => 2]));

expect($this->repository->getDatabaseSummary())
->toBeInstanceOf(DatabaseSummary::class);
});

it('fetches cache summary from redis', function () {
$this
->connection
->shouldReceive('eval')
->once()
->andReturn(json_encode([
'averageHitPerSecond' => 1, 'averageMissPerSecond' => 2, 'averageWritePerSecond' => 3,
]));

expect($this->repository->getCacheSummary())
->toBeInstanceOf(CacheSummary::class);
});

it('fetches top routes from redis', function () {
$this
->connection
->shouldReceive('eval')
->once()
->andReturn(json_encode([
[
'uri' => '', 'method' => '', 'averageRequestPerSecond' => 1, 'averageMemoryUsage' => 1,
'averageDuration' => 1,
],
]));

expect($this->repository->getTopRoutes())
->toBeInstanceOf(RouteCollection::class);
});
53 changes: 53 additions & 0 deletions tests/ServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

use Illuminate\Config\Repository;
use Illuminate\Contracts\Redis\Factory;
use Illuminate\Foundation\Application;
use Illuminate\Redis\RedisManager;
use Leventcz\Top\Facades\State;
use Leventcz\Top\Facades\Top;
use Leventcz\Top\ServiceProvider;
use Leventcz\Top\StateManager;
use Leventcz\Top\TopManager;

beforeEach(function () {
$this->app = new Application();
$this->app->bind('config', fn () => new Repository());
$this->app->bind(Factory::class, fn () => Mockery::mock(RedisManager::class));
$this->app->register(ServiceProvider::class);
});

afterAll(function () {
Mockery::close();
});

it('ensures top is bound to the container', function () {
expect($this->app->get('top'))
->toBeInstanceOf(TopManager::class);
});

it('ensures top.state bound to the container', function () {
expect($this->app->get('top.state'))
->toBeInstanceOf(StateManager::class);
});

it('ensures top is always singleton', function () {
$top = $this->app->get('top');

expect($this->app->get('top'))
->toBe($top);
});

it('ensures top facade resolves correctly', function () {
Top::setFacadeApplication($this->app);

expect(Top::getFacadeRoot())
->toBeInstanceOf(TopManager::class);
});

it('ensures state facade resolves correctly', function () {
State::setFacadeApplication($this->app);

expect(State::getFacadeRoot())
->toBeInstanceOf(StateManager::class);
});
47 changes: 47 additions & 0 deletions tests/StateManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

use Leventcz\Top\Contracts\Repository;
use Leventcz\Top\Data\EventCounter;
use Leventcz\Top\Data\HandledRequest;
use Leventcz\Top\StateManager;

beforeEach(function () {
$this->eventCounter = Mockery::mock(EventCounter::class);
$this->repository = Mockery::mock(Repository::class);
$this->stateManager = new StateManager($this->eventCounter, $this->repository);
});

afterAll(function () {
Mockery::close();
});

it('adds event to state', function () {
$this
->eventCounter
->shouldReceive('add')
->with('test', 4)
->once();

$this->stateManager->add('test', 4);
});

it('flushes state', function () {
$this
->eventCounter
->shouldReceive('flush')
->once();

$this->stateManager->flush();
});

it('saves state', function () {
$request = new HandledRequest('', '', 1, 1, 1);

$this
->repository
->shouldReceive('save')
->with($request, $this->eventCounter)
->once();

$this->stateManager->save($request);
});
10 changes: 0 additions & 10 deletions tests/TestCase.php

This file was deleted.

Loading

0 comments on commit 1bc4836

Please sign in to comment.