File: /home/xedaptot/ai.naniguide.com/tests/Unit/OrderFulfillment/OrderItemTypesLabelsTest.php
<?php
/**
* OrderItemTypes::label() / icon() / tone() — single source of truth for type display.
*
* Guards against the regression where raw class names (e.g. "subscription.new",
* or worse "App\\Model\\OrderItemNewSubscription") leak into the admin invoice list
* because the blade falls through a missing trans key. See:
* - docs/automated/features/admin-subscriptions-invoices.md (BUG #1)
* - CLAUDE.md HARD RULE #2 (no fall-through on enum/state)
*/
use App\Library\OrderFulfillment\OrderItemTypes;
use Tests\TestCase;
uses(TestCase::class);
it('resolves a translated label for every known type', function (string $type) {
$label = OrderItemTypes::label($type);
expect($label)->toBeString()->not->toBe('');
expect($label)->not->toContain('refactor/admin_invoices');
expect($label)->not->toBe($type);
})->with(OrderItemTypes::all());
it('returns "—" for null/empty type', function () {
expect(OrderItemTypes::label(null))->toBe('—');
expect(OrderItemTypes::label(''))->toBe('—');
});
it('falls back to a humanised form for unknown types instead of leaking the code', function () {
$unknown = 'mystery.payload';
$label = OrderItemTypes::label($unknown);
expect($label)->not->toBe($unknown);
expect($label)->toContain('Mystery');
});
it('maps every known type to a non-empty material icon', function (string $type) {
expect(OrderItemTypes::icon($type))->toBeString()->not->toBe('');
})->with(OrderItemTypes::all());
it('returns "receipt" icon as the safe fallback for unknown types', function () {
expect(OrderItemTypes::icon(null))->toBe('receipt');
expect(OrderItemTypes::icon('foo.bar'))->toBe('receipt');
});
it('maps every known type to a non-empty tone token', function (string $type) {
expect(OrderItemTypes::tone($type))->toBeString()->not->toBe('');
})->with(OrderItemTypes::all());
it('returns "default" tone as the fallback', function () {
expect(OrderItemTypes::tone(null))->toBe('default');
expect(OrderItemTypes::tone('foo.bar'))->toBe('default');
});
/* ─────────── Legacy FQCN aliases — pre-schema-refactor data ─────────── */
it('label() resolves legacy FQCN OrderItem* class names to the modern label', function () {
expect(OrderItemTypes::label('App\\Model\\OrderItemNewSubscription'))
->toBe(OrderItemTypes::label(OrderItemTypes::SUBSCRIPTION_NEW));
expect(OrderItemTypes::label('App\\Model\\OrderItemRenewSubscription'))
->toBe(OrderItemTypes::label(OrderItemTypes::SUBSCRIPTION_RENEW));
expect(OrderItemTypes::label('App\\Model\\OrderItemChangePlan'))
->toBe(OrderItemTypes::label(OrderItemTypes::SUBSCRIPTION_CHANGE_PLAN));
expect(OrderItemTypes::label('App\\Model\\OrderItemSendingCreditsTopup'))
->toBe(OrderItemTypes::label(OrderItemTypes::CREDIT_SENDING_TOPUP));
});
it('icon() and tone() also normalise legacy FQCN before mapping', function () {
expect(OrderItemTypes::icon('App\\Model\\OrderItemNewSubscription'))->toBe('person_add');
expect(OrderItemTypes::icon('App\\Model\\OrderItemRenewSubscription'))->toBe('autorenew');
expect(OrderItemTypes::tone('App\\Model\\OrderItemNewSubscription'))->toBe('teal');
expect(OrderItemTypes::tone('App\\Model\\OrderItemRenewSubscription'))->toBe('blue');
});
it('label() never leaks "App\\Model\\" prefix even for unknown legacy FQCN', function () {
$unknown = 'App\\Model\\OrderItemMysteryThing';
$label = OrderItemTypes::label($unknown);
// Falls through to humaniser — still must not contain the raw class path or the slashes.
expect($label)->not->toContain('App\\')
->not->toContain('App\\Model')
->not->toBe($unknown);
});