'https://sms.test/pattern', 'services.iranpayamak.api_key' => 'api-key', 'services.iranpayamak.line_number' => '5000', 'services.iranpayamak.patterns.verify_code' => 'pattern-1', ]); $this->service = app(IranPayamakService::class); } public function test_send_pattern_posts_expected_payload_and_headers(): void { Http::fake(['sms.test/pattern' => Http::response(['status' => 'ok'])]); $result = $this->service->sendPattern('09120000000', 'verify_code', ['otp' => '1234']); $this->assertSame(['status' => 'ok'], $result); Http::assertSent(function (Request $request) { return $request->url() === 'https://sms.test/pattern' && $request->hasHeader('Api-Key', 'api-key') && $request['code'] === 'pattern-1' && $request['attributes'] === ['otp' => '1234'] && $request['recipient'] === '09120000000' && $request['line_number'] === '5000' && $request['number_format'] === 'english'; }); } public function test_send_pattern_converts_plus98_prefix_to_local_format(): void { Http::fake(['sms.test/pattern' => Http::response([])]); $this->service->sendPattern('+989120000000', 'verify_code', []); Http::assertSent(fn (Request $request) => $request['recipient'] === '09120000000'); } public function test_send_pattern_returns_empty_array_for_empty_body(): void { Http::fake(['sms.test/pattern' => Http::response('', 200)]); $this->assertSame([], $this->service->sendPattern('09120000000', 'verify_code', [])); } public function test_send_pattern_throws_on_unsuccessful_response(): void { Http::fake(['sms.test/pattern' => Http::response('unauthorized', 401)]); try { $this->service->sendPattern('09120000000', 'verify_code', []); $this->fail('RuntimeException was not thrown'); } catch (RuntimeException $e) { $this->assertStringContainsString('401', $e->getMessage()); $this->assertStringContainsString('unauthorized', $e->getMessage()); } } }