|
11 | 11 | use donatj\MockWebServer\Response; |
12 | 12 | use LibreCodeCoop\NfsePHP\Contracts\XmlSignerInterface; |
13 | 13 | use LibreCodeCoop\NfsePHP\Dto\DpsData; |
| 14 | +use LibreCodeCoop\NfsePHP\Exception\CancellationException; |
| 15 | +use LibreCodeCoop\NfsePHP\Exception\IssuanceException; |
| 16 | +use LibreCodeCoop\NfsePHP\Exception\NfseErrorCode; |
| 17 | +use LibreCodeCoop\NfsePHP\Exception\QueryException; |
14 | 18 | use LibreCodeCoop\NfsePHP\Http\NfseClient; |
15 | 19 | use LibreCodeCoop\NfsePHP\SecretStore\NoOpSecretStore; |
16 | 20 | use LibreCodeCoop\NfsePHP\Tests\TestCase; |
@@ -117,6 +121,128 @@ public function testCancelReturnsTrueOnSuccess(): void |
117 | 121 | self::assertTrue($client->cancel('abc-123', 'Cancelamento a pedido do tomador')); |
118 | 122 | } |
119 | 123 |
|
| 124 | + // ------------------------------------------------------------------------- |
| 125 | + // Typed exception tests |
| 126 | + // ------------------------------------------------------------------------- |
| 127 | + |
| 128 | + public function testEmitThrowsIssuanceExceptionWhenGatewayRejects(): void |
| 129 | + { |
| 130 | + $payload = json_encode(['codigo' => 'E422', 'mensagem' => 'CNPJ inválido'], JSON_THROW_ON_ERROR); |
| 131 | + |
| 132 | + self::$server->setResponseOfPath( |
| 133 | + '/NFS-e/api/v1/dps', |
| 134 | + new Response($payload, ['Content-Type' => 'application/json'], 422), |
| 135 | + ); |
| 136 | + |
| 137 | + $client = new NfseClient( |
| 138 | + secretStore: new NoOpSecretStore(), |
| 139 | + baseUrlOverride: self::$server->getServerRoot() . '/NFS-e/api/v1', |
| 140 | + signer: $this->signer, |
| 141 | + ); |
| 142 | + |
| 143 | + $this->expectException(IssuanceException::class); |
| 144 | + $client->emit($this->makeDps()); |
| 145 | + } |
| 146 | + |
| 147 | + public function testIssuanceExceptionCarriesErrorCodeHttpStatusAndUpstreamPayload(): void |
| 148 | + { |
| 149 | + $errorData = ['codigo' => 'E422', 'mensagem' => 'CNPJ inválido']; |
| 150 | + |
| 151 | + self::$server->setResponseOfPath( |
| 152 | + '/NFS-e/api/v1/dps', |
| 153 | + new Response(json_encode($errorData, JSON_THROW_ON_ERROR), ['Content-Type' => 'application/json'], 422), |
| 154 | + ); |
| 155 | + |
| 156 | + $client = new NfseClient( |
| 157 | + secretStore: new NoOpSecretStore(), |
| 158 | + baseUrlOverride: self::$server->getServerRoot() . '/NFS-e/api/v1', |
| 159 | + signer: $this->signer, |
| 160 | + ); |
| 161 | + |
| 162 | + try { |
| 163 | + $client->emit($this->makeDps()); |
| 164 | + self::fail('Expected IssuanceException'); |
| 165 | + } catch (IssuanceException $e) { |
| 166 | + self::assertSame(NfseErrorCode::IssuanceRejected, $e->errorCode); |
| 167 | + self::assertSame(422, $e->httpStatus); |
| 168 | + self::assertSame($errorData, $e->upstreamPayload); |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + public function testQueryThrowsQueryExceptionWhenGatewayReturnsError(): void |
| 173 | + { |
| 174 | + self::$server->setResponseOfPath( |
| 175 | + '/NFS-e/api/v1/dps/missing-key', |
| 176 | + new Response('{"error":"not found"}', ['Content-Type' => 'application/json'], 404), |
| 177 | + ); |
| 178 | + |
| 179 | + $client = new NfseClient( |
| 180 | + secretStore: new NoOpSecretStore(), |
| 181 | + baseUrlOverride: self::$server->getServerRoot() . '/NFS-e/api/v1', |
| 182 | + ); |
| 183 | + |
| 184 | + $this->expectException(QueryException::class); |
| 185 | + $client->query('missing-key'); |
| 186 | + } |
| 187 | + |
| 188 | + public function testQueryExceptionCarriesErrorCodeAndHttpStatus(): void |
| 189 | + { |
| 190 | + self::$server->setResponseOfPath( |
| 191 | + '/NFS-e/api/v1/dps/missing-key', |
| 192 | + new Response('{"error":"not found"}', ['Content-Type' => 'application/json'], 404), |
| 193 | + ); |
| 194 | + |
| 195 | + $client = new NfseClient( |
| 196 | + secretStore: new NoOpSecretStore(), |
| 197 | + baseUrlOverride: self::$server->getServerRoot() . '/NFS-e/api/v1', |
| 198 | + ); |
| 199 | + |
| 200 | + try { |
| 201 | + $client->query('missing-key'); |
| 202 | + self::fail('Expected QueryException'); |
| 203 | + } catch (QueryException $e) { |
| 204 | + self::assertSame(NfseErrorCode::QueryFailed, $e->errorCode); |
| 205 | + self::assertSame(404, $e->httpStatus); |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + public function testCancelThrowsCancellationExceptionWhenGatewayReturnsError(): void |
| 210 | + { |
| 211 | + self::$server->setResponseOfPath( |
| 212 | + '/NFS-e/api/v1/dps/blocked-key', |
| 213 | + new Response('{"error":"cannot cancel"}', ['Content-Type' => 'application/json'], 409), |
| 214 | + ); |
| 215 | + |
| 216 | + $client = new NfseClient( |
| 217 | + secretStore: new NoOpSecretStore(), |
| 218 | + baseUrlOverride: self::$server->getServerRoot() . '/NFS-e/api/v1', |
| 219 | + ); |
| 220 | + |
| 221 | + $this->expectException(CancellationException::class); |
| 222 | + $client->cancel('blocked-key', 'a pedido do tomador'); |
| 223 | + } |
| 224 | + |
| 225 | + public function testCancellationExceptionCarriesErrorCodeAndHttpStatus(): void |
| 226 | + { |
| 227 | + self::$server->setResponseOfPath( |
| 228 | + '/NFS-e/api/v1/dps/blocked-key', |
| 229 | + new Response('{"error":"cannot cancel"}', ['Content-Type' => 'application/json'], 409), |
| 230 | + ); |
| 231 | + |
| 232 | + $client = new NfseClient( |
| 233 | + secretStore: new NoOpSecretStore(), |
| 234 | + baseUrlOverride: self::$server->getServerRoot() . '/NFS-e/api/v1', |
| 235 | + ); |
| 236 | + |
| 237 | + try { |
| 238 | + $client->cancel('blocked-key', 'a pedido do tomador'); |
| 239 | + self::fail('Expected CancellationException'); |
| 240 | + } catch (CancellationException $e) { |
| 241 | + self::assertSame(NfseErrorCode::CancellationRejected, $e->errorCode); |
| 242 | + self::assertSame(409, $e->httpStatus); |
| 243 | + } |
| 244 | + } |
| 245 | + |
120 | 246 | // ------------------------------------------------------------------------- |
121 | 247 |
|
122 | 248 | private function makeDps(): DpsData |
|
0 commit comments