seedLookupData(); $repository = $this->repository([ 'provider' => 'github', 'project_id' => $fixtures['project']->id, 'webhook_secret' => 'github-secret', ]); $ticket = $this->ticket(['project_id' => $fixtures['project']->id]); $payload = [ 'ref' => 'refs/heads/feature/' . $ticket->reference . '-automation', 'commits' => [ [ 'id' => str_repeat('a', 40), 'message' => $ticket->reference . ' Add webhook ingestion', 'url' => 'https://github.example.test/org/repo/commit/' . str_repeat('a', 40), 'timestamp' => '2026-06-15T10:00:00+00:00', 'author' => ['name' => 'Mark Gregory', 'email' => 'mark@example.com'], ], ], ]; $json = json_encode($payload, JSON_THROW_ON_ERROR); $response = $this->call('POST', route('api.helpdesk.git.webhook', ['repository' => $repository->id]), [], [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_GITHUB_EVENT' => 'push', 'HTTP_X_GITHUB_DELIVERY' => 'delivery-123', 'HTTP_X_HUB_SIGNATURE_256' => 'sha256=' . hash_hmac('sha256', $json, 'github-secret'), ], $json); $response->assertStatus(202)->assertJsonPath('status', 'accepted')->assertJsonPath('linked_count', 1); $this->assertDatabaseHas('helpdesk_git_commits', [ 'repository_id' => $repository->id, 'ticket_id' => $ticket->id, 'hash' => str_repeat('a', 40), 'short_hash' => str_repeat('a', 12), 'branch' => 'feature/' . $ticket->reference . '-automation', ]); $this->assertDatabaseHas('helpdesk_git_webhook_logs', [ 'repository_id' => $repository->id, 'provider' => 'github', 'delivery_id' => 'delivery-123', 'event' => 'push', 'status' => 'accepted', 'commit_count' => 1, 'linked_count' => 1, ]); } public function test_github_webhook_rejects_invalid_signature_without_creating_commits(): void { $repository = $this->repository(['provider' => 'github', 'webhook_secret' => 'github-secret']); $payload = ['ref' => 'refs/heads/develop', 'commits' => [['id' => str_repeat('b', 40), 'message' => 'No ticket']]]; $json = json_encode($payload, JSON_THROW_ON_ERROR); $response = $this->call('POST', route('api.helpdesk.git.webhook', ['repository' => $repository->id]), [], [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_GITHUB_EVENT' => 'push', 'HTTP_X_HUB_SIGNATURE_256' => 'sha256=invalid', ], $json); $response->assertForbidden()->assertJsonPath('status', 'rejected'); self::assertSame(0, GitCommit::query()->count()); self::assertSame('rejected', GitWebhookLog::query()->firstOrFail()->status); } public function test_github_non_push_webhook_is_audited_and_ignored(): void { $repository = $this->repository(['provider' => 'github', 'webhook_secret' => 'github-secret']); $payload = ['zen' => 'Keep it logically awesome.']; $json = json_encode($payload, JSON_THROW_ON_ERROR); $response = $this->call('POST', route('api.helpdesk.git.webhook', ['repository' => $repository->id]), [], [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_GITHUB_EVENT' => 'ping', 'HTTP_X_HUB_SIGNATURE_256' => 'sha256=' . hash_hmac('sha256', $json, 'github-secret'), ], $json); $response->assertStatus(202)->assertJsonPath('status', 'ignored'); self::assertSame(0, GitCommit::query()->count()); $this->assertDatabaseHas('helpdesk_git_webhook_logs', [ 'repository_id' => $repository->id, 'provider' => 'github', 'event' => 'ping', 'status' => 'ignored', 'ignored_count' => 1, ]); } public function test_gitlab_push_webhook_tracks_and_links_commit_to_ticket_reference(): void { $fixtures = $this->seedLookupData(); $repository = $this->repository([ 'provider' => 'gitlab', 'project_id' => $fixtures['project']->id, 'webhook_secret' => 'gitlab-secret', ]); $ticket = $this->ticket(['project_id' => $fixtures['project']->id]); $payload = [ 'ref' => 'refs/heads/develop', 'commits' => [ [ 'id' => str_repeat('c', 40), 'message' => 'Fix ' . $ticket->reference . ' SLA report', 'url' => 'https://gitlab.example.test/org/repo/-/commit/' . str_repeat('c', 40), 'timestamp' => '2026-06-15T11:00:00+00:00', 'author' => ['name' => 'Support Agent', 'email' => 'agent@example.com'], ], ], ]; $json = json_encode($payload, JSON_THROW_ON_ERROR); $response = $this->call('POST', route('api.helpdesk.git.webhook', ['repository' => $repository->id]), [], [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_GITLAB_EVENT' => 'Push Hook', 'HTTP_X_GITLAB_TOKEN' => 'gitlab-secret', 'HTTP_X_GITLAB_EVENT_UUID' => 'gitlab-delivery-123', ], $json); $response->assertStatus(202)->assertJsonPath('status', 'accepted')->assertJsonPath('linked_count', 1); $this->assertDatabaseHas('helpdesk_git_commits', [ 'repository_id' => $repository->id, 'ticket_id' => $ticket->id, 'hash' => str_repeat('c', 40), 'branch' => 'develop', ]); $this->assertDatabaseHas('helpdesk_git_webhook_logs', [ 'repository_id' => $repository->id, 'provider' => 'gitlab', 'delivery_id' => 'gitlab-delivery-123', 'event' => 'Push Hook', 'status' => 'accepted', 'linked_count' => 1, ]); } }