283 lines
11 KiB
PHP
283 lines
11 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Projects;
|
|
|
|
use App\Actions\Git\GitProviderKey;
|
|
use App\Actions\Projects\DuplicateProjectConfiguration;
|
|
use App\Models\Project;
|
|
use App\Models\ProjectTemplate;
|
|
use App\Models\RepositoryConnection;
|
|
use App\Models\User;
|
|
use App\Models\UserGitConnection;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
use Tests\TestCase;
|
|
|
|
class ProjectRepositorySettingsControllerTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* Ensure users with edit permission can view repository settings.
|
|
*/
|
|
public function test_users_with_edit_permission_can_view_repository_settings(): void
|
|
{
|
|
$user = User::factory()->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 repository discovery distinguishes empty provider results from provider failures.
|
|
*/
|
|
public function test_repository_settings_expose_personal_access_and_provider_states(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
$emptyGitConnection = UserGitConnection::factory()->for($user)->create([
|
|
'account_login' => 'empty-account',
|
|
'base_url' => 'https://empty.git.test',
|
|
'provider' => GitProviderKey::Gitea,
|
|
]);
|
|
UserGitConnection::factory()->for($user)->create([
|
|
'account_login' => 'broken-account',
|
|
'base_url' => 'https://broken.git.test',
|
|
'provider' => GitProviderKey::Gitea,
|
|
]);
|
|
RepositoryConnection::factory()->for($project)->create([
|
|
'base_url' => $emptyGitConnection->base_url,
|
|
'provider' => GitProviderKey::Gitea,
|
|
]);
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://empty.git.test/api/v1/user/repos*' => Http::response([]),
|
|
'https://broken.git.test/api/v1/user/repos*' => Http::response([], 502),
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->get(route('projects.settings.repositories', $project))
|
|
->assertOk()
|
|
->assertInertia(fn (Assert $page) => $page
|
|
->component('projects/settings/Repositories')
|
|
->has('availableRepositorySources', 2)
|
|
->where('availableRepositorySources', function ($sources): bool {
|
|
$sourcesByAccount = collect($sources)->keyBy('account_login');
|
|
$emptySource = $sourcesByAccount->get('empty-account');
|
|
$brokenSource = $sourcesByAccount->get('broken-account');
|
|
|
|
return data_get($emptySource, 'provider') === GitProviderKey::Gitea->value
|
|
&& data_get($emptySource, 'provider_label') === GitProviderKey::Gitea->label()
|
|
&& data_get($emptySource, 'repositories') === []
|
|
&& data_get($emptySource, 'error') === null
|
|
&& data_get($brokenSource, 'repositories') === []
|
|
&& data_get($brokenSource, 'error') === config('app.name').' could not load repositories from this Git connection right now.';
|
|
})
|
|
->where('repositoryConnections.0.has_personal_access', true),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
]);
|
|
}
|
|
}
|