create(); $project = Project::factory()->create([ 'key' => 'CORE', ]); RepositoryConnection::factory()->for($project)->create([ 'base_url' => 'https://git.example.test', 'owner_name' => 'core-team', 'provider' => GitProviderKey::Gitea, 'provider_repository_id' => '501', 'repository_full_name' => 'core-team/kanbangit', 'repository_name' => 'kanbangit', 'repository_url' => 'https://git.example.test/core-team/kanbangit', ]); $template = ProjectTemplate::factory()->create([ 'configuration_snapshot' => app(DuplicateProjectConfiguration::class)->export($project, [ DuplicateProjectConfiguration::COPY_REPOSITORY_CONNECTIONS, ]), 'copy_areas' => [DuplicateProjectConfiguration::COPY_REPOSITORY_CONNECTIONS], 'name' => 'Connected Template', ]); $project->update(['project_template_id' => $template->id]); $this->syncProjectPermissions($project, $user, ['edit']); $this->actingAs($user) ->get(route('projects.settings.repositories', $project)) ->assertOk() ->assertInertia(fn (Assert $page) => $page ->component('projects/settings/Repositories') ->where('project.id', $project->id) ->where('repositoryConnections.0.repository_full_name', 'core-team/kanbangit') ->where('repositoryConnections.0.has_personal_access', false) ->where('availableRepositorySources', []) ->where('templateState.templateName', 'Connected Template') ->where('templateState.inheritedAreas.0.value', DuplicateProjectConfiguration::COPY_REPOSITORY_CONNECTIONS), ); } /** * Ensure users without edit permission cannot manage repository settings. */ public function test_users_without_edit_permission_cannot_view_repository_settings(): void { $user = User::factory()->create(); $project = Project::factory()->create(); $this->actingAs($user) ->get(route('projects.settings.repositories', $project)) ->assertForbidden(); } /** * Ensure authorized users can link a repository through their own Git connection. */ public function test_users_with_edit_permission_can_link_project_repositories(): void { Http::preventStrayRequests(); Http::fake([ 'https://git.example.test/api/v1/user/repos*' => Http::response([ [ 'id' => 501, 'name' => 'kanbangit', 'full_name' => 'core-team/kanbangit', 'html_url' => 'https://git.example.test/core-team/kanbangit', 'owner' => [ 'login' => 'core-team', ], 'private' => true, ], ]), ]); $user = User::factory()->create(); $project = Project::factory()->create(); $userGitConnection = UserGitConnection::factory()->for($user)->create([ 'base_url' => 'https://git.example.test', 'provider' => GitProviderKey::Gitea, ]); $this->syncProjectPermissions($project, $user, ['edit']); $this->actingAs($user) ->post(route('projects.settings.repositories.store', $project), [ 'provider_repository_id' => '501', 'user_git_connection_id' => $userGitConnection->id, ]) ->assertRedirect(route('projects.settings.repositories', $project)) ->assertSessionHas('status', 'core-team/kanbangit was linked to '.$project->name.'.'); $this->assertDatabaseHas('repository_connections', [ 'project_id' => $project->id, 'provider' => GitProviderKey::Gitea->value, 'base_url' => 'https://git.example.test', 'provider_repository_id' => '501', 'owner_name' => 'core-team', 'repository_name' => 'kanbangit', 'repository_full_name' => 'core-team/kanbangit', ]); } /** * Ensure users cannot link repositories through another user's Git connection. */ public function test_users_cannot_link_repositories_through_foreign_git_connections(): void { Http::preventStrayRequests(); Http::fake(); $user = User::factory()->create(); $otherUser = User::factory()->create(); $project = Project::factory()->create(); $foreignGitConnection = UserGitConnection::factory()->for($otherUser)->create(); $this->syncProjectPermissions($project, $user, ['edit']); $this->actingAs($user) ->post(route('projects.settings.repositories.store', $project), [ 'provider_repository_id' => '501', 'user_git_connection_id' => $foreignGitConnection->id, ]) ->assertForbidden(); } /** * Ensure authorized users can relink an existing project repository. */ public function test_users_with_edit_permission_can_update_repository_connections(): void { Http::preventStrayRequests(); Http::fake([ 'https://git.internal.test/api/v1/user/repos*' => Http::response([ [ 'id' => 902, 'name' => 'platform', 'full_name' => 'infra/platform', 'html_url' => 'https://git.internal.test/infra/platform', 'owner' => [ 'login' => 'infra', ], 'private' => false, ], ]), ]); $user = User::factory()->create(); $project = Project::factory()->create(); $repositoryConnection = RepositoryConnection::factory()->for($project)->create([ 'base_url' => 'https://git.example.test', 'owner_name' => 'core-team', 'provider' => GitProviderKey::Gitea, 'provider_repository_id' => '501', 'repository_full_name' => 'core-team/kanbangit', 'repository_name' => 'kanbangit', 'repository_url' => 'https://git.example.test/core-team/kanbangit', ]); $userGitConnection = UserGitConnection::factory()->for($user)->create([ 'base_url' => 'https://git.internal.test', 'provider' => GitProviderKey::Gitea, ]); $this->syncProjectPermissions($project, $user, ['edit']); $this->actingAs($user) ->patch(route('projects.settings.repositories.update', [$project, $repositoryConnection]), [ 'provider_repository_id' => '902', 'user_git_connection_id' => $userGitConnection->id, ]) ->assertRedirect(route('projects.settings.repositories', $project)) ->assertSessionHas('status', 'infra/platform connection was updated.'); $this->assertDatabaseHas('repository_connections', [ 'id' => $repositoryConnection->id, 'base_url' => 'https://git.internal.test', 'provider_repository_id' => '902', 'owner_name' => 'infra', 'repository_name' => 'platform', 'repository_full_name' => 'infra/platform', ]); } /** * Ensure authorized users can remove project repository links. */ public function test_users_with_edit_permission_can_delete_repository_connections(): void { $user = User::factory()->create(); $project = Project::factory()->create(); $repositoryConnection = RepositoryConnection::factory()->for($project)->create([ 'repository_full_name' => 'core-team/kanbangit', ]); $this->syncProjectPermissions($project, $user, ['edit']); $this->actingAs($user) ->delete(route('projects.settings.repositories.destroy', [$project, $repositoryConnection])) ->assertRedirect(route('projects.settings.repositories', $project)) ->assertSessionHas('status', 'core-team/kanbangit was removed from the project.'); $this->assertDatabaseMissing('repository_connections', [ 'id' => $repositoryConnection->id, ]); } }