style: apply CS Fixer formatting across codebase
Some checks are pending
CI / test (push) Waiting to run
Some checks are pending
CI / test (push) Waiting to run
Consistent brace style, spacing, and method expansion throughout domain, infrastructure, and test files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fc18958e0e
commit
a79791a972
37 changed files with 223 additions and 167 deletions
|
|
@ -57,9 +57,6 @@ final class PhotoService
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $orderedPhotoIds UUIDs in the desired display order
|
||||
*/
|
||||
/**
|
||||
* Attach a file that was already stored (e.g. uploaded at ingest time) to an article.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ class Article
|
|||
foreach ($this->attributeValues as $existing) {
|
||||
if ($existing->getAttributeDefinition()->getId()->equals($value->getAttributeDefinition()->getId())) {
|
||||
$existing->setValue($value->getValue());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ class ArticleType
|
|||
$existing->setArticleFieldKey($mapping->getArticleFieldKey());
|
||||
$existing->setAttributeDefinition($mapping->getAttributeDefinition());
|
||||
$existing->setRequired($mapping->isRequired());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -105,7 +106,7 @@ class ArticleType
|
|||
public function getAttributeDefinitions(): Collection
|
||||
{
|
||||
return $this->attributeAssignments->map(
|
||||
fn (ArticleTypeAttribute $a) => $a->getAttributeDefinition()
|
||||
static fn (ArticleTypeAttribute $a) => $a->getAttributeDefinition()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -113,8 +114,8 @@ class ArticleType
|
|||
public function getRequiredAttributeDefinitions(): Collection
|
||||
{
|
||||
return $this->attributeAssignments
|
||||
->filter(fn (ArticleTypeAttribute $a) => $a->isRequired())
|
||||
->map(fn (ArticleTypeAttribute $a) => $a->getAttributeDefinition());
|
||||
->filter(static fn (ArticleTypeAttribute $a) => $a->isRequired())
|
||||
->map(static fn (ArticleTypeAttribute $a) => $a->getAttributeDefinition());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
@ -125,15 +126,15 @@ class ArticleType
|
|||
public function getRequiredAttributeDefs(): Collection
|
||||
{
|
||||
return $this->attributeAssignments
|
||||
->filter(fn (ArticleTypeAttribute $a) => $a->isRequired())
|
||||
->map(fn (ArticleTypeAttribute $a) => $a->getAttributeDefinition());
|
||||
->filter(static fn (ArticleTypeAttribute $a) => $a->isRequired())
|
||||
->map(static fn (ArticleTypeAttribute $a) => $a->getAttributeDefinition());
|
||||
}
|
||||
|
||||
/** @param iterable<AttributeDefinition> $defs */
|
||||
public function setRequiredAttributeDefs(iterable $defs): void
|
||||
{
|
||||
/** @var list<AttributeDefinition> $list */
|
||||
$list = $defs instanceof Collection ? $defs->toArray() : \iterator_to_array($defs, false);
|
||||
$list = $defs instanceof Collection ? $defs->toArray() : iterator_to_array($defs, false);
|
||||
$this->pendingRequired = $list;
|
||||
}
|
||||
|
||||
|
|
@ -141,15 +142,15 @@ class ArticleType
|
|||
public function getOptionalAttributeDefs(): Collection
|
||||
{
|
||||
return $this->attributeAssignments
|
||||
->filter(fn (ArticleTypeAttribute $a) => !$a->isRequired())
|
||||
->map(fn (ArticleTypeAttribute $a) => $a->getAttributeDefinition());
|
||||
->filter(static fn (ArticleTypeAttribute $a) => !$a->isRequired())
|
||||
->map(static fn (ArticleTypeAttribute $a) => $a->getAttributeDefinition());
|
||||
}
|
||||
|
||||
/** @param iterable<AttributeDefinition> $defs */
|
||||
public function setOptionalAttributeDefs(iterable $defs): void
|
||||
{
|
||||
/** @var list<AttributeDefinition> $list */
|
||||
$list = $defs instanceof Collection ? $defs->toArray() : \iterator_to_array($defs, false);
|
||||
$list = $defs instanceof Collection ? $defs->toArray() : iterator_to_array($defs, false);
|
||||
$this->pendingOptional = $list;
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +160,7 @@ class ArticleType
|
|||
*/
|
||||
public function applyAttributeAssignments(): void
|
||||
{
|
||||
if ($this->pendingRequired === null && $this->pendingOptional === null) {
|
||||
if (null === $this->pendingRequired && null === $this->pendingOptional) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use Symfony\Component\Uid\Uuid;
|
|||
class ArticleTypeEbayMapping
|
||||
{
|
||||
public const SOURCE_ARTICLE_FIELD = 'article_field';
|
||||
public const SOURCE_ATTRIBUTE = 'attribute';
|
||||
public const SOURCE_ATTRIBUTE = 'attribute';
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: 'uuid')]
|
||||
|
|
@ -46,31 +46,61 @@ class ArticleTypeEbayMapping
|
|||
|
||||
public function __construct(ArticleType $articleType, string $ebayAspectName, string $sourceType)
|
||||
{
|
||||
$this->id = Uuid::v7();
|
||||
$this->articleType = $articleType;
|
||||
$this->id = Uuid::v7();
|
||||
$this->articleType = $articleType;
|
||||
$this->ebayAspectName = $ebayAspectName;
|
||||
$this->sourceType = $sourceType;
|
||||
$this->sourceType = $sourceType;
|
||||
}
|
||||
|
||||
public function getId(): Uuid { return $this->id; }
|
||||
public function getId(): Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getArticleType(): ArticleType { return $this->articleType; }
|
||||
public function getArticleType(): ArticleType
|
||||
{
|
||||
return $this->articleType;
|
||||
}
|
||||
|
||||
public function getEbayAspectName(): string { return $this->ebayAspectName; }
|
||||
public function getEbayAspectName(): string
|
||||
{
|
||||
return $this->ebayAspectName;
|
||||
}
|
||||
|
||||
public function getSourceType(): string { return $this->sourceType; }
|
||||
public function getSourceType(): string
|
||||
{
|
||||
return $this->sourceType;
|
||||
}
|
||||
|
||||
public function getArticleFieldKey(): ?string { return $this->articleFieldKey; }
|
||||
public function getArticleFieldKey(): ?string
|
||||
{
|
||||
return $this->articleFieldKey;
|
||||
}
|
||||
|
||||
public function setArticleFieldKey(?string $key): void { $this->articleFieldKey = $key; }
|
||||
public function setArticleFieldKey(?string $key): void
|
||||
{
|
||||
$this->articleFieldKey = $key;
|
||||
}
|
||||
|
||||
public function getAttributeDefinition(): ?AttributeDefinition { return $this->attributeDefinition; }
|
||||
public function getAttributeDefinition(): ?AttributeDefinition
|
||||
{
|
||||
return $this->attributeDefinition;
|
||||
}
|
||||
|
||||
public function setAttributeDefinition(?AttributeDefinition $def): void { $this->attributeDefinition = $def; }
|
||||
public function setAttributeDefinition(?AttributeDefinition $def): void
|
||||
{
|
||||
$this->attributeDefinition = $def;
|
||||
}
|
||||
|
||||
public function isRequired(): bool { return $this->required; }
|
||||
public function isRequired(): bool
|
||||
{
|
||||
return $this->required;
|
||||
}
|
||||
|
||||
public function setRequired(bool $required): void { $this->required = $required; }
|
||||
public function setRequired(bool $required): void
|
||||
{
|
||||
$this->required = $required;
|
||||
}
|
||||
|
||||
public function getSourceLabel(): string
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,14 +37,48 @@ class Translation
|
|||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getId(): Uuid { return $this->id; }
|
||||
public function getLocale(): string { return $this->locale; }
|
||||
public function getDomain(): string { return $this->domain; }
|
||||
public function getKey(): string { return $this->key; }
|
||||
public function getValue(): string { return $this->value; }
|
||||
public function getId(): Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setLocale(string $locale): void { $this->locale = $locale; }
|
||||
public function setDomain(string $domain): void { $this->domain = $domain; }
|
||||
public function setKey(string $key): void { $this->key = $key; }
|
||||
public function setValue(string $value): void { $this->value = $value; }
|
||||
public function getLocale(): string
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
public function getDomain(): string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setLocale(string $locale): void
|
||||
{
|
||||
$this->locale = $locale;
|
||||
}
|
||||
|
||||
public function setDomain(string $domain): void
|
||||
{
|
||||
$this->domain = $domain;
|
||||
}
|
||||
|
||||
public function setKey(string $key): void
|
||||
{
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
public function setValue(string $value): void
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@ final class EbayTextAgent
|
|||
$modelNumber = $article->getModelNumber() ?? '';
|
||||
$deviceLabel = trim("{$manufacturer} {$modelName} {$modelNumber}") ?: $typeName;
|
||||
|
||||
$specsSection = $attributeText !== ''
|
||||
$specsSection = '' !== $attributeText
|
||||
? "Known attributes:\n{$attributeText}"
|
||||
: ($specsText !== '' ? "Research notes:\n".mb_substr($specsText, 0, 1500) : '');
|
||||
: ('' !== $specsText ? "Research notes:\n".mb_substr($specsText, 0, 1500) : '');
|
||||
|
||||
$titlePrompt = $this->prompts->render('ebay_title', [
|
||||
'typeName' => $typeName,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ final class SpecsResearchAgent
|
|||
*/
|
||||
public function research(string $modelName, string $articleTypeName, string $manufacturer = '', array $attributeFields = []): array
|
||||
{
|
||||
$subject = trim(($manufacturer !== '' ? $manufacturer.' ' : '').$modelName);
|
||||
$subject = trim(('' !== $manufacturer ? $manufacturer.' ' : '').$modelName);
|
||||
|
||||
$searchResults = $this->search->search("{$subject} {$articleTypeName} specifications");
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ final class SpecsResearchAgent
|
|||
$prompt = $this->prompts->render('specs_research', [
|
||||
'articleType' => $articleTypeName,
|
||||
'subject' => $subject,
|
||||
'searchResults' => $searchResults !== '' ? $searchResults : 'No web results available.',
|
||||
'searchResults' => '' !== $searchResults ? $searchResults : 'No web results available.',
|
||||
'fields' => $fieldsList,
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ final class MistralClient implements OllamaClientInterface
|
|||
|
||||
private function guessMimeType(string $path): string
|
||||
{
|
||||
return match (strtolower(pathinfo($path, PATHINFO_EXTENSION))) {
|
||||
return match (strtolower(pathinfo($path, \PATHINFO_EXTENSION))) {
|
||||
'jpg', 'jpeg' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'gif' => 'image/gif',
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ final class FrappeErpAdapter implements ErpAdapterInterface
|
|||
*/
|
||||
private function addressMatches(array $addr, string $street, string $city, string $zip): bool
|
||||
{
|
||||
$n = static fn(string $s): string => mb_strtolower(trim($s));
|
||||
$n = static fn (string $s): string => mb_strtolower(trim($s));
|
||||
|
||||
return $n($addr['address_line1'] ?? '') === $n($street)
|
||||
&& $n($addr['city'] ?? '') === $n($city)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ final class CreateApiKeyCommand extends Command
|
|||
|
||||
$rawKey = bin2hex(random_bytes(24));
|
||||
$prefix = substr($rawKey, 0, 8);
|
||||
$keyHash = password_hash($rawKey, PASSWORD_BCRYPT);
|
||||
$keyHash = password_hash($rawKey, \PASSWORD_BCRYPT);
|
||||
|
||||
$apiKey = new ApiKey($user, $label, $prefix, $keyHash);
|
||||
$this->apiKeyRepository->save($apiKey);
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ use App\Infrastructure\Messenger\Message\JsonCodingMessage;
|
|||
use App\Infrastructure\Messenger\Message\PhotoUploadMessage;
|
||||
use App\Infrastructure\Messenger\Message\SpecsResearchMessage;
|
||||
use App\Infrastructure\Messenger\Message\ValidationMessage;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
|
|
@ -27,7 +28,6 @@ use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField;
|
|||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Orm\EntityRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Translation\TranslatableMessage;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ final class ArticleTypeCrudController extends AbstractCrudController
|
|||
yield TextField::new('name', 'Name');
|
||||
yield TextField::new('ebayCategoryId', 'eBay Category ID')->setRequired(false)->hideOnIndex();
|
||||
yield IntegerField::new('attributeAssignments', '# Attributes')
|
||||
->formatValue(static fn (mixed $v): int => is_countable($v) ? count($v) : 0)
|
||||
->formatValue(static fn (mixed $v): int => is_countable($v) ? \count($v) : 0)
|
||||
->hideOnForm()
|
||||
->setSortable(false);
|
||||
|
||||
|
|
@ -77,8 +77,7 @@ final class ArticleTypeCrudController extends AbstractCrudController
|
|||
'attr' => ['data-ea-widget' => 'ea-autocomplete'],
|
||||
])
|
||||
->hideOnIndex()
|
||||
->formatValue(static fn (mixed $v, ArticleType $at): string =>
|
||||
implode(', ', $at->getRequiredAttributeDefs()->map(fn (AttributeDefinition $d) => $d->getName())->toArray()) ?: '—'
|
||||
->formatValue(static fn (mixed $v, ArticleType $at): string => implode(', ', $at->getRequiredAttributeDefs()->map(static fn (AttributeDefinition $d) => $d->getName())->toArray()) ?: '—'
|
||||
);
|
||||
|
||||
yield Field::new('optionalAttributeDefs', 'Optional Attributes')
|
||||
|
|
@ -92,8 +91,7 @@ final class ArticleTypeCrudController extends AbstractCrudController
|
|||
'attr' => ['data-ea-widget' => 'ea-autocomplete'],
|
||||
])
|
||||
->hideOnIndex()
|
||||
->formatValue(static fn (mixed $v, ArticleType $at): string =>
|
||||
implode(', ', $at->getOptionalAttributeDefs()->map(fn (AttributeDefinition $d) => $d->getName())->toArray()) ?: '—'
|
||||
->formatValue(static fn (mixed $v, ArticleType $at): string => implode(', ', $at->getOptionalAttributeDefs()->map(static fn (AttributeDefinition $d) => $d->getName())->toArray()) ?: '—'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ final class EbayAspectImportController extends AbstractController
|
|||
return $this->json([]);
|
||||
}
|
||||
|
||||
return $this->json(array_slice($results, 0, 15));
|
||||
return $this->json(\array_slice($results, 0, 15));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -54,23 +54,23 @@ final class EbayAspectImportController extends AbstractController
|
|||
*/
|
||||
private const ARTICLE_FIELDS = [
|
||||
'articleTypeName' => 'Produktart (Artikel-Typ)',
|
||||
'manufacturer' => 'Hersteller (Marke)',
|
||||
'modelNumber' => 'Herstellernummer (PN / MPN)',
|
||||
'modelName' => 'Modellname',
|
||||
'serialNumber' => 'Seriennummer',
|
||||
'manufacturer' => 'Hersteller (Marke)',
|
||||
'modelNumber' => 'Herstellernummer (PN / MPN)',
|
||||
'modelName' => 'Modellname',
|
||||
'serialNumber' => 'Seriennummer',
|
||||
];
|
||||
|
||||
/** eBay aspect names (lowercase) that auto-match to article fields. */
|
||||
private const ARTICLE_FIELD_ALIASES = [
|
||||
'produktart' => 'articleTypeName',
|
||||
'marke' => 'manufacturer',
|
||||
'brand' => 'manufacturer',
|
||||
'hersteller' => 'manufacturer',
|
||||
'herstellernummer' => 'modelNumber',
|
||||
'mpn' => 'modelNumber',
|
||||
'teilenummer' => 'modelNumber',
|
||||
'modell' => 'modelName',
|
||||
'seriennummer' => 'serialNumber',
|
||||
'produktart' => 'articleTypeName',
|
||||
'marke' => 'manufacturer',
|
||||
'brand' => 'manufacturer',
|
||||
'hersteller' => 'manufacturer',
|
||||
'herstellernummer' => 'modelNumber',
|
||||
'mpn' => 'modelNumber',
|
||||
'teilenummer' => 'modelNumber',
|
||||
'modell' => 'modelName',
|
||||
'seriennummer' => 'serialNumber',
|
||||
];
|
||||
|
||||
#[Route('/admin/ebay/aspect-import/{id}', name: 'admin_ebay_aspect_import')]
|
||||
|
|
@ -109,20 +109,20 @@ final class EbayAspectImportController extends AbstractController
|
|||
$allDefs = $this->em->getRepository(AttributeDefinition::class)->findBy([], ['name' => 'ASC']);
|
||||
$rows = $this->buildRows($aspects, $allDefs, $articleType);
|
||||
$counts = [
|
||||
'required' => count(array_filter($aspects, static fn (array $a) => $a['required'])),
|
||||
'recommended' => count(array_filter($aspects, static fn (array $a) => !$a['required'] && 'RECOMMENDED' === $a['usage'])),
|
||||
'optional' => count(array_filter($aspects, static fn (array $a) => !$a['required'] && 'OPTIONAL' === $a['usage'])),
|
||||
'required' => \count(array_filter($aspects, static fn (array $a) => $a['required'])),
|
||||
'recommended' => \count(array_filter($aspects, static fn (array $a) => !$a['required'] && 'RECOMMENDED' === $a['usage'])),
|
||||
'optional' => \count(array_filter($aspects, static fn (array $a) => !$a['required'] && 'OPTIONAL' === $a['usage'])),
|
||||
];
|
||||
}
|
||||
|
||||
return $this->render('admin/ebay/aspect_import.html.twig', [
|
||||
'articleType' => $articleType,
|
||||
'rows' => $rows,
|
||||
'allDefs' => $allDefs,
|
||||
'articleType' => $articleType,
|
||||
'rows' => $rows,
|
||||
'allDefs' => $allDefs,
|
||||
'articleFields' => self::ARTICLE_FIELDS,
|
||||
'counts' => $counts,
|
||||
'categoryId' => $categoryId,
|
||||
'searchUrl' => $this->generateUrl('admin_ebay_category_search'),
|
||||
'counts' => $counts,
|
||||
'categoryId' => $categoryId,
|
||||
'searchUrl' => $this->generateUrl('admin_ebay_category_search'),
|
||||
'existingMappings' => $articleType->getEbayMappings(),
|
||||
]);
|
||||
}
|
||||
|
|
@ -158,7 +158,7 @@ final class EbayAspectImportController extends AbstractController
|
|||
/** @var array<string, string> $data */
|
||||
$data = array_map(static fn (mixed $v): string => \is_scalar($v) ? (string) $v : '', $rawData);
|
||||
|
||||
$action = $data['action'] ?? 'skip';
|
||||
$action = $data['action'] ?? 'skip';
|
||||
$ebayName = $data['ebayName'] ?? '';
|
||||
$isRequired = ($data['ebayRequired'] ?? '0') === '1';
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ final class EbayAspectImportController extends AbstractController
|
|||
}
|
||||
|
||||
$rawValues = array_values(array_filter(array_map('trim', explode(',', $data['ebayValues'] ?? ''))));
|
||||
$type = (count($rawValues) > 0 && count($rawValues) <= 30)
|
||||
$type = (\count($rawValues) > 0 && \count($rawValues) <= 30)
|
||||
? AttributeType::Select
|
||||
: AttributeType::String;
|
||||
|
||||
|
|
@ -289,12 +289,12 @@ final class EbayAspectImportController extends AbstractController
|
|||
$existing = $existingMappings[$aspect['name']];
|
||||
$isFieldMapping = ArticleTypeEbayMapping::SOURCE_ARTICLE_FIELD === $existing->getSourceType();
|
||||
$rows[] = [
|
||||
'aspect' => $aspect,
|
||||
'action' => $isFieldMapping ? 'article_field' : 'match',
|
||||
'preMatchId' => $isFieldMapping ? null : $existing->getAttributeDefinition()?->getId()->toRfc4122(),
|
||||
'preFieldKey' => $isFieldMapping ? $existing->getArticleFieldKey() : null,
|
||||
'aspect' => $aspect,
|
||||
'action' => $isFieldMapping ? 'article_field' : 'match',
|
||||
'preMatchId' => $isFieldMapping ? null : $existing->getAttributeDefinition()?->getId()->toRfc4122(),
|
||||
'preFieldKey' => $isFieldMapping ? $existing->getArticleFieldKey() : null,
|
||||
'alreadyAssigned' => true,
|
||||
'suggestedType' => 'string',
|
||||
'suggestedType' => 'string',
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
|
@ -327,7 +327,7 @@ final class EbayAspectImportController extends AbstractController
|
|||
$preMatchId = null;
|
||||
}
|
||||
|
||||
$suggestedType = (count($aspect['values']) > 0 && count($aspect['values']) <= 30)
|
||||
$suggestedType = (\count($aspect['values']) > 0 && \count($aspect['values']) <= 30)
|
||||
? AttributeType::Select->value
|
||||
: AttributeType::String->value;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
|||
final class PipelineStreamController extends AbstractController
|
||||
{
|
||||
private const array STEP_KEYS = [
|
||||
'vision' => 'pipeline.step.vision',
|
||||
'vision' => 'pipeline.step.vision',
|
||||
'specs_research' => 'pipeline.step.specs_research',
|
||||
'json_coding' => 'pipeline.step.json_coding',
|
||||
'draft_article' => 'pipeline.step.draft_article',
|
||||
'ebay_text' => 'pipeline.step.ebay_text',
|
||||
'validation' => 'pipeline.step.validation',
|
||||
'json_coding' => 'pipeline.step.json_coding',
|
||||
'draft_article' => 'pipeline.step.draft_article',
|
||||
'ebay_text' => 'pipeline.step.ebay_text',
|
||||
'validation' => 'pipeline.step.validation',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
|
|
@ -69,7 +69,7 @@ final class PipelineStreamController extends AbstractController
|
|||
$status = $job->getStatus()->value;
|
||||
|
||||
$prev = $seen[$jobId] ?? null;
|
||||
if ($prev !== null && $prev['step'] === $step && $prev['status'] === $status) {
|
||||
if (null !== $prev && $prev['step'] === $step && $prev['status'] === $status) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -105,31 +105,25 @@ final class PipelineStreamController extends AbstractController
|
|||
$label = "Job #{$inv}";
|
||||
|
||||
$t = fn (string $key, array $p = []) => $this->translator->trans($key, $p, 'admin');
|
||||
$stepLabel = $step !== null
|
||||
$stepLabel = null !== $step
|
||||
? $t(self::STEP_KEYS[$step] ?? $step)
|
||||
: '';
|
||||
|
||||
$message = match (true) {
|
||||
$status === AIPipelineJobStatus::Queued->value
|
||||
=> $t('pipeline.event.queued', ['%inv%' => $inv]),
|
||||
$status === AIPipelineJobStatus::Processing->value && $step === null
|
||||
=> $t('pipeline.event.processing_start', ['%inv%' => $inv]),
|
||||
$status === AIPipelineJobStatus::Processing->value
|
||||
=> $t('pipeline.event.processing_step', ['%inv%' => $inv, '%step%' => $stepLabel]),
|
||||
$status === AIPipelineJobStatus::Completed->value
|
||||
=> $t('pipeline.event.completed', ['%inv%' => $inv]),
|
||||
$status === AIPipelineJobStatus::Failed->value
|
||||
=> $t('pipeline.event.failed', ['%inv%' => $inv, '%reason%' => $job->getErrorMessage() ?? '']),
|
||||
$status === AIPipelineJobStatus::NeedsReview->value
|
||||
=> $t('pipeline.event.needs_review', ['%inv%' => $inv]),
|
||||
$status === AIPipelineJobStatus::Queued->value => $t('pipeline.event.queued', ['%inv%' => $inv]),
|
||||
$status === AIPipelineJobStatus::Processing->value && null === $step => $t('pipeline.event.processing_start', ['%inv%' => $inv]),
|
||||
$status === AIPipelineJobStatus::Processing->value => $t('pipeline.event.processing_step', ['%inv%' => $inv, '%step%' => $stepLabel]),
|
||||
$status === AIPipelineJobStatus::Completed->value => $t('pipeline.event.completed', ['%inv%' => $inv]),
|
||||
$status === AIPipelineJobStatus::Failed->value => $t('pipeline.event.failed', ['%inv%' => $inv, '%reason%' => $job->getErrorMessage() ?? '']),
|
||||
$status === AIPipelineJobStatus::NeedsReview->value => $t('pipeline.event.needs_review', ['%inv%' => $inv]),
|
||||
default => "{$label}: {$status}",
|
||||
};
|
||||
|
||||
return [
|
||||
'jobId' => $job->getId()->toRfc4122(),
|
||||
'label' => $label,
|
||||
'status' => $status,
|
||||
'step' => $step,
|
||||
'jobId' => $job->getId()->toRfc4122(),
|
||||
'label' => $label,
|
||||
'status' => $status,
|
||||
'step' => $step,
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ final class PromptTemplateCrudController extends AbstractCrudController
|
|||
$lines = ['Use <code>{{variableName}}</code> as placeholders. Known keys and their variables:'];
|
||||
$lines[] = '<ul>';
|
||||
foreach ($known as $key => $vars) {
|
||||
$varList = $vars !== [] ? implode(', ', array_map(static fn (string $v) => "<code>{{$v}}</code>", $vars)) : '—';
|
||||
$varList = [] !== $vars ? implode(', ', array_map(static fn (string $v) => "<code>{{$v}}</code>", $vars)) : '—';
|
||||
$lines[] = "<li><strong>{$key}</strong>: {$varList}</li>";
|
||||
}
|
||||
$lines[] = '</ul>';
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ final class TranslationCrudController extends AbstractCrudController
|
|||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
$readonly = $pageName === Crud::PAGE_EDIT;
|
||||
$readonly = Crud::PAGE_EDIT === $pageName;
|
||||
|
||||
yield IdField::new('id')->hideOnForm()->hideOnIndex();
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ final class StringArrayType extends AbstractType
|
|||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addModelTransformer(new CallbackTransformer(
|
||||
fn (?array $v) => implode("\n", $v ?? []),
|
||||
fn (mixed $v) => '' === ($v ?? '') ? null : array_values(
|
||||
static fn (?array $v) => implode("\n", $v ?? []),
|
||||
static fn (mixed $v) => '' === ($v ?? '') ? null : array_values(
|
||||
array_filter(array_map('trim', explode("\n", (string) $v)))
|
||||
),
|
||||
));
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ final class DraftArticleHandler
|
|||
$article->setManufacturer((string) $vision['manufacturer']);
|
||||
}
|
||||
$correctedModelNumber = (string) ($job->getOutputData()['specs_research']['correctedModelNumber'] ?? '');
|
||||
$modelNumber = $correctedModelNumber !== '' ? $correctedModelNumber : (string) ($vision['modelNumber'] ?? '');
|
||||
$modelNumber = '' !== $correctedModelNumber ? $correctedModelNumber : (string) ($vision['modelNumber'] ?? '');
|
||||
if ('' !== $modelNumber) {
|
||||
$article->setModelNumber($modelNumber);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ final class PhotoUploadHandler
|
|||
attributes: $attributes,
|
||||
condition: $inputData['condition'] ?? 'good',
|
||||
inventoryNumber: $inputData['inventoryNumber'] ?? null,
|
||||
serialNumber: $result['serial'] !== '' ? $result['serial'] : null,
|
||||
serialNumber: '' !== $result['serial'] ? $result['serial'] : null,
|
||||
));
|
||||
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ use Doctrine\ORM\EntityManagerInterface;
|
|||
|
||||
final class DoctrineTranslationRepository implements TranslationRepositoryInterface
|
||||
{
|
||||
public function __construct(private readonly EntityManagerInterface $em) {}
|
||||
public function __construct(private readonly EntityManagerInterface $em)
|
||||
{
|
||||
}
|
||||
|
||||
public function findMapByLocaleAndDomain(string $locale, string $domain): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ final class PermissionVoter extends Voter
|
|||
public const PREFIX = 'PERM_';
|
||||
|
||||
public const ARTICLES_MANAGE = 'articles.manage';
|
||||
public const PIPELINE_RUN = 'pipeline.run';
|
||||
public const ORDERS_MANAGE = 'orders.manage';
|
||||
public const USERS_MANAGE = 'users.manage';
|
||||
public const PROMPTS_MANAGE = 'prompts.manage';
|
||||
public const PIPELINE_RUN = 'pipeline.run';
|
||||
public const ORDERS_MANAGE = 'orders.manage';
|
||||
public const USERS_MANAGE = 'users.manage';
|
||||
public const PROMPTS_MANAGE = 'prompts.manage';
|
||||
public const SETTINGS_MANAGE = 'settings.manage';
|
||||
|
||||
/** @return list<string> */
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ final class DatabaseTranslator implements TranslatorInterface, LocaleAwareInterf
|
|||
$effectiveDomain = $domain ?? 'messages';
|
||||
$cacheKey = $effectiveLocale.'.'.$effectiveDomain;
|
||||
|
||||
if (!array_key_exists($cacheKey, $this->cache)) {
|
||||
if (!\array_key_exists($cacheKey, $this->cache)) {
|
||||
try {
|
||||
$this->cache[$cacheKey] = $this->repo->findMapByLocaleAndDomain($effectiveLocale, $effectiveDomain);
|
||||
} catch (\Throwable) {
|
||||
|
|
@ -36,10 +36,10 @@ final class DatabaseTranslator implements TranslatorInterface, LocaleAwareInterf
|
|||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($id, $this->cache[$cacheKey])) {
|
||||
if (\array_key_exists($id, $this->cache[$cacheKey])) {
|
||||
$value = $this->cache[$cacheKey][$id];
|
||||
|
||||
return $parameters !== [] ? strtr($value, $parameters) : $value;
|
||||
return [] !== $parameters ? strtr($value, $parameters) : $value;
|
||||
}
|
||||
|
||||
return $this->inner->trans($id, $parameters, $domain, $locale);
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ final class EbayTaxonomyIntegrationTest extends TestCase
|
|||
);
|
||||
}
|
||||
|
||||
public function test_fetches_application_token(): void
|
||||
public function testFetchesApplicationToken(): void
|
||||
{
|
||||
$token = $this->oauth->getAccessToken();
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ final class EbayTaxonomyIntegrationTest extends TestCase
|
|||
$this->assertStringStartsWith('v^1.1', $token);
|
||||
}
|
||||
|
||||
public function test_fetches_aspects_for_notebooks_category(): void
|
||||
public function testFetchesAspectsForNotebooksCategory(): void
|
||||
{
|
||||
// 177 = Notebooks & Netbooks in EBAY_DE
|
||||
$aspects = $this->taxonomy->getCategoryAspects('177');
|
||||
|
|
@ -89,7 +89,7 @@ final class EbayTaxonomyIntegrationTest extends TestCase
|
|||
);
|
||||
}
|
||||
|
||||
public function test_three_tier_aspect_classification(): void
|
||||
public function testThreeTierAspectClassification(): void
|
||||
{
|
||||
// eBay has three effective tiers:
|
||||
// required=true + usage=RECOMMENDED → hard gate, eBay blocks listing without it
|
||||
|
|
@ -98,15 +98,15 @@ final class EbayTaxonomyIntegrationTest extends TestCase
|
|||
$aspects = $this->taxonomy->getCategoryAspects('177');
|
||||
|
||||
$hardRequired = array_filter($aspects, static fn (array $a) => $a['required']);
|
||||
$recommended = array_filter($aspects, static fn (array $a) => !$a['required'] && 'RECOMMENDED' === $a['usage']);
|
||||
$optional = array_filter($aspects, static fn (array $a) => !$a['required'] && 'OPTIONAL' === $a['usage']);
|
||||
$recommended = array_filter($aspects, static fn (array $a) => !$a['required'] && 'RECOMMENDED' === $a['usage']);
|
||||
$optional = array_filter($aspects, static fn (array $a) => !$a['required'] && 'OPTIONAL' === $a['usage']);
|
||||
|
||||
$this->assertNotEmpty($hardRequired, 'Should have hard-required aspects');
|
||||
$this->assertNotEmpty($recommended, 'Should have recommended (ranking-signal) aspects');
|
||||
$this->assertNotEmpty($optional, 'Should have truly optional aspects');
|
||||
}
|
||||
|
||||
public function test_aspects_with_predefined_values_have_options(): void
|
||||
public function testAspectsWithPredefinedValuesHaveOptions(): void
|
||||
{
|
||||
$aspects = $this->taxonomy->getCategoryAspects('177');
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ final class EbayTaxonomyIntegrationTest extends TestCase
|
|||
$this->assertNotEmpty($withOptions, 'Some aspects should have predefined selectable values');
|
||||
}
|
||||
|
||||
public function test_caches_aspects_on_second_call(): void
|
||||
public function testCachesAspectsOnSecondCall(): void
|
||||
{
|
||||
// First call hits the API
|
||||
$first = $this->taxonomy->getCategoryAspects('177');
|
||||
|
|
@ -125,7 +125,7 @@ final class EbayTaxonomyIntegrationTest extends TestCase
|
|||
$this->assertSame($first, $second);
|
||||
}
|
||||
|
||||
public function test_fetches_aspects_for_ram_category(): void
|
||||
public function testFetchesAspectsForRamCategory(): void
|
||||
{
|
||||
// 170083 = RAM/Speicher in EBAY_DE — useful for our memory article types
|
||||
$aspects = $this->taxonomy->getCategoryAspects('170083');
|
||||
|
|
@ -135,7 +135,7 @@ final class EbayTaxonomyIntegrationTest extends TestCase
|
|||
$this->assertNotEmpty($names);
|
||||
}
|
||||
|
||||
public function test_category_suggestions_returns_results(): void
|
||||
public function testCategorySuggestionsReturnsResults(): void
|
||||
{
|
||||
$results = $this->taxonomy->getCategorySuggestions('Notebook');
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ final class FrappeCustomerIntegrationTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
public function test_create_customer(): void
|
||||
public function testCreateCustomer(): void
|
||||
{
|
||||
$response = $this->client->post('/api/resource/Customer', [
|
||||
'customer_name' => 'Test Superseller Integration',
|
||||
|
|
@ -64,7 +64,7 @@ final class FrappeCustomerIntegrationTest extends TestCase
|
|||
$this->createdCustomerName = $response['data']['name'];
|
||||
}
|
||||
|
||||
public function test_find_created_customer(): void
|
||||
public function testFindCreatedCustomer(): void
|
||||
{
|
||||
// Create first
|
||||
$created = $this->client->post('/api/resource/Customer', [
|
||||
|
|
@ -82,14 +82,14 @@ final class FrappeCustomerIntegrationTest extends TestCase
|
|||
$this->assertSame('Test Superseller Integration', $response['data']['customer_name']);
|
||||
}
|
||||
|
||||
public function test_find_nonexistent_customer_throws(): void
|
||||
public function testFindNonexistentCustomerThrows(): void
|
||||
{
|
||||
$this->expectException(ClientExceptionInterface::class);
|
||||
|
||||
$this->client->get('/api/resource/Customer/CUST-DOES-NOT-EXIST-99999');
|
||||
}
|
||||
|
||||
public function test_delete_customer(): void
|
||||
public function testDeleteCustomer(): void
|
||||
{
|
||||
// Create
|
||||
$created = $this->client->post('/api/resource/Customer', [
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ final class FrappeErpAdapterIntegrationTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
public function test_find_simon_kuehn_by_name_and_address(): void
|
||||
public function testFindSimonKuehnByNameAndAddress(): void
|
||||
{
|
||||
$customerId = $this->adapter->findExistingCustomer(
|
||||
'Simon Kühn',
|
||||
|
|
@ -74,7 +74,7 @@ final class FrappeErpAdapterIntegrationTest extends TestCase
|
|||
$this->assertStringStartsWith('Simon', $customerId);
|
||||
}
|
||||
|
||||
public function test_unknown_person_is_not_found(): void
|
||||
public function testUnknownPersonIsNotFound(): void
|
||||
{
|
||||
$customerId = $this->adapter->findExistingCustomer(
|
||||
'Voldemort',
|
||||
|
|
@ -86,7 +86,7 @@ final class FrappeErpAdapterIntegrationTest extends TestCase
|
|||
$this->assertNull($customerId);
|
||||
}
|
||||
|
||||
public function test_find_simon_and_create_invoice_for_1337(): void
|
||||
public function testFindSimonAndCreateInvoiceFor1337(): void
|
||||
{
|
||||
$frappeId = $this->adapter->findExistingCustomer(
|
||||
'Simon Kühn',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||
namespace App\Tests\Unit\Domain\Article;
|
||||
|
||||
use App\Domain\Article\ArticleType;
|
||||
use App\Domain\Article\ArticleTypeAttribute;
|
||||
use App\Domain\Article\AttributeDefinition;
|
||||
use App\Domain\Article\AttributeType;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ final class AttributeDefinitionTest extends TestCase
|
|||
public static function allAttributeTypes(): array
|
||||
{
|
||||
return array_combine(
|
||||
array_map(fn (AttributeType $t) => $t->value, AttributeType::cases()),
|
||||
array_map(fn (AttributeType $t) => [$t], AttributeType::cases()),
|
||||
array_map(static fn (AttributeType $t) => $t->value, AttributeType::cases()),
|
||||
array_map(static fn (AttributeType $t) => [$t], AttributeType::cases()),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ final class ApiKeyTest extends TestCase
|
|||
public function testRawKeyVerifiesAgainstStoredHash(): void
|
||||
{
|
||||
$rawKey = bin2hex(random_bytes(24));
|
||||
$hash = password_hash($rawKey, PASSWORD_BCRYPT);
|
||||
$hash = password_hash($rawKey, \PASSWORD_BCRYPT);
|
||||
$prefix = substr($rawKey, 0, 8);
|
||||
|
||||
$key = new ApiKey($this->user, 'label', $prefix, $hash);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Tests\Unit\Infrastructure\AI\Agent;
|
||||
|
||||
use App\Domain\AI\PromptTemplate;
|
||||
use App\Domain\AI\Repository\PromptTemplateRepositoryInterface;
|
||||
use App\Domain\Article\ArticleType;
|
||||
use App\Domain\Article\AttributeDefinition;
|
||||
use App\Domain\Article\AttributeType;
|
||||
use App\Domain\AI\PromptTemplate;
|
||||
use App\Domain\AI\Repository\PromptTemplateRepositoryInterface;
|
||||
use App\Infrastructure\AI\Agent\JsonCodingAgent;
|
||||
use App\Infrastructure\AI\OllamaClientInterface;
|
||||
use App\Infrastructure\AI\PromptTemplateService;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ final class OllamaVisionAgentTest extends TestCase
|
|||
$this->agent = new OllamaVisionAgent($this->ollama, $prompts, 'llava');
|
||||
}
|
||||
|
||||
public function test_parses_all_fields(): void
|
||||
public function testParsesAllFields(): void
|
||||
{
|
||||
$this->ollama->method('generateWithImage')->willReturn(
|
||||
"MANUFACTURER: Lenovo\nMODEL_NAME: ThinkBook 14 G6 IRL\nMODEL_NUMBER: 21KG00NQGE\nSERIAL: PNV09SJZ"
|
||||
|
|
@ -42,7 +42,7 @@ final class OllamaVisionAgentTest extends TestCase
|
|||
$this->assertSame('PNV09SJZ', $result['serial']);
|
||||
}
|
||||
|
||||
public function test_strips_serial_bleed_into_model_number_leading(): void
|
||||
public function testStripsSerialBleedIntoModelNumberLeading(): void
|
||||
{
|
||||
// LLM outputs "SERIAL: xyz" as the entire value for MODEL_NUMBER
|
||||
$this->ollama->method('generateWithImage')->willReturn(
|
||||
|
|
@ -55,7 +55,7 @@ final class OllamaVisionAgentTest extends TestCase
|
|||
$this->assertSame('1005NK677594', $result['serial']);
|
||||
}
|
||||
|
||||
public function test_strips_serial_bleed_into_model_name_mid_value(): void
|
||||
public function testStripsSerialBleedIntoModelNameMidValue(): void
|
||||
{
|
||||
// LLM appends serial after the model name
|
||||
$this->ollama->method('generateWithImage')->willReturn(
|
||||
|
|
@ -68,7 +68,7 @@ final class OllamaVisionAgentTest extends TestCase
|
|||
$this->assertSame('PNV09SJZ', $result['serial']);
|
||||
}
|
||||
|
||||
public function test_returns_empty_strings_when_fields_missing(): void
|
||||
public function testReturnsEmptyStringsWhenFieldsMissing(): void
|
||||
{
|
||||
$this->ollama->method('generateWithImage')->willReturn('I cannot read the nameplate.');
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ final class MistralClientTest extends TestCase
|
|||
|
||||
public function testGenerateWithImageReturnsParsedContent(): void
|
||||
{
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'test_') . '.jpg';
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'test_').'.jpg';
|
||||
file_put_contents($tmpFile, 'fake-image-data');
|
||||
|
||||
$body = json_encode([
|
||||
|
|
@ -66,7 +66,7 @@ final class MistralClientTest extends TestCase
|
|||
|
||||
public function testGenerateWithImageEncodesImageAsBase64(): void
|
||||
{
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'test_') . '.png';
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'test_').'.png';
|
||||
$imageContent = 'fake-png-bytes';
|
||||
file_put_contents($tmpFile, $imageContent);
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ final class MistralClientTest extends TestCase
|
|||
*/
|
||||
public function testGuessMimeTypeFromExtension(string $filename, string $expectedMime): void
|
||||
{
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'test_') . '.' . pathinfo($filename, PATHINFO_EXTENSION);
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'test_').'.'.pathinfo($filename, \PATHINFO_EXTENSION);
|
||||
file_put_contents($tmpFile, 'x');
|
||||
|
||||
$body = json_encode(['choices' => [['message' => ['content' => 'ok']]]]);
|
||||
|
|
@ -105,7 +105,7 @@ final class MistralClientTest extends TestCase
|
|||
$requestBody = json_decode($response->getRequestOptions()['body'], true);
|
||||
$imageUrl = $requestBody['messages'][0]['content'][1]['image_url']['url'];
|
||||
|
||||
self::assertStringStartsWith('data:' . $expectedMime . ';base64,', $imageUrl);
|
||||
self::assertStringStartsWith('data:'.$expectedMime.';base64,', $imageUrl);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string}> */
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ final class ArticleTypeCrudControllerTest extends TestCase
|
|||
public function testConfigureFieldsContainsExpectedProperties(): void
|
||||
{
|
||||
$fields = iterator_to_array($this->controller->configureFields('new'));
|
||||
$names = array_map(fn ($f) => $f->getAsDto()->getProperty(), $fields);
|
||||
$names = array_map(static fn ($f) => $f->getAsDto()->getProperty(), $fields);
|
||||
|
||||
self::assertContains('name', $names);
|
||||
self::assertContains('requiredAttributeDefs', $names);
|
||||
|
|
@ -43,7 +43,7 @@ final class ArticleTypeCrudControllerTest extends TestCase
|
|||
public function testConfigureFieldsForIndexContainsNameAndCount(): void
|
||||
{
|
||||
$fields = iterator_to_array($this->controller->configureFields('index'));
|
||||
$names = array_map(fn ($f) => $f->getAsDto()->getProperty(), $fields);
|
||||
$names = array_map(static fn ($f) => $f->getAsDto()->getProperty(), $fields);
|
||||
|
||||
self::assertContains('name', $names);
|
||||
self::assertContains('attributeAssignments', $names);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ final class AttributeDefinitionCrudControllerTest extends TestCase
|
|||
public function testConfigureFieldsYieldsExpectedFieldNames(): void
|
||||
{
|
||||
$fields = iterator_to_array($this->controller->configureFields('new'));
|
||||
$names = array_map(fn ($f) => $f->getAsDto()->getProperty(), $fields);
|
||||
$names = array_map(static fn ($f) => $f->getAsDto()->getProperty(), $fields);
|
||||
|
||||
self::assertContains('name', $names);
|
||||
self::assertContains('type', $names);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ final class EbayWebhookVerifierTest extends TestCase
|
|||
);
|
||||
}
|
||||
|
||||
public function test_valid_signature_passes(): void
|
||||
public function testValidSignaturePasses(): void
|
||||
{
|
||||
$body = '{"notification":{"data":{"orderId":"123"}}}';
|
||||
$expected = base64_encode(hash('sha256', $body.'my-secret-tokenhttps://example.com/webhooks/ebay', binary: true));
|
||||
|
|
@ -27,12 +27,12 @@ final class EbayWebhookVerifierTest extends TestCase
|
|||
$this->assertTrue($this->verifier->verify($body, $expected));
|
||||
}
|
||||
|
||||
public function test_invalid_signature_fails(): void
|
||||
public function testInvalidSignatureFails(): void
|
||||
{
|
||||
$this->assertFalse($this->verifier->verify('{"body":"x"}', 'invalidsignature'));
|
||||
}
|
||||
|
||||
public function test_challenge_response_returns_correct_hash(): void
|
||||
public function testChallengeResponseReturnsCorrectHash(): void
|
||||
{
|
||||
$challengeCode = 'abc123';
|
||||
$expected = hash('sha256', $challengeCode.'my-secret-tokenhttps://example.com/webhooks/ebay');
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ final class FrappeErpAdapterTest extends TestCase
|
|||
$this->adapter = new FrappeErpAdapter($this->frappe, 'REFURB-HW');
|
||||
}
|
||||
|
||||
public function test_create_customer_returns_frappe_id(): void
|
||||
public function testCreateCustomerReturnsFrappeId(): void
|
||||
{
|
||||
$this->frappe
|
||||
->method('post')
|
||||
|
|
@ -41,7 +41,7 @@ final class FrappeErpAdapterTest extends TestCase
|
|||
$this->assertSame('CUST-00001', $result);
|
||||
}
|
||||
|
||||
public function test_create_sales_invoice_submits_and_returns_id(): void
|
||||
public function testCreateSalesInvoiceSubmitsAndReturnsId(): void
|
||||
{
|
||||
$this->frappe
|
||||
->expects($this->once())
|
||||
|
|
@ -71,7 +71,7 @@ final class FrappeErpAdapterTest extends TestCase
|
|||
$this->assertSame('SINV-00001', $result);
|
||||
}
|
||||
|
||||
public function test_fetch_invoice_pdf_returns_binary(): void
|
||||
public function testFetchInvoicePdfReturnsBinary(): void
|
||||
{
|
||||
$this->frappe
|
||||
->method('getContent')
|
||||
|
|
@ -83,7 +83,7 @@ final class FrappeErpAdapterTest extends TestCase
|
|||
$this->assertStringStartsWith('%PDF', $result);
|
||||
}
|
||||
|
||||
public function test_find_existing_customer_returns_id_when_address_matches(): void
|
||||
public function testFindExistingCustomerReturnsIdWhenAddressMatches(): void
|
||||
{
|
||||
$this->frappe
|
||||
->expects($this->exactly(2))
|
||||
|
|
@ -98,7 +98,7 @@ final class FrappeErpAdapterTest extends TestCase
|
|||
$this->assertSame('CUST-99999', $result);
|
||||
}
|
||||
|
||||
public function test_find_existing_customer_returns_null_when_address_mismatch(): void
|
||||
public function testFindExistingCustomerReturnsNullWhenAddressMismatch(): void
|
||||
{
|
||||
$this->frappe
|
||||
->expects($this->exactly(2))
|
||||
|
|
@ -113,7 +113,7 @@ final class FrappeErpAdapterTest extends TestCase
|
|||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function test_find_existing_customer_returns_null_when_not_in_erp(): void
|
||||
public function testFindExistingCustomerReturnsNullWhenNotInErp(): void
|
||||
{
|
||||
$this->frappe
|
||||
->expects($this->once())
|
||||
|
|
@ -125,7 +125,7 @@ final class FrappeErpAdapterTest extends TestCase
|
|||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function test_find_simon_and_create_invoice_for_1337(): void
|
||||
public function testFindSimonAndCreateInvoiceFor1337(): void
|
||||
{
|
||||
$this->frappe
|
||||
->expects($this->exactly(2))
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ final class CreateApiKeyCommandTest extends TestCase
|
|||
$savedKey = null;
|
||||
$this->apiKeys->expects($this->once())
|
||||
->method('save')
|
||||
->willReturnCallback(function (ApiKey $key) use (&$savedKey): void {
|
||||
->willReturnCallback(static function (ApiKey $key) use (&$savedKey): void {
|
||||
$savedKey = $key;
|
||||
});
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ final class CreateApiKeyCommandTest extends TestCase
|
|||
|
||||
self::assertNotNull($savedKey);
|
||||
self::assertSame('dev laptop', $savedKey->getLabel());
|
||||
self::assertSame(8, strlen($savedKey->getKeyPrefix()));
|
||||
self::assertSame(8, \strlen($savedKey->getKeyPrefix()));
|
||||
self::assertStringContainsString($savedKey->getKeyPrefix(), $display);
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ final class CreateApiKeyCommandTest extends TestCase
|
|||
|
||||
$savedKey = null;
|
||||
$this->apiKeys->method('save')
|
||||
->willReturnCallback(function (ApiKey $key) use (&$savedKey): void {
|
||||
->willReturnCallback(static function (ApiKey $key) use (&$savedKey): void {
|
||||
$savedKey = $key;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ final class CustomerResolverTest extends TestCase
|
|||
$this->resolver = new CustomerResolver($this->customerRepo, $this->erp);
|
||||
}
|
||||
|
||||
public function test_stage_1_platform_id_match_returns_existing_customer_without_erp_call(): void
|
||||
public function testStage1PlatformIdMatchReturnsExistingCustomerWithoutErpCall(): void
|
||||
{
|
||||
$existing = new Customer('Max Mustermann', 'max@test.de', ['street' => 'Musterstr 1', 'city' => 'Berlin', 'zip' => '10115']);
|
||||
$existing->addPlatformId('ebay', 'buyer123');
|
||||
|
|
@ -46,7 +46,7 @@ final class CustomerResolverTest extends TestCase
|
|||
$this->assertSame($existing, $result);
|
||||
}
|
||||
|
||||
public function test_stage_2_address_match_adds_platform_id_and_saves(): void
|
||||
public function testStage2AddressMatchAddsPlatformIdAndSaves(): void
|
||||
{
|
||||
$existing = new Customer('Max Mustermann', 'max@test.de', ['street' => 'Musterstr 1', 'city' => 'Berlin', 'zip' => '10115']);
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ final class CustomerResolverTest extends TestCase
|
|||
$this->assertSame('buyer456', $result->getPlatformId('ebay'));
|
||||
}
|
||||
|
||||
public function test_no_match_creates_new_customer_via_erp(): void
|
||||
public function testNoMatchCreatesNewCustomerViaErp(): void
|
||||
{
|
||||
$this->customerRepo->method('findByPlatformId')->willReturn(null);
|
||||
$this->customerRepo->method('findByMatchingKey')->willReturn(null);
|
||||
|
|
|
|||
Loading…
Reference in a new issue