Skip to content

Commit 2eabd45

Browse files
authored
Merge pull request #20 from LibreCodeCoop/test/ufs-municipalities-fallback-coverage
test: harden SettingsController IBGE fallback coverage
2 parents 8b18155 + b3a7f7f commit 2eabd45

File tree

3 files changed

+167
-15
lines changed

3 files changed

+167
-15
lines changed

Http/Controllers/SettingsController.php

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,13 @@ public function edit(): \Illuminate\View\View
3333
public function ufs(IbgeLocalities $ibgeLocalities): JsonResponse
3434
{
3535
try {
36-
$rows = Http::timeout(8)
37-
->acceptJson()
38-
->get(self::IBGE_BASE_URL . '/estados')
39-
->throw()
40-
->json();
36+
$rows = $this->fetchUfsRows();
4137

42-
return response()->json([
38+
return $this->jsonResponse([
4339
'data' => $ibgeLocalities->mapUfs(is_array($rows) ? $rows : []),
4440
]);
4541
} catch (Throwable) {
46-
return response()->json([
42+
return $this->jsonResponse([
4743
'data' => [],
4844
'message' => 'Failed to load UFs from IBGE.',
4945
], 502);
@@ -54,24 +50,20 @@ public function municipalities(string $uf, IbgeLocalities $ibgeLocalities): Json
5450
{
5551
$normalizedUf = strtoupper(trim($uf));
5652
if (!preg_match('/^[A-Z]{2}$/', $normalizedUf)) {
57-
return response()->json([
53+
return $this->jsonResponse([
5854
'data' => [],
5955
'message' => 'Invalid UF.',
6056
], 422);
6157
}
6258

6359
try {
64-
$rows = Http::timeout(8)
65-
->acceptJson()
66-
->get(self::IBGE_BASE_URL . '/estados/' . $normalizedUf . '/municipios')
67-
->throw()
68-
->json();
60+
$rows = $this->fetchMunicipalitiesRows($normalizedUf);
6961

70-
return response()->json([
62+
return $this->jsonResponse([
7163
'data' => $ibgeLocalities->mapMunicipalities(is_array($rows) ? $rows : []),
7264
]);
7365
} catch (Throwable) {
74-
return response()->json([
66+
return $this->jsonResponse([
7567
'data' => [],
7668
'message' => 'Failed to load municipalities from IBGE.',
7769
], 502);
@@ -186,6 +178,33 @@ protected function prepareNfseInput(array $nfseInput, bool $isReplacingCertifica
186178
return $nfseInput;
187179
}
188180

181+
protected function fetchUfsRows(): array
182+
{
183+
$rows = Http::timeout(8)
184+
->acceptJson()
185+
->get(self::IBGE_BASE_URL . '/estados')
186+
->throw()
187+
->json();
188+
189+
return is_array($rows) ? $rows : [];
190+
}
191+
192+
protected function fetchMunicipalitiesRows(string $normalizedUf): array
193+
{
194+
$rows = Http::timeout(8)
195+
->acceptJson()
196+
->get(self::IBGE_BASE_URL . '/estados/' . $normalizedUf . '/municipios')
197+
->throw()
198+
->json();
199+
200+
return is_array($rows) ? $rows : [];
201+
}
202+
203+
protected function jsonResponse(array $payload, int $status = 200): JsonResponse
204+
{
205+
return response()->json($payload, $status);
206+
}
207+
189208
private function certificateState(): array
190209
{
191210
$cnpj = (string) setting('nfse.cnpj_prestador', '');

tests/Unit/Http/Controllers/SettingsControllerTest.php

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
}
1111

1212
namespace Modules\Nfse\Tests\Unit\Http\Controllers {
13+
use Illuminate\Http\JsonResponse;
1314
use Illuminate\Http\RedirectResponse;
1415
use Illuminate\Http\Request;
1516
use Illuminate\Http\UploadedFile;
@@ -19,6 +20,7 @@
1920

2021
use function Modules\Nfse\Http\Controllers\storage_path;
2122

23+
use Modules\Nfse\Support\IbgeLocalities;
2224
use Modules\Nfse\Tests\TestCase;
2325

2426
final class SettingsControllerTest extends TestCase
@@ -78,6 +80,133 @@ public function runPrepareNfseInput(array $nfseInput, bool $isReplacingCertifica
7880
self::assertSame('', $prepared['bao_secret_id']);
7981
}
8082

83+
public function testUfsReturnsMappedAndSortedDataWhenIbgeRowsAreAvailable(): void
84+
{
85+
$controller = new class () extends SettingsController {
86+
protected function fetchUfsRows(): array
87+
{
88+
return [
89+
['sigla' => 'sp', 'nome' => 'Sao Paulo'],
90+
['sigla' => 'rj', 'nome' => 'Rio de Janeiro'],
91+
['sigla' => 'x', 'nome' => 'Invalido'],
92+
];
93+
}
94+
95+
protected function jsonResponse(array $payload, int $status = 200): JsonResponse
96+
{
97+
return new JsonResponse($payload, $status);
98+
}
99+
};
100+
101+
$response = $controller->ufs(new IbgeLocalities());
102+
103+
self::assertSame(200, $response->getStatusCode());
104+
self::assertSame([
105+
'data' => [
106+
['uf' => 'RJ', 'name' => 'Rio de Janeiro'],
107+
['uf' => 'SP', 'name' => 'Sao Paulo'],
108+
],
109+
], $response->getData(true));
110+
}
111+
112+
public function testUfsReturnsFallbackWhenIbgeRequestFails(): void
113+
{
114+
$controller = new class () extends SettingsController {
115+
protected function fetchUfsRows(): array
116+
{
117+
throw new \RuntimeException('ibge unavailable');
118+
}
119+
120+
protected function jsonResponse(array $payload, int $status = 200): JsonResponse
121+
{
122+
return new JsonResponse($payload, $status);
123+
}
124+
};
125+
126+
$response = $controller->ufs(new IbgeLocalities());
127+
128+
self::assertSame(502, $response->getStatusCode());
129+
self::assertSame([
130+
'data' => [],
131+
'message' => 'Failed to load UFs from IBGE.',
132+
], $response->getData(true));
133+
}
134+
135+
public function testMunicipalitiesReturnsInvalidUfResponseWhenUfFormatIsInvalid(): void
136+
{
137+
$controller = new class () extends SettingsController {
138+
protected function jsonResponse(array $payload, int $status = 200): JsonResponse
139+
{
140+
return new JsonResponse($payload, $status);
141+
}
142+
};
143+
144+
$response = $controller->municipalities('r', new IbgeLocalities());
145+
146+
self::assertSame(422, $response->getStatusCode());
147+
self::assertSame([
148+
'data' => [],
149+
'message' => 'Invalid UF.',
150+
], $response->getData(true));
151+
}
152+
153+
public function testMunicipalitiesReturnsMappedDataWhenIbgeRowsAreAvailable(): void
154+
{
155+
$controller = new class () extends SettingsController {
156+
public string $receivedUf = '';
157+
158+
protected function fetchMunicipalitiesRows(string $normalizedUf): array
159+
{
160+
$this->receivedUf = $normalizedUf;
161+
162+
return [
163+
['id' => '3303302', 'nome' => 'Niteroi'],
164+
['id' => '3304557', 'nome' => 'Rio de Janeiro'],
165+
['id' => '', 'nome' => 'Invalido'],
166+
];
167+
}
168+
169+
protected function jsonResponse(array $payload, int $status = 200): JsonResponse
170+
{
171+
return new JsonResponse($payload, $status);
172+
}
173+
};
174+
175+
$response = $controller->municipalities('rj', new IbgeLocalities());
176+
177+
self::assertSame('RJ', $controller->receivedUf);
178+
self::assertSame(200, $response->getStatusCode());
179+
self::assertSame([
180+
'data' => [
181+
['ibge_code' => '3303302', 'name' => 'Niteroi'],
182+
['ibge_code' => '3304557', 'name' => 'Rio de Janeiro'],
183+
],
184+
], $response->getData(true));
185+
}
186+
187+
public function testMunicipalitiesReturnsFallbackWhenIbgeRequestFails(): void
188+
{
189+
$controller = new class () extends SettingsController {
190+
protected function fetchMunicipalitiesRows(string $normalizedUf): array
191+
{
192+
throw new \RuntimeException('ibge unavailable');
193+
}
194+
195+
protected function jsonResponse(array $payload, int $status = 200): JsonResponse
196+
{
197+
return new JsonResponse($payload, $status);
198+
}
199+
};
200+
201+
$response = $controller->municipalities('rj', new IbgeLocalities());
202+
203+
self::assertSame(502, $response->getStatusCode());
204+
self::assertSame([
205+
'data' => [],
206+
'message' => 'Failed to load municipalities from IBGE.',
207+
], $response->getData(true));
208+
}
209+
81210
public function testReplaceCertificateCyclePurgesOldArtifactsClearsSettingsAndStoresNewCertificate(): void
82211
{
83212
ControllerIsolationState::reset();

tests/Unit/Http/Controllers/Support/ControllerIsolationState.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
if (!class_exists(\Illuminate\Http\RedirectResponse::class, false)) {
2222
eval('namespace Illuminate\\Http; class RedirectResponse { public bool $withInputCalled = false; public array $flash = []; public ?string $route = null; public ?string $target = null; public function withInput(): self { $this->withInputCalled = true; return $this; } public function with(string $key, mixed $value): self { $this->flash[$key] = $value; return $this; } }');
2323
}
24+
25+
if (!class_exists(\Illuminate\Http\JsonResponse::class, false)) {
26+
eval('namespace Illuminate\\Http; class JsonResponse { public function __construct(public array $payload = [], public int $status = 200) {} public function getData(bool $assoc = false): object|array { return $assoc ? $this->payload : (object) $this->payload; } public function getStatusCode(): int { return $this->status; } }');
27+
}
2428
}
2529

2630
namespace Modules\Nfse\Http\Controllers {

0 commit comments

Comments
 (0)