Skip to content

Commit

Permalink
test: add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sitepark-veltrup committed Jan 31, 2024
1 parent f877a2d commit 52deb6b
Show file tree
Hide file tree
Showing 11 changed files with 399 additions and 117 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
!var/cache/.gitkeep
/var/log/*
!var/log/.gitkeep
/var/test/*
/tools
.phpactor.json
composer.lock
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"symfony/event-dispatcher": "^6.3 | ^7.0",
"symfony/finder": "^6.3 | ^7.0",
"symfony/lock": "^6.3 | ^7.0",
"symfony/property-access": "^6.3 | ^7.0",
"symfony/serializer": "^6.3 | ^7.0",
"symfony/yaml": "^6.3 | ^7.0"
},
"require-dev": {
Expand All @@ -37,7 +39,8 @@
"phpcompatibility/php-compatibility": "^9.3",
"phpunit/phpunit": "^10.4",
"roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.7"
"squizlabs/php_codesniffer": "^3.7",
"symfony/filesystem": "^6.3 | ^7.0"
},
"scripts": {

Expand Down
2 changes: 1 addition & 1 deletion src/Console/Command/Io/IndexerProgressBarFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ public function create(OutputInterface $output): IndexerProgressBar
{
return new IndexerProgressBar($output);
}
}
}
83 changes: 6 additions & 77 deletions src/Dto/Indexer/IndexerStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
namespace Atoolo\Search\Dto\Indexer;

use DateTime;
use InvalidArgumentException;
use JsonException;

/**
* @phpstan-type JsonStatus array{
Expand Down Expand Up @@ -60,88 +58,19 @@ public function getStatusLine(): string
$endTime = new DateTime();
}
$duration = $this->startTime->diff($endTime);

$lastUpdate = $this->lastUpdate;
if ($lastUpdate->getTimestamp() === 0) {
$lastUpdate = $endTime;
}
return
'[' . $this->state->name . '] ' .
'start: ' . $this->startTime->format('d.m.Y H:i') . ', ' .
'time: ' . $duration->format('%Hh %Im %Ss') . ', ' .
'processed: ' . $this->processed . "/" . $this->total . ', ' .
'skipped: ' . $this->skipped . ', ' .
'lastUpdate: ' . $this->startTime->format('d.m.Y H:i') . ', ' .
'lastUpdate: ' . $lastUpdate->format('d.m.Y H:i') . ', ' .
'updated: ' . $this->updated . ', ' .
'errors: ' . $this->errors;
}

/**
* @throws JsonException
*/
public static function load(string $file): IndexerStatus
{
if (!file_exists($file)) {
return self::empty();
}
$content = file_get_contents($file);
if ($content === false) {
throw new InvalidArgumentException('Cannot read file ' . $file);
}
/** @var JsonStatus $data */
$data = json_decode(
$content,
true,
512,
JSON_THROW_ON_ERROR
);

$state = isset($data['state'])
? IndexerStatusState::valueOf($data['state'])
: IndexerStatusState::UNKNOWN;

$startTime = new DateTime();
$startTime->setTimestamp($data['startTime']);

$endTime = new DateTime();
if ($data['endTime'] !== null) {
$endTime->setTimestamp($data['endTime']);
} else {
$endTime->setTimestamp(0);
}

$lastUpdate = new DateTime();
if ($data['lastUpdate'] !== null) {
$lastUpdate->setTimestamp($data['lastUpdate']);
} else {
$lastUpdate->setTimestamp(0);
}

return new IndexerStatus(
$state,
$startTime,
$endTime,
$data['total'],
$data['processed'],
$data['skipped'] ?? 0,
$lastUpdate,
$data['updated'] ?? 0,
$data['errors'] ?? 0
);
}

/**
* @throws JsonException
*/
public function store(string $file): void
{
$jsonString = json_encode([
'state' => $this->state->name,
'statusLine' => $this->getStatusLine(),
'startTime' => $this->startTime->getTimestamp(),
'endTime' => $this->endTime?->getTimestamp(),
'total' => $this->total,
'processed' => $this->processed,
'skipped' => $this->skipped,
'lastUpdate' => $this->lastUpdate->getTimestamp(),
'updated' => $this->updated,
'errors' => $this->errors
], JSON_THROW_ON_ERROR);
file_put_contents($file, $jsonString);
}
}
10 changes: 5 additions & 5 deletions src/Dto/Indexer/IndexerStatusState.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace Atoolo\Search\Dto\Indexer;

enum IndexerStatusState
enum IndexerStatusState: string
{
case UNKNOWN;
case RUNNING;
case INDEXED;
case ABORTED;
case UNKNOWN = 'UNKNOWN';
case RUNNING = 'RUNNING';
case INDEXED = 'INDEXED';
case ABORTED = 'ABORTED';

public static function valueOf(string $name): IndexerStatusState
{
Expand Down
29 changes: 6 additions & 23 deletions src/Service/Indexer/BackgroundIndexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@
use Atoolo\Search\Dto\Indexer\IndexerStatus;
use Atoolo\Search\Indexer;
use Atoolo\Search\Service\SolrClientFactory;
use JsonException;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use RuntimeException;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\SemaphoreStore;
use Symfony\Component\Serializer\Exception\ExceptionInterface;

class BackgroundIndexer implements Indexer
{
Expand All @@ -31,20 +30,10 @@ public function __construct(
private readonly SolrClientFactory $clientFactory,
private readonly IndexingAborter $aborter,
private readonly string $source,
private readonly string $statusCacheDir,
private readonly IndexerStatusStore $statusStore,
private readonly LoggerInterface $logger = new NullLogger()
) {
$this->lockFactory = new LockFactory(new SemaphoreStore());
if (
!is_dir($concurrentDirectory = $this->statusCacheDir) &&
!mkdir($concurrentDirectory) &&
!is_dir($concurrentDirectory)
) {
throw new RuntimeException(sprintf(
'Directory "%s" was not created',
$concurrentDirectory
));
}
}

/**
Expand Down Expand Up @@ -74,18 +63,18 @@ public function index(IndexerParameter $parameter): IndexerStatus
}

/**
* @throws JsonException
* @throws ExceptionInterface
*/
public function getStatus(string $index): IndexerStatus
{
$file = $this->getStatusFile($index);
return IndexerStatus::load($file);
return $this->statusStore->load($index);
}

private function getIndexer(string $index): SolrIndexer
{
$progressHandler = new BackgroundIndexerProgressState(
$this->getStatusFile($index),
$index,
$this->statusStore,
$this->logger
);
return new SolrIndexer(
Expand All @@ -99,10 +88,4 @@ private function getIndexer(string $index): SolrIndexer
$this->source
);
}

private function getStatusFile(string $index): string
{
return $this->statusCacheDir .
'/atoolo.search.index.' . $index . ".status.json";
}
}
20 changes: 10 additions & 10 deletions src/Service/Indexer/BackgroundIndexerProgressState.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
use Atoolo\Search\Dto\Indexer\IndexerStatus;
use Atoolo\Search\Dto\Indexer\IndexerStatusState;
use DateTime;
use JsonException;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Throwable;
use JsonException;

class BackgroundIndexerProgressState implements IndexerProgressHandler
{
Expand All @@ -19,7 +20,8 @@ class BackgroundIndexerProgressState implements IndexerProgressHandler
private bool $isUpdate = false;

public function __construct(
private readonly string $file,
private string $index,
private readonly IndexerStatusStore $statusStore,
private readonly LoggerInterface $logger = new NullLogger()
) {
}
Expand All @@ -39,10 +41,13 @@ public function start(int $total): void
);
}

/**
* @throws ExceptionInterface
*/
public function startUpdate(int $total): void
{
$this->isUpdate = true;
$storedStatus = IndexerStatus::load($this->file);
$storedStatus = $this->statusStore->load($this->index);
$this->status = new IndexerStatus(
IndexerStatusState::RUNNING,
$storedStatus->startTime,
Expand All @@ -66,7 +71,7 @@ public function advance(int $step): void
if ($this->isUpdate) {
$this->status->updated += $step;
}
$this->status->store($this->file);
$this->statusStore->store($this->index, $this->status);
}


Expand Down Expand Up @@ -97,7 +102,7 @@ public function finish(): void
if ($this->status->state === IndexerStatusState::RUNNING) {
$this->status->state = IndexerStatusState::INDEXED;
}
$this->status->store($this->file);
$this->statusStore->store($this->index, $this->status);
}

public function abort(): void
Expand All @@ -117,9 +122,4 @@ public function getStatus(): IndexerStatus
{
return $this->status;
}

public function getStatusFile(): string
{
return $this->file;
}
}
94 changes: 94 additions & 0 deletions src/Service/Indexer/IndexerStatusStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

namespace Atoolo\Search\Service\Indexer;

use Atoolo\Search\Dto\Indexer\IndexerStatus;
use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
use Symfony\Component\Serializer\Serializer;

class IndexerStatusStore
{
public function __construct(private readonly string $basedir)
{
}

/**
* @throws ExceptionInterface
*/
public function load(string $index): IndexerStatus
{
$file = $this->getStatusFile($index);

if (!file_exists($file)) {
return IndexerStatus::empty();
}

$json = file_get_contents($file);
if ($json === false) {
throw new InvalidArgumentException('Cannot read file ' . $file);
}

/** @var IndexerStatus $status */
$status = $this
->createSerializer()
->deserialize($json, IndexerStatus::class, 'json');

return $status;
}

public function store(string $index, IndexerStatus $status): void
{
$this->createBaseDirectory();

$file = $this->getStatusFile($index);
$json = $this
->createSerializer()
->serialize($status, 'json');
$result = file_put_contents($file, $json);
if ($result === false) {
throw new RuntimeException(
'Unable to write indexer-status file ' . $file
);
}
}

private function createBaseDirectory(): void
{
if (
!is_dir($concurrentDirectory = $this->basedir) &&
!mkdir($concurrentDirectory) &&
!is_dir($concurrentDirectory)
) {
throw new RuntimeException(sprintf(
'Directory "%s" was not created',
$concurrentDirectory
));
}
}

private function createSerializer(): Serializer
{
$encoders = [new JsonEncoder()];
$normalizers = [
new BackedEnumNormalizer(),
new DateTimeNormalizer(),
new PropertyNormalizer()
];

return new Serializer($normalizers, $encoders);
}

private function getStatusFile(string $index): string
{
return $this->basedir .
'/atoolo.search.index.' . $index . ".status.json";
}
}
Loading

0 comments on commit 52deb6b

Please sign in to comment.