Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
CleanupNuvemshopE2ECommand
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 3
506
0.00% covered (danger)
0.00%
0 / 1
 handle
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
210
 deleteCouponDirectly
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 cleanupLocal
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace App\Console\Commands;
4
5use App\Enums\CouponStatus;
6use App\Models\Influencer;
7use App\Models\InfluencerCoupon;
8use App\Models\Order;
9use App\Models\StoreIntegration;
10use App\Models\User;
11use App\Services\Nuvemshop\NuvemshopProductService;
12use App\Services\Nuvemshop\NuvemshopWebhookRegistrationService;
13use Illuminate\Console\Command;
14use Illuminate\Support\Facades\File;
15use Illuminate\Support\Facades\Http;
16use Throwable;
17
18class CleanupNuvemshopE2ECommand extends Command
19{
20    protected $signature = 'orvox:test:nuvemshop-cleanup
21        {--manifest=storage/app/orvox-nuvemshop-e2e-manifest.json : Caminho do manifest gerado no E2E}
22        {--keep-local : Não apaga dados locais, apenas Nuvemshop}';
23
24    protected $description = 'Remove produto/cupom/webhooks criados para teste E2E da Nuvemshop e limpa dados locais de teste.';
25
26    public function handle(NuvemshopProductService $productService, NuvemshopWebhookRegistrationService $webhookService): int
27    {
28        $manifestPath = base_path((string) $this->option('manifest'));
29
30        if (! File::exists($manifestPath)) {
31            $this->error('Manifest não encontrado: '.$manifestPath);
32            return self::FAILURE;
33        }
34
35        $manifest = json_decode(File::get($manifestPath), true) ?: [];
36        $integration = StoreIntegration::find(data_get($manifest, 'integration_id'));
37
38        if (! $integration) {
39            $this->error('Integração não encontrada no manifest.');
40            return self::FAILURE;
41        }
42
43        $errors = [];
44
45        if ($productId = data_get($manifest, 'nuvemshop.product_id')) {
46            try {
47                $productService->deleteProduct($integration, $productId);
48                $this->info('Produto removido da Nuvemshop: '.$productId);
49            } catch (Throwable $exception) {
50                $errors[] = 'Produto '.$productId.': '.$exception->getMessage();
51            }
52        }
53
54        foreach ((array) data_get($manifest, 'nuvemshop.webhooks', []) as $event => $webhook) {
55            $webhookId = data_get($webhook, 'id');
56            if (! $webhookId) {
57                continue;
58            }
59
60            try {
61                $webhookService->delete($integration, $webhookId);
62                $this->info('Webhook removido da Nuvemshop: '.$event.' #'.$webhookId);
63            } catch (Throwable $exception) {
64                $errors[] = 'Webhook '.$webhookId.': '.$exception->getMessage();
65            }
66        }
67
68        if ($couponId = data_get($manifest, 'nuvemshop.coupon_id')) {
69            try {
70                $this->deleteCouponDirectly($integration, $couponId);
71                $this->info('Cupom removido da Nuvemshop: '.$couponId);
72            } catch (Throwable $exception) {
73                $errors[] = 'Cupom '.$couponId.': '.$exception->getMessage();
74            }
75        }
76
77        if (! $this->option('keep-local')) {
78            $this->cleanupLocal($manifest);
79        }
80
81        if ($errors) {
82            $this->warn('Cleanup finalizado com avisos:');
83            foreach ($errors as $error) {
84                $this->warn('- '.$error);
85            }
86            return self::FAILURE;
87        }
88
89        File::delete($manifestPath);
90        $this->info('Cleanup E2E finalizado.');
91
92        return self::SUCCESS;
93    }
94
95    private function deleteCouponDirectly(StoreIntegration $integration, string|int $couponId): void
96    {
97        $base = rtrim((string) config('services.nuvemshop.api_base_url', 'https://api.tiendanube.com/v1'), '/');
98
99        $response = Http::acceptJson()
100            ->asJson()
101            ->withToken($integration->access_token)
102            ->withHeaders(['User-Agent' => config('services.nuvemshop.user_agent', 'Orvox Influencers Test Suite')])
103            ->delete($base.'/'.$integration->external_store_id.'/coupons/'.$couponId);
104
105        if ($response->failed() && $response->status() !== 404) {
106            throw new \RuntimeException($response->body());
107        }
108    }
109
110    private function cleanupLocal(array $manifest): void
111    {
112        if ($orderId = data_get($manifest, 'local.simulated_order_id')) {
113            Order::whereKey($orderId)->delete();
114            $this->info('Pedido simulado local removido: '.$orderId);
115        }
116
117        if ($couponId = data_get($manifest, 'local.coupon_id')) {
118            InfluencerCoupon::whereKey($couponId)->update([
119                'status' => CouponStatus::Inactive->value,
120                'ended_at' => now(),
121            ]);
122            $this->info('Cupom local inativado: '.$couponId);
123        }
124
125        if ($influencerId = data_get($manifest, 'local.influencer_id')) {
126            Influencer::whereKey($influencerId)->delete();
127            $this->info('Influencer local removido: '.$influencerId);
128        }
129
130        if ($userId = data_get($manifest, 'local.user_id')) {
131            User::whereKey($userId)->delete();
132            $this->info('Usuário local removido: '.$userId);
133        }
134    }
135}