Add harvest and generic imports

This commit is contained in:
Constantin Graf
2025-02-24 15:21:24 -05:00
committed by Constantin Graf
parent 9faa8fe6e1
commit f93c5370bf
22 changed files with 1120 additions and 3 deletions

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Service\Import\Importers;
use App\Models\Client;
use App\Models\Organization;
use App\Models\Project;
use App\Models\Task;
use App\Service\ColorService;
use App\Service\Import\Importers\DefaultImporter;
use App\Service\Import\Importers\GenericProjectsImporter;
use App\Service\Import\Importers\ImportException;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;
#[CoversClass(GenericProjectsImporter::class)]
#[CoversClass(ImportException::class)]
#[CoversClass(DefaultImporter::class)]
#[UsesClass(GenericProjectsImporter::class)]
class GenericProjectsImporterTest extends ImporterTestAbstract
{
public function test_import_of_test_file_succeeds(): void
{
// Arrange
$organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new GenericProjectsImporter;
$importer->init($organization);
$data = Storage::disk('testfiles')->get('generic_projects_import_test_1.csv');
// Act
$importer->importData($data, $timezone);
$report = $importer->getReport();
// Assert
$clients = Client::all();
$this->assertCount(2, $clients);
$client1 = $clients->firstWhere('name', 'Big Company');
$this->assertNotNull($client1);
$client2 = $clients->firstWhere('name', 'Some client');
$this->assertNotNull($client2);
$projects = Project::all();
$this->assertCount(3, $projects);
// Project 1
$project1 = $projects->firstWhere('name', 'Project for Big Company');
$this->assertNotNull($project1);
$this->assertTrue(app(ColorService::class)->isBuiltInColor($project1->color));
$this->assertSame(10001, $project1->billable_rate);
$this->assertFalse($project1->is_public);
$this->assertSame($client1->getKey(), $project1->client_id);
$this->assertTrue($project1->is_billable);
$this->assertSame(null, $project1->estimated_time);
$this->assertNull($project1->archived_at);
// Project 2
$project2 = $projects->firstWhere('name', 'Project without Client');
$this->assertNotNull($project2);
$this->assertSame('#ef5350', $project2->color);
$this->assertSame(null, $project2->billable_rate);
$this->assertFalse($project2->is_public);
$this->assertSame(null, $project2->client_id);
$this->assertFalse($project2->is_billable);
$this->assertSame(1000, $project2->estimated_time);
$this->assertSame(null, $project2->archived_at);
$project3 = $projects->firstWhere('name', 'Project (Archived)');
$this->assertNotNull($project3);
$this->assertSame('#6a407f', $project3->color);
$this->assertSame(null, $project3->billable_rate);
$this->assertTrue($project3->is_public);
$this->assertSame($client2->getKey(), $project3->client_id);
$this->assertTrue($project3->is_billable);
$this->assertSame(null, $project3->estimated_time);
$this->assertSame('2024-08-25T10:00:00Z', $project3->archived_at->toIso8601ZuluString());
$tasks = Task::all();
$this->assertCount(0, $tasks);
$this->assertSame(0, $report->timeEntriesCreated);
$this->assertSame(0, $report->tagsCreated);
$this->assertSame(0, $report->tasksCreated);
$this->assertSame(0, $report->usersCreated);
$this->assertSame(3, $report->projectsCreated);
$this->assertSame(2, $report->clientsCreated);
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Service\Import\Importers;
use App\Models\Organization;
use App\Service\Import\Importers\DefaultImporter;
use App\Service\Import\Importers\GenericTimeEntriesImporter;
use App\Service\Import\Importers\ImportException;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;
#[CoversClass(GenericTimeEntriesImporter::class)]
#[CoversClass(ImportException::class)]
#[CoversClass(DefaultImporter::class)]
#[UsesClass(GenericTimeEntriesImporter::class)]
class GenericTimeEntriesImporterTest extends ImporterTestAbstract
{
public function test_import_of_test_file_succeeds(): void
{
// Arrange
$organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new GenericTimeEntriesImporter;
$importer->init($organization);
$data = Storage::disk('testfiles')->get('generic_time_entries_import_test_1.csv');
// Act
$importer->importData($data, $timezone);
$report = $importer->getReport();
// Assert
$testScenario = $this->checkTestScenarioAfterImportExcludingTimeEntries();
$this->checkTimeEntries($testScenario);
$this->assertSame(2, $report->timeEntriesCreated);
$this->assertSame(2, $report->tagsCreated);
$this->assertSame(1, $report->tasksCreated);
$this->assertSame(1, $report->usersCreated);
$this->assertSame(2, $report->projectsCreated);
$this->assertSame(1, $report->clientsCreated);
}
public function test_import_of_test_file_twice_succeeds(): void
{
// Arrange
$organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new GenericTimeEntriesImporter;
$importer->init($organization);
$data = Storage::disk('testfiles')->get('generic_time_entries_import_test_1.csv');
$importer->importData($data, $timezone);
$importer = new GenericTimeEntriesImporter;
$importer->init($organization);
// Act
$importer->importData($data, $timezone);
$report = $importer->getReport();
// Assert
$testScenario = $this->checkTestScenarioAfterImportExcludingTimeEntries();
$this->checkTimeEntries($testScenario, true);
$this->assertSame(2, $report->timeEntriesCreated);
$this->assertSame(0, $report->tagsCreated);
$this->assertSame(0, $report->tasksCreated);
$this->assertSame(0, $report->usersCreated);
$this->assertSame(0, $report->projectsCreated);
$this->assertSame(0, $report->clientsCreated);
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Service\Import\Importers;
use App\Models\Client;
use App\Models\Organization;
use App\Service\Import\Importers\DefaultImporter;
use App\Service\Import\Importers\HarvestClientsImporter;
use App\Service\Import\Importers\ImportException;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;
#[CoversClass(HarvestClientsImporter::class)]
#[CoversClass(ImportException::class)]
#[CoversClass(DefaultImporter::class)]
#[UsesClass(HarvestClientsImporter::class)]
class HarvestClientsImporterTest extends ImporterTestAbstract
{
public function test_import_of_test_file_succeeds(): void
{
// Arrange
$organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new HarvestClientsImporter;
$importer->init($organization);
$data = Storage::disk('testfiles')->get('harvest_clients_import_test_1.csv');
// Act
$importer->importData($data, $timezone);
// Assert
$clients = Client::query()->whereBelongsTo($organization, 'organization')->get();
$this->assertCount(2, $clients);
$client1 = $clients->where('name', 'Example Client')->first();
$this->assertNotNull($client1);
// Client name in Harvest: \\ 🔥 Special characters """`!@#$%^&*()_+\-=\[\]{};':"\\|,.''<>\/?~ \\\
$client2 = $clients->where('name', '\\\\ 🔥 Special characters """`!@#$%^&*()_+\-=\[\]{};\':"\\\\|,.\'\'<>\/?~ \\\\\\')->first();
$this->assertNotNull($client2);
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Service\Import\Importers;
use App\Models\Client;
use App\Models\Organization;
use App\Models\Project;
use App\Service\Import\Importers\DefaultImporter;
use App\Service\Import\Importers\HarvestProjectsImporter;
use App\Service\Import\Importers\ImportException;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;
#[CoversClass(HarvestProjectsImporter::class)]
#[CoversClass(ImportException::class)]
#[CoversClass(DefaultImporter::class)]
#[UsesClass(HarvestProjectsImporter::class)]
class HarvestProjectsImporterTest extends ImporterTestAbstract
{
public function test_import_of_test_file_succeeds(): void
{
// Arrange
$organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new HarvestProjectsImporter;
$importer->init($organization);
$data = Storage::disk('testfiles')->get('harvest_projects_import_test_1.csv');
// Act
$importer->importData($data, $timezone);
// Assert
$clients = Client::query()->whereBelongsTo($organization, 'organization')->get();
$this->assertCount(2, $clients);
/** @var Client|null $client1 */
$client1 = $clients->where('name', 'Example Client')->first();
$this->assertNotNull($client1);
// Client name in Harvest: \\ 🔥 Special characters client """`!@#$%^&*()_+\-=\[\]{};':"\\|,.''<>\/?~ \\\
/** @var Client|null $client2 */
$client2 = $clients->where('name', '\\\\ 🔥 Special characters client """`!@#$%^&*()_+\-=\[\]{};\':"\\\\|,.\'\'<>\/?~ \\\\\\')->first();
$this->assertNotNull($client2);
$projects = Project::query()->whereBelongsTo($organization, 'organization')->get();
$this->assertCount(2, $projects);
/** @var Project|null $project1 */
$project1 = $projects->where('name', 'Example Project')->first();
$this->assertNotNull($project1);
$this->assertSame($client1->getKey(), $project1->client_id);
$this->assertSame(50 * 60 * 60, $project1->estimated_time); // 50h
$this->assertSame(true, $project1->is_billable);
/** @var Project|null $project2 */
$project2 = $projects->where('name', '\\\\ 🔥 Special characters project """`!@#$%^&*()_+\-=\[\]{};\':"\\\\|,.\'\'<>\/?~ \\\\\\')->first();
$this->assertNotNull($project2);
$this->assertSame($client2->getKey(), $project2->client_id);
$this->assertSame(null, $project2->estimated_time);
$this->assertSame(false, $project2->is_billable);
}
}

View File

@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Service\Import\Importers;
use App\Enums\Role;
use App\Models\Client;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Project;
use App\Models\Tag;
use App\Models\Task;
use App\Models\TimeEntry;
use App\Models\User;
use App\Service\Import\Importers\DefaultImporter;
use App\Service\Import\Importers\HarvestTimeEntriesImporter;
use App\Service\Import\Importers\ImportException;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;
#[CoversClass(HarvestTimeEntriesImporter::class)]
#[CoversClass(ImportException::class)]
#[CoversClass(DefaultImporter::class)]
#[UsesClass(HarvestTimeEntriesImporter::class)]
class HarvestTimeEntriesImporterTest extends ImporterTestAbstract
{
public function test_import_of_test_file_succeeds(): void
{
// Arrange
$organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new HarvestTimeEntriesImporter;
$importer->init($organization);
$data = Storage::disk('testfiles')->get('harvest_time_entries_import_test_1.csv');
// Act
$importer->importData($data, $timezone);
$report = $importer->getReport();
// Assert
$users = User::all();
$this->assertCount(2, $users);
$user1 = $users->firstWhere('name', 'Peter Tester');
$this->assertNotNull($user1);
$this->assertSame(null, $user1->password);
$this->assertSame('Peter Tester', $user1->name);
$this->assertSame('peter.tester@solidtime-import.test', $user1->email);
$members = Member::all();
$this->assertCount(1, $members);
$member1 = $members->firstWhere('user_id', $user1->getKey());
$this->assertNotNull($member1);
$this->assertSame(Role::Placeholder->value, $member1->role);
$clients = Client::all();
$this->assertCount(1, $clients);
$client1 = $clients->firstWhere('name', 'Big Company');
$this->assertNotNull($client1);
$this->assertNull($client1->archived_at);
$projects = Project::with(['members'])->get();
$this->assertCount(2, $projects);
/** @var Project|null $project1 */
$project1 = $projects->firstWhere('name', 'Project without Client');
$this->assertNotNull($project1);
$this->assertNull($project1->client_id);
/** @var Project|null $project2 */
$project2 = $projects->firstWhere('name', 'Project for Big Company');
$this->assertNotNull($project2);
$this->assertSame($client1->getKey(), $project2->client_id);
$project3 = null;
// Project without Client
$this->assertSame(false, $project1->is_public);
// Project for Big Company
$this->assertSame(false, $project2->is_public);
$tasks = Task::all();
$this->assertCount(1, $tasks);
$task1 = $tasks->firstWhere('name', 'Task 1');
$this->assertNotNull($task1);
$this->assertNull($task1->done_at);
$this->assertSame($project2->getKey(), $task1->project_id);
$tags = Tag::all();
$this->assertCount(0, $tags);
$timeEntries = TimeEntry::all();
$this->assertCount(2, $timeEntries);
$timeEntry1 = $timeEntries->firstWhere('description', '');
$this->assertNotNull($timeEntry1);
$this->assertSame('', $timeEntry1->description);
$this->assertSame('2024-03-03 23:00:00', $timeEntry1->start->toDateTimeString());
$this->assertSame('2024-03-04 19:00:00', $timeEntry1->end->toDateTimeString());
$this->assertFalse($timeEntry1->billable);
$this->assertTrue($timeEntry1->is_imported);
$this->assertSame([], $timeEntry1->tags);
$timeEntry2 = $timeEntries->firstWhere('description', 'Working hard');
$this->assertNotNull($timeEntry2);
$this->assertSame('Working hard', $timeEntry2->description);
$this->assertSame('2024-03-03 23:00:00', $timeEntry2->start->toDateTimeString());
$this->assertSame('2024-03-03 23:00:36', $timeEntry2->end->toDateTimeString());
$this->assertTrue($timeEntry2->billable);
$this->assertTrue($timeEntry2->is_imported);
$this->assertSame([], $timeEntry2->tags);
$this->assertSame(2, $report->timeEntriesCreated);
$this->assertSame(0, $report->tagsCreated);
$this->assertSame(1, $report->tasksCreated);
$this->assertSame(1, $report->usersCreated);
$this->assertSame(2, $report->projectsCreated);
$this->assertSame(1, $report->clientsCreated);
}
}

View File

@@ -42,6 +42,11 @@ class ImporterProviderTest extends TestCase
'clockify_time_entries',
'clockify_projects',
'solidtime',
'harvest_projects',
'harvest_time_entries',
'harvest_clients',
'generic_projects',
'generic_time_entries',
], $keys);
}
}