Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.20% covered (success)
90.20%
46 / 51
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
NuvemshopProductService
90.20% covered (success)
90.20%
46 / 51
50.00% covered (danger)
50.00%
3 / 6
13.16
0.00% covered (danger)
0.00%
0 / 1
 createTestProduct
95.45% covered (success)
95.45%
21 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 deleteProduct
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 checkoutHint
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 assertIntegration
50.00% covered (danger)
50.00%
3 / 6
0.00% covered (danger)
0.00%
0 / 1
8.12
 http
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 url
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Services\Nuvemshop;
4
5use App\Models\StoreIntegration;
6use Illuminate\Support\Facades\Http;
7use RuntimeException;
8
9class NuvemshopProductService
10{
11    public function createTestProduct(StoreIntegration $integration, array $overrides = []): array
12    {
13        $this->assertIntegration($integration, 'write_products');
14
15        $name = $overrides['name'] ?? '[ORVOX TESTE] Produto automático '.now()->format('YmdHis');
16        $price = (float) ($overrides['price'] ?? 1.00);
17        $sku = $overrides['sku'] ?? 'ORVOX-TEST-'.now()->format('YmdHis');
18
19        $payload = array_replace_recursive([
20            'name' => $name,
21            'description' => 'Produto criado automaticamente pelo teste E2E do Orvox Influencers. Pode ser excluído.',
22            'published' => false,
23            'free_shipping' => false,
24            'variants' => [[
25                'price' => number_format($price, 2, '.', ''),
26                'promotional_price' => null,
27                'stock_management' => false,
28                'stock' => 999,
29                'sku' => $sku,
30            ]],
31        ], $overrides['payload'] ?? []);
32
33        $response = $this->http($integration)
34            ->post($this->url($integration, '/products'), $payload);
35
36        if ($response->failed()) {
37            throw new RuntimeException('Falha ao criar produto de teste na Nuvemshop: '.$response->body());
38        }
39
40        return $response->json() ?? [];
41    }
42
43    public function deleteProduct(StoreIntegration $integration, string|int $productId): void
44    {
45        $this->assertIntegration($integration, 'write_products');
46
47        $response = $this->http($integration)
48            ->delete($this->url($integration, '/products/'.$productId));
49
50        if ($response->failed() && $response->status() !== 404) {
51            throw new RuntimeException('Falha ao excluir produto de teste na Nuvemshop: '.$response->body());
52        }
53    }
54
55    public function checkoutHint(array $product): string
56    {
57        return (string) (
58            data_get($product, 'canonical_url')
59            ?? data_get($product, 'url')
60            ?? data_get($product, 'handle.pt')
61            ?? data_get($product, 'handle.es')
62            ?? data_get($product, 'id')
63            ?? 'Produto criado, mas a API não retornou URL pública.'
64        );
65    }
66
67    private function assertIntegration(StoreIntegration $integration, string $scope): void
68    {
69        if ((bool) config('services.nuvemshop.mock')) {
70            throw new RuntimeException('NUVEMSHOP_MOCK=true. Desative mock para testes reais na Nuvemshop.');
71        }
72
73        if (blank($integration->external_store_id) || blank($integration->access_token)) {
74            throw new RuntimeException('Integração Nuvemshop sem external_store_id ou access_token.');
75        }
76
77        if (! in_array($scope, $integration->scopes ?? [], true)) {
78            throw new RuntimeException('A integração Nuvemshop não possui o scope necessário: '.$scope);
79        }
80    }
81
82    private function http(StoreIntegration $integration): \Illuminate\Http\Client\PendingRequest
83    {
84        return Http::acceptJson()
85            ->asJson()
86            ->withToken($integration->access_token)
87            ->withHeaders([
88                'User-Agent' => config('services.nuvemshop.user_agent', 'Orvox Influencers Test Suite'),
89            ])
90            ->timeout(30)
91            ->retry(2, 500);
92    }
93
94    private function url(StoreIntegration $integration, string $path): string
95    {
96        $base = rtrim((string) config('services.nuvemshop.api_base_url', 'https://api.tiendanube.com/v1'), '/');
97
98        return $base.'/'.$integration->external_store_id.'/'.ltrim($path, '/');
99    }
100}