Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds test coverage for commands and project api routes #275

Merged
merged 9 commits into from
Apr 12, 2024
4 changes: 0 additions & 4 deletions app/Cache/CachedProjectList.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ function () {
->transform(fn ($project) => app(CachedProjectResource::class)($project->packagist_name))
->sortByDesc(fn ($project) => $project['debt_score'])
->values();

return Project::all()
->sortByDesc(fn ($project) => $project['debt_score'])
->values();
}
);
}
Expand Down
5 changes: 3 additions & 2 deletions app/Console/Commands/FetchProjectStats.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ public function fetchOneProject(Project $project)
$project->issues_count = $issues->count();
$project->pull_requests_count = $pullRequests->count();

$project->issues = $issues;
$project->pull_requests = $pullRequests;
// We call ->values() here to re-key the collection after filtering
$project->issues = $issues->values();
$project->pull_requests = $pullRequests->values();

// Fetch download counts (if applicable)
$packagist = Package::fromProject($project);
Expand Down
3 changes: 2 additions & 1 deletion app/Http/Resources/ProjectResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

class ProjectResource extends JsonResource
{
public static $wrap = null;

public function toArray($request): array
{
self::withoutWrapping();
$sparkline = new Sparkline;
$sparkline->setLineColorRGB(67, 79, 181);
$sparkline->deactivateBackgroundColor();
Expand Down
5 changes: 5 additions & 0 deletions app/OrgSlack.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ public function routeNotificationForSlack()
{
return config('services.slack.webhook_url');
}

public function getKey()
{
return 'org-slack-key';
}
}
116 changes: 116 additions & 0 deletions tests/Feature/CreateProjectSnapshotsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace Tests\Feature;

use App\Cache\CachedProjectList;
use App\Models\Project;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;

class CreateProjectSnapshotsTest extends TestCase
{
use RefreshDatabase;

/** @test */
public function one_snapshot_is_created_for_each_project(): void
{
$projects = Project::factory()
->count(3)
->withPrs([['number' => 1], ['number' => 2], ['number' => 3]])
->withIssues([['number' => 1], ['number' => 2]])
->create();

$this->artisan('stats:snapshot')
->assertExitCode(0);

$this->assertDatabaseCount('snapshots', 3);

$projects->each(function ($project) {
$this->assertDatabaseHas('snapshots', [
'project_id' => $project->id,
'snapshot_date' => now()->format('Y-m-d'),
'debt_score' => $project->debtScore(),
'pull_request_count' => 3,
'old_pull_request_count' => 0,
'issue_count' => 2,
'old_issue_count' => 0,
'downloads_total' => 0,
'downloads_last_30_days' => 0,
]);
});
}

/** @test */
public function no_snapshots_are_created_if_the_project_has_already_been_snapshotted_today(): void
{
Project::factory()
->create();

$this->artisan('stats:snapshot')
->assertExitCode(0);

$this->artisan('stats:snapshot')
->expectsOutput('No projects need snapshots for ' . now()->format('Y-m-d'))
->assertExitCode(0);

$this->assertDatabaseCount('snapshots', 1);
}

/** @test */
public function the_force_option_overwrites_the_project_snapshot_taken_today(): void
{
$project = Project::factory()
->create(['downloads_total' => 50]);

$this->artisan('stats:snapshot')
->assertExitCode(0);

$this->assertDatabaseCount('snapshots', 1);
$this->assertDatabaseHas('snapshots', [
'project_id' => $project->id,
'downloads_total' => 50,
]);

$project->update(['downloads_total' => 1000]);

$this->artisan('stats:snapshot --force')
->assertExitCode(0);

$this->assertDatabaseCount('snapshots', 1);
$this->assertDatabaseHas('snapshots', [
'project_id' => $project->id,
'downloads_total' => 1000,
]);
}

/** @test */
public function no_snapshots_are_created_if_no_projects_exist(): void
{
$this->artisan('stats:snapshot')
->expectsOutput('No projects need snapshots for ' . now()->format('Y-m-d'))
->assertExitCode(0);
}

/** @test */
public function the_cache_is_cleared_and_rebuilt_after_snapshots_are_taken(): void
{
Cache::shouldReceive('clear')
->once();

$cacheRebuilt = false;

$this->app->bind(CachedProjectList::class, function () use (&$cacheRebuilt) {
return function () use (&$cacheRebuilt) {
$cacheRebuilt = true;
};
});

Project::factory()->create();

$this->artisan('stats:snapshot')
->assertExitCode(0);

$this->assertTrue($cacheRebuilt);
}
}
161 changes: 161 additions & 0 deletions tests/Feature/FetchGitHubProjectsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

namespace Tests\Feature;

use App\Models\Project;
use Github\Api\Repo;
use GrahamCampbell\GitHub\Facades\GitHub as GitHubClient;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Mockery;
use Tests\TestCase;

class FetchGitHubProjectsTest extends TestCase
{
use RefreshDatabase;

protected function setUp(): void
{
parent::setUp();

Http::preventStrayRequests();

Http::fake([
'packagist.org/search.json?q=tighten&per_page=100' => Http::response([
'results' => [
[
'name' => 'tighten/ziggy',
'description' => 'Use your Laravel named routes in JavaScript.',
'url' => 'https://packagist.org/packages/tighten/ziggy',
'repository' => 'https://github.com/tighten/ziggy',
'downloads' => 16043634,
'favers' => 3697,
],
[
'name' => 'tighten/collect',
'description' => 'Collect - Illuminate Collections as a separate package.',
'url' => 'https://packagist.org/packages/tighten/collect',
'repository' => 'https://github.com/tighten/collect',
'downloads' => 17251907,
'favers' => 1540,
'abandoned' => 'illuminate/collections',
],
],
]),
]);

$this->mockGithubClient('tighten');
}

/** @test */
public function fetch_projects_and_persist_successfully(): void
{
$this->artisan('projects:fetch');

$this->assertDatabaseCount('projects', 2);

$this->assertDatabaseHas('projects', [
'namespace' => 'tighten',
'name' => 'ziggy',
'packagist_name' => 'tighten/ziggy',
]);

$this->assertDatabaseHas('projects', [
'namespace' => 'tighten',
'name' => 'collect',
'packagist_name' => 'tighten/collect',
]);
}

/** @test */
public function fetch_projects_and_persist_successfully_with_all_option_enabled()
{
$this->artisan('projects:fetch --all');

$this->assertDatabaseCount('projects', 4);
}

/** @test */
public function fetch_projects_and_persist_successfully_with_archived_option_enabled()
{
$this->artisan('projects:fetch --archived');

$this->assertDatabaseCount('projects', 3);
}

/** @test */
public function fetch_projects_and_persist_successfully_with_fork_option_enabled()
{
$this->artisan('projects:fetch --fork');

$this->assertDatabaseCount('projects', 3);
}

/** @test */
public function avoid_persisting_projects_that_already_exist_in_the_database(): void
{
$existingProject = Project::factory()->create([
'namespace' => 'tighten',
'name' => 'ziggy',
'packagist_name' => 'tighten/ziggy',
]);

$this->artisan('projects:fetch');

$this->assertDatabaseCount('projects', 2);
$this->assertEquals($existingProject->updated_at, $existingProject->fresh()->updated_at);
}

public function mockGithubClient($namespace): void
{
$repoMock = Mockery::mock(Repo::class);

$repoMock->shouldReceive('org')
->with($namespace, [
'page' => 1,
'per_page' => 100,
])
->once()
->andReturn([
[
'full_name' => 'tighten/ziggy',
'description' => 'Use your Laravel named routes in JavaScript.',
'url' => 'https://api.github.com/repos/tighten/ziggy',
'archived' => false,
'fork' => false,
],
[
'full_name' => 'tighten/collect',
'description' => 'Collect - Illuminate Collections as a separate package.',
'url' => 'https://api.github.com/repos/tighten/collect',
'archived' => false,
'fork' => false,
],
[
'full_name' => 'tighten/FlexSlider',
'description' => 'An awesome, fully responsive jQuery slider plugin',
'url' => 'https://api.github.com/repos/tighten/FlexSlider',
'archived' => false,
'fork' => true,
],
[
'full_name' => 'tighten/confomo',
'description' => 'ConFOMO is a simple tool that makes it easy to track your friends at conferences.',
'url' => 'https://api.github.com/repos/tighten/confomo',
'archived' => true,
'fork' => false,
],
]);

$repoMock->shouldReceive('org')
->with($namespace, [
'page' => 2,
'per_page' => 100,
])
->once()
->andReturn([]);

GitHubClient::shouldReceive('repositories')
->andReturn($repoMock);
}
}
Loading
Loading