diff --git a/bin/test-integration b/bin/test-integration new file mode 100755 index 0000000..93d28c9 --- /dev/null +++ b/bin/test-integration @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Load .env.local secrets and run integration tests inside the app container. +if [ -f "$(dirname "$0")/../.env.local" ]; then + set -a + # shellcheck disable=SC1091 + source "$(dirname "$0")/../.env.local" + set +a +fi + +docker compose exec \ + -e FRAPPE_ERP_BASE_URL="${FRAPPE_ERP_BASE_URL:-}" \ + -e FRAPPE_ERP_API_KEY="${FRAPPE_ERP_API_KEY:-}" \ + -e FRAPPE_ERP_API_SECRET="${FRAPPE_ERP_API_SECRET:-}" \ + app php vendor/bin/phpunit tests/Integration/ --testdox "$@" diff --git a/src/Infrastructure/Channel/Frappe/FrappeHttpClient.php b/src/Infrastructure/Channel/Frappe/FrappeHttpClient.php index 01e0546..1ba2f1d 100644 --- a/src/Infrastructure/Channel/Frappe/FrappeHttpClient.php +++ b/src/Infrastructure/Channel/Frappe/FrappeHttpClient.php @@ -42,6 +42,40 @@ class FrappeHttpClient return $result; } + /** + * GET a Frappe resource endpoint. + * + * @return array + */ + public function get(string $path): array + { + $response = $this->httpClient->request('GET', $this->baseUrl.$path, [ + 'headers' => ['Authorization' => $this->authHeader], + ]); + + /** @var array $result */ + $result = $response->toArray(); + + return $result; + } + + /** + * DELETE a Frappe resource. + * + * @return array + */ + public function delete(string $path): array + { + $response = $this->httpClient->request('DELETE', $this->baseUrl.$path, [ + 'headers' => ['Authorization' => $this->authHeader], + ]); + + /** @var array $result */ + $result = $response->toArray(); + + return $result; + } + /** GET raw binary content (for PDF downloads). */ public function getContent(string $path): string { diff --git a/tests/Integration/Infrastructure/Channel/Frappe/FrappeCustomerIntegrationTest.php b/tests/Integration/Infrastructure/Channel/Frappe/FrappeCustomerIntegrationTest.php new file mode 100644 index 0000000..6958085 --- /dev/null +++ b/tests/Integration/Infrastructure/Channel/Frappe/FrappeCustomerIntegrationTest.php @@ -0,0 +1,110 @@ +markTestSkipped('FRAPPE_ERP_* env vars not set'); + } + + $this->client = new FrappeHttpClient( + HttpClient::create(), + (string) $baseUrl, + (string) $apiKey, + (string) $apiSecret, + ); + } + + protected function tearDown(): void + { + if ('' !== $this->createdCustomerName) { + try { + $this->client->delete('/api/resource/Customer/'.$this->createdCustomerName); + } catch (\Throwable) { + // best-effort cleanup + } + } + } + + public function test_create_customer(): void + { + $response = $this->client->post('/api/resource/Customer', [ + 'customer_name' => 'Test Superseller Integration', + 'customer_type' => 'Individual', + 'customer_group' => 'Individual', + 'territory' => 'Germany', + ]); + + $this->assertArrayHasKey('data', $response); + $this->assertNotEmpty($response['data']['name']); + + $this->createdCustomerName = $response['data']['name']; + } + + public function test_find_created_customer(): void + { + // Create first + $created = $this->client->post('/api/resource/Customer', [ + 'customer_name' => 'Test Superseller Integration', + 'customer_type' => 'Individual', + 'customer_group' => 'Individual', + 'territory' => 'Germany', + ]); + $this->createdCustomerName = $created['data']['name']; + + // Find by name + $response = $this->client->get('/api/resource/Customer/'.$this->createdCustomerName); + + $this->assertSame($this->createdCustomerName, $response['data']['name']); + $this->assertSame('Test Superseller Integration', $response['data']['customer_name']); + } + + public function test_find_nonexistent_customer_throws(): void + { + $this->expectException(ClientExceptionInterface::class); + + $this->client->get('/api/resource/Customer/CUST-DOES-NOT-EXIST-99999'); + } + + public function test_delete_customer(): void + { + // Create + $created = $this->client->post('/api/resource/Customer', [ + 'customer_name' => 'Test Superseller Integration', + 'customer_type' => 'Individual', + 'customer_group' => 'Individual', + 'territory' => 'Germany', + ]); + $name = $created['data']['name']; + + // Delete + $this->client->delete('/api/resource/Customer/'.$name); + + // Confirm gone + $this->expectException(ClientExceptionInterface::class); + $this->client->get('/api/resource/Customer/'.$name); + } +}