Skip to content

Commit

Permalink
Merge pull request #75 from eclipxe13/maintenance-20230525
Browse files Browse the repository at this point in the history
Mantenimiento 2023-05-25 (corrige integración continua)
  • Loading branch information
eclipxe13 authored May 25, 2023
2 parents c5a7c43 + f4abcb8 commit 7ce13d3
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 49 deletions.
8 changes: 4 additions & 4 deletions .phive/phars.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="php-cs-fixer" version="^3.14.4" installed="3.14.4" location="./tools/php-cs-fixer" copy="false"/>
<phar name="phpcs" version="^3.7.1" installed="3.7.1" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^3.7.1" installed="3.7.1" location="./tools/phpcbf" copy="false"/>
<phar name="phpstan" version="^1.9.17" installed="1.9.17" location="./tools/phpstan" copy="false"/>
<phar name="php-cs-fixer" version="^3.17.0" installed="3.17.0" location="./tools/php-cs-fixer" copy="false"/>
<phar name="phpcs" version="^3.7.2" installed="3.7.2" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^3.7.2" installed="3.7.2" location="./tools/phpcbf" copy="false"/>
<phar name="phpstan" version="^1.10.15" installed="1.10.15" location="./tools/phpstan" copy="false"/>
</phive>
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@
"ext-fileinfo": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"psr/http-message": "^1.0",
"psr/http-message": "^1.1|^2.0",
"guzzlehttp/guzzle": "^7.0",
"guzzlehttp/promises": "^1.3",
"guzzlehttp/promises": "^2.0",
"symfony/dom-crawler": "^5.4|^6.0",
"symfony/css-selector": "^5.4|^6.0",
"eclipxe/enum": "^0.2.0",
"eclipxe/micro-catalog": "^v0.1.3",
"phpcfdi/credentials": "^1.1",
"phpcfdi/image-captcha-resolver": "^0.2.0"
"phpcfdi/image-captcha-resolver": "^0.2.3"
},
"require-dev": {
"ext-iconv": "*",
Expand All @@ -63,11 +63,11 @@
"@dev:tests"
],
"dev:check-style": [
"@php tools/php-cs-fixer fix --dry-run --verbose",
"@php -r 'exit(intval(PHP_VERSION_ID >= 74000));' || $PHP_BINARY tools/php-cs-fixer fix --dry-run --verbose",
"@php tools/phpcs --colors -sp"
],
"dev:fix-style": [
"@php tools/php-cs-fixer fix --verbose",
"@php -r 'exit(intval(PHP_VERSION_ID >= 74000));' || $PHP_BINARY tools/php-cs-fixer fix --verbose",
"@php tools/phpcbf --colors -sp"
],
"dev:tests": [
Expand Down
19 changes: 19 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ Usamos [Versionado Semántico 2.0.0](SEMVER.md) por lo que puedes usar esta libr

## Cambios aún no liberados en una versión

En este momento no hay cambios no liberados.

## Versión 3.2.3 2023-05-25

- Se actualiza la dependencia de `guzzlehttp/promises` a versión mínima 2.0.
- Se actualiza la dependencia de `psr/http-message` a versiones mínimas 1.1 o 2.0.
- Se actualiza la dependencia de `phpcfdi/image-captcha-resolver` a versión mínima 0.2.3.

Los siguientes cambios aplican al entorno de desarrollo:

- La ejecución de `php-cs-fixer` dentro de `composer` se condiciona a mínimo PHP 8.0.
- Se refactoriza la clase `RepositoryItem` para que las responsabilidades de la creación de una instancia
a partir de un arreglo se realizen en la clase `RepositoryItemFactory`.
- Se corrigen las pruebas para usar `psr/http-message:^2.0`.
- Se corrige el issue falso positivo encontrado por PHPStan al convertir un objeto a cadena de caracteres.
- Actualización de herramientas de desarrollo.

También se concluyen los siguientes cambios previos no liberados.

### Cambios no liberados: 2023-02-13

- Actualización de herramientas de desarrollo.
Expand Down
6 changes: 6 additions & 0 deletions src/Exceptions/ResourceDownloadError.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace PhpCfdi\CfdiSatScraper\Exceptions;

use Stringable;
use Throwable;

/**
Expand Down Expand Up @@ -67,6 +68,11 @@ public static function reasonToString($reason): string
return strval($reason);
}
if (is_object($reason) && is_callable([$reason, '__toString'])) {
/**
* Fix PHPStan false positive detecting cast from object to string
* @phpstan-var Stringable $reason
* @noinspection PhpMultipleClassDeclarationsInspection
*/
return strval($reason);
}
return print_r($reason, true);
Expand Down
2 changes: 1 addition & 1 deletion src/ResourceDownloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ protected function makePromises(): Traversable
* @throws RuntimeException if ask to create folder path exists and is not a folder
* @throws RuntimeException if unable to create folder
*
* @see \PhpCfdi\CfdiSatScraper\Internal\ResourceDownloadStoreInFolder
* @see ResourceDownloadStoreInFolder
*/
public function saveTo(string $destinationFolder, bool $createFolder = false, int $createMode = 0775): array
{
Expand Down
4 changes: 3 additions & 1 deletion tests/Integration/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@ public static function fromArray($dataItems): self
if (! is_array($dataItems)) {
throw new RuntimeException('JSON decoded contents from %s is not an array');
}

$itemFactory = new RepositoryItemFactory();
$items = [];
foreach ($dataItems as $index => $dataItem) {
if (! is_array($dataItem)) {
throw new RuntimeException("Entry $index is not an array");
}
$item = RepositoryItem::fromArray($dataItem);
$item = $itemFactory->make($dataItem);
$items[$item->getUuid()] = $item;
}
return new self($items);
Expand Down
22 changes: 0 additions & 22 deletions tests/Integration/RepositoryItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
namespace PhpCfdi\CfdiSatScraper\Tests\Integration;

use DateTimeImmutable;
use DomainException;
use Exception;
use JsonSerializable;

class RepositoryItem implements JsonSerializable
Expand All @@ -31,26 +29,6 @@ public function __construct(string $uuid, DateTimeImmutable $date, string $state
$this->state = strtoupper(substr($state, 0, 1));
}

/** @param array<mixed> $item */
public static function fromArray(array $item): self
{
return new self(
strval($item['uuid'] ?? ''),
self::dateFromString(strval($item['date'] ?? '')),
strval($item['state'] ?? ''),
strval($item['type'] ?? ''),
);
}

private static function dateFromString(string $value): DateTimeImmutable
{
try {
return new DateTimeImmutable($value);
} catch (Exception $exception) {
throw new DomainException(sprintf('Unable to parse date with value %s', $value));
}
}

public function getUuid(): string
{
return $this->uuid;
Expand Down
42 changes: 42 additions & 0 deletions tests/Integration/RepositoryItemFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace PhpCfdi\CfdiSatScraper\Tests\Integration;

use DateTimeImmutable;
use DomainException;
use Exception;

class RepositoryItemFactory
{
/** @param array<mixed> $values */
public function make(array $values): RepositoryItem
{
$maker = new self();
return new RepositoryItem(
$maker->stringFromValues($values, 'uuid'),
$maker->dateFromString($maker->stringFromValues($values, 'date')),
$maker->stringFromValues($values, 'state'),
$maker->stringFromValues($values, 'type'),
);
}

/** @param array<mixed> $values */
private function stringFromValues(array $values, string $key): string
{
if (! isset($values[$key]) || ! is_string($values[$key])) {
throw new DomainException(sprintf('Cannot create an entry with invalid %s', $key));
}
return $values[$key];
}

private function dateFromString(string $value): DateTimeImmutable
{
try {
return new DateTimeImmutable($value);
} catch (Exception $exception) {
throw new DomainException(sprintf('Unable to parse date with value %s', $value));
}
}
}
59 changes: 44 additions & 15 deletions tests/Integration/RetrieveByUuidTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@
namespace PhpCfdi\CfdiSatScraper\Tests\Integration;

use PhpCfdi\CfdiSatScraper\Filters\DownloadType;
use PhpCfdi\CfdiSatScraper\MetadataList;
use PhpCfdi\CfdiSatScraper\ResourceType;
use PhpCfdi\CfdiSatScraper\SatScraper;
use RuntimeException;

class RetrieveByUuidTest extends IntegrationTestCase
{
/** @return array<string, array{DownloadType, int}> */
public function providerRetrieveByUuid(): array
{
return [
'recibidos, random 1' => [DownloadType::recibidos(), 8],
'recibidos, random 1' => [DownloadType::recibidos(), 1],
'emitidos, random 1' => [DownloadType::emitidos(), 1],
'recibidos, random 4' => [DownloadType::recibidos(), 3],
'emitidos, random 4' => [DownloadType::emitidos(), 3],
'recibidos, random 10' => [DownloadType::recibidos(), 10],
'emitidos, random 10' => [DownloadType::emitidos(), 10],
];
}

Expand All @@ -32,6 +35,8 @@ public function providerRetrieveByUuid(): array
*/
public function testRetrieveByUuid(DownloadType $downloadType, int $count): void
{
// set up
$resourceType = ResourceType::xml();
$typeText = $this->getDownloadTypeText($downloadType);
$repository = $this->getRepository()->filterByType($downloadType);
$repository = $repository->randomize()->topItems($count);
Expand All @@ -43,29 +48,53 @@ public function testRetrieveByUuid(DownloadType $downloadType, int $count): void
);
}

// check that all uuids exists and don't have more
$scraper = $this->getSatScraper();
$list = $scraper->listByUuids($uuids, $downloadType);
foreach ($uuids as $uuid) {
$this->assertTrue($list->has($uuid), "The UUID $uuid was not found in the metadata list $typeText");
}
$this->assertCount(count($uuids), $list, sprintf('It was expected to receive only %d records', count($uuids)));

$tempDir = sys_get_temp_dir();
foreach ($uuids as $uuid) {
$filename = strtolower(sprintf('%s/%s.xml', $tempDir, $uuid));
if (file_exists($filename)) {
unlink($filename);
}
}
$scraper->resourceDownloader(ResourceType::xml(), $list)->saveTo($tempDir);
// just use items that have a download link
$list = $list->filterWithResourceLink($resourceType);

// clean destination
$tempDir = sys_get_temp_dir() . '/cfdi-sat-scraper/retrieve-by-uuid';
shell_exec(sprintf('rm -rf %s', escapeshellarg($tempDir)));
shell_exec(sprintf('mkdir -p %s', escapeshellarg($tempDir)));

// perform download
$this->downloadWithRetry($scraper, $resourceType, $list, $tempDir);

// check file existence
foreach ($repository->getIterator() as $uuid => $item) {
$filename = strtolower(sprintf('%s/%s.xml', $tempDir, $uuid));
if ('Cancelado' !== $item->getState()) {
$this->assertFileDoesNotExist($filename, sprintf('The cfdi file with uuid %s does not exists: %s', $uuid, $filename));
$filename = sprintf('%s/%s.xml', $tempDir, strtolower($uuid));
if (! $list->has($uuid)) {
$this->assertFileDoesNotExist($filename, sprintf('The cfdi file with uuid %s should not exists: %s', $uuid, $filename));
} else {
$this->assertFileExists($filename, sprintf('The cfdi file with uuid %s does not exists: %s', $uuid, $filename));
$this->assertFileExists($filename, sprintf('The cfdi file with uuid %s should exists: %s', $uuid, $filename));
$this->assertCfdiHasUuid($uuid, file_get_contents($filename) ?: '');
}
}
}

private function downloadWithRetry(SatScraper $scraper, ResourceType $resourceType, MetadataList $list, string $destination): void
{
$maxAttempts = 10;
$attempt = 1;
$downloader = $scraper->resourceDownloader($resourceType);

$list = $list->filterWithResourceLink($resourceType);
while ($list->count() > 0) {
$downloader->setMetadataList($list);
$downloaded = $downloader->saveTo($destination);
$list = $list->filterWithOutUuids($downloaded);
$attempt = $attempt + 1;
if ($attempt === $maxAttempts) {
print_r(['Missing' => $list]);
throw new RuntimeException(sprintf('Unable to domplete download after %s attempts', $attempt));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function testInvalidStatusCode(): void
{
/** @var ResponseInterface&MockObject $response */
$response = $this->createMock(ResponseInterface::class);
$response->method('getStatusCode')->willReturn('503');
$response->method('getStatusCode')->willReturn(503);
$uuid = 'uuid';
$exception = ResourceDownloadResponseError::invalidStatusCode($response, $uuid);
$this->assertSame(
Expand Down

0 comments on commit 7ce13d3

Please sign in to comment.