diff --git a/.env.example b/.env.example index c4d4a2cf..12871552 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ APP_NAME=Laravel APP_ENV=local -APP_KEY= +APP_KEY=base64:UNQNf1SXeASNkWux01Rj8EnHYx8FO0kAxWNDwktclkk= APP_DEBUG=true APP_URL=http://localhost diff --git a/.github/workflows/pint.yml b/.github/workflows/pint.yml new file mode 100644 index 00000000..65ec88eb --- /dev/null +++ b/.github/workflows/pint.yml @@ -0,0 +1,11 @@ +name: PHP Linting +on: push +jobs: + pint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: "laravel-pint" + uses: aglipanci/laravel-pint-action@2.0.0 + with: + configPath: "pint.json" diff --git a/app/Models/Client.php b/app/Models/Client.php new file mode 100644 index 00000000..511987e1 --- /dev/null +++ b/app/Models/Client.php @@ -0,0 +1,42 @@ + + */ + protected $casts = [ + 'name' => 'string', + ]; + + /** + * @return BelongsTo + */ + public function organization(): BelongsTo + { + return $this->belongsTo(Team::class, 'organization_id'); + } +} diff --git a/app/Models/Project.php b/app/Models/Project.php new file mode 100644 index 00000000..601c6798 --- /dev/null +++ b/app/Models/Project.php @@ -0,0 +1,63 @@ + $tasks + * + * @method static ProjectFactory factory() + */ +class Project extends Model +{ + use HasFactory; + use HasUuids; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'name' => 'string', + ]; + + /** + * @return BelongsTo + */ + public function organization(): BelongsTo + { + return $this->belongsTo(Team::class, 'organization_id'); + } + + /** + * @return BelongsTo + */ + public function client(): BelongsTo + { + return $this->belongsTo(Client::class, 'client_id'); + } + + /** + * @return HasMany + */ + public function tasks(): HasMany + { + return $this->hasMany(Task::class); + } +} diff --git a/app/Models/Tag.php b/app/Models/Tag.php new file mode 100644 index 00000000..2a2d3057 --- /dev/null +++ b/app/Models/Tag.php @@ -0,0 +1,42 @@ + + */ + protected $casts = [ + 'name' => 'string', + ]; + + /** + * @return BelongsTo + */ + public function organization(): BelongsTo + { + return $this->belongsTo(Team::class, 'organization_id'); + } +} diff --git a/app/Models/Task.php b/app/Models/Task.php new file mode 100644 index 00000000..4e0b0495 --- /dev/null +++ b/app/Models/Task.php @@ -0,0 +1,52 @@ + + */ + protected $casts = [ + 'name' => 'string', + ]; + + /** + * @return BelongsTo + */ + public function project(): BelongsTo + { + return $this->belongsTo(Project::class); + } + + /** + * @return BelongsTo + */ + public function organization(): BelongsTo + { + return $this->belongsTo(Team::class, 'organization_id'); + } +} diff --git a/app/Models/TimeEntry.php b/app/Models/TimeEntry.php new file mode 100644 index 00000000..e46e1351 --- /dev/null +++ b/app/Models/TimeEntry.php @@ -0,0 +1,77 @@ + + */ + protected $casts = [ + 'description' => 'string', + 'start' => 'datetime', + 'end' => 'datetime', + 'billable' => 'bool', + 'tags' => 'array', + ]; + + /** + * @return BelongsTo + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id'); + } + + /** + * @return BelongsTo + */ + public function organization(): BelongsTo + { + return $this->belongsTo(Team::class, 'organization_id'); + } + + /** + * @return BelongsTo + */ + public function project(): BelongsTo + { + return $this->belongsTo(Project::class, 'project_id'); + } + + /** + * @return BelongsTo + */ + public function task(): BelongsTo + { + return $this->belongsTo(Task::class, 'task_id'); + } +} diff --git a/composer.json b/composer.json index aaf6b7ab..996c581d 100644 --- a/composer.json +++ b/composer.json @@ -13,9 +13,11 @@ "laravel/jetstream": "^4.2", "laravel/passport": "*", "laravel/tinker": "^2.8", - "tightenco/ziggy": "^1.0" + "tightenco/ziggy": "^1.0", + "tpetry/laravel-postgresql-enhanced": "^0.33.0" }, "require-dev": { + "brianium/paratest": "^7.3", "fakerphp/faker": "^1.9.1", "larastan/larastan": "^2.0", "laravel/pint": "^1.0", @@ -54,6 +56,18 @@ ], "analyse": [ "@php ./vendor/bin/phpstan analyse --memory-limit=2G --configuration=phpstan.neon" + ], + "ptest": [ + "@php artisan test --parallel --colors=always --stop-on-failure" + ], + "test": [ + "@php artisan test --colors=always --stop-on-failure" + ], + "test:coverage": [ + "@php artisan test --coverage --colors=always --stop-on-failure" + ], + "fix": [ + "@php pint" ] }, "extra": { diff --git a/composer.lock b/composer.lock index ae3e4287..faab6e03 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "726e9483d9635413c31a43887a491b4e", + "content-hash": "34dbd2208afb93ce559614a5c014397e", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -453,26 +453,26 @@ }, { "name": "danharrin/livewire-rate-limiting", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/danharrin/livewire-rate-limiting.git", - "reference": "bc2cc0a0b5b517fdc5bba8671013dd71081f70a8" + "reference": "bf16003f0d977b5a41071526d697eec94ac41735" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/danharrin/livewire-rate-limiting/zipball/bc2cc0a0b5b517fdc5bba8671013dd71081f70a8", - "reference": "bc2cc0a0b5b517fdc5bba8671013dd71081f70a8", + "url": "https://api.github.com/repos/danharrin/livewire-rate-limiting/zipball/bf16003f0d977b5a41071526d697eec94ac41735", + "reference": "bf16003f0d977b5a41071526d697eec94ac41735", "shasum": "" }, "require": { - "illuminate/support": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0|^11.0", "php": "^8.0" }, "require-dev": { "livewire/livewire": "^3.0", "livewire/volt": "^1.3", - "orchestra/testbench": "^7.0|^8.0", + "orchestra/testbench": "^7.0|^8.0|^9.0", "phpunit/phpunit": "^9.0|^10.0" }, "type": "library", @@ -503,7 +503,7 @@ "type": "github" } ], - "time": "2023-10-27T15:01:19+00:00" + "time": "2024-01-21T14:53:34+00:00" }, { "name": "dasprid/enum", @@ -1339,16 +1339,16 @@ }, { "name": "filament/actions", - "version": "v3.2.2", + "version": "v3.2.9", "source": { "type": "git", "url": "https://github.com/filamentphp/actions.git", - "reference": "c47731ba370047d9440f1b43935b5d7242274dcc" + "reference": "465ef83a1c43b3b7fe122dde50eda2ee0f9138ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/actions/zipball/c47731ba370047d9440f1b43935b5d7242274dcc", - "reference": "c47731ba370047d9440f1b43935b5d7242274dcc", + "url": "https://api.github.com/repos/filamentphp/actions/zipball/465ef83a1c43b3b7fe122dde50eda2ee0f9138ea", + "reference": "465ef83a1c43b3b7fe122dde50eda2ee0f9138ea", "shasum": "" }, "require": { @@ -1388,20 +1388,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-01-15T20:39:04+00:00" + "time": "2024-01-21T14:44:52+00:00" }, { "name": "filament/filament", - "version": "v3.2.2", + "version": "v3.2.9", "source": { "type": "git", "url": "https://github.com/filamentphp/panels.git", - "reference": "baf5ac84b07d38216a4dd09dae9bedc836aa1d3e" + "reference": "a830f2d38073d3a4cdbe3798c957b69be50d39c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/panels/zipball/baf5ac84b07d38216a4dd09dae9bedc836aa1d3e", - "reference": "baf5ac84b07d38216a4dd09dae9bedc836aa1d3e", + "url": "https://api.github.com/repos/filamentphp/panels/zipball/a830f2d38073d3a4cdbe3798c957b69be50d39c3", + "reference": "a830f2d38073d3a4cdbe3798c957b69be50d39c3", "shasum": "" }, "require": { @@ -1453,20 +1453,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-01-15T20:39:17+00:00" + "time": "2024-01-21T14:44:57+00:00" }, { "name": "filament/forms", - "version": "v3.2.2", + "version": "v3.2.9", "source": { "type": "git", "url": "https://github.com/filamentphp/forms.git", - "reference": "ed467134f77ca667ca02fdfa5c913d9e2fea9e2a" + "reference": "fc37c620f66a2e13e160b516cc4d0e5ad8ae9425" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/forms/zipball/ed467134f77ca667ca02fdfa5c913d9e2fea9e2a", - "reference": "ed467134f77ca667ca02fdfa5c913d9e2fea9e2a", + "url": "https://api.github.com/repos/filamentphp/forms/zipball/fc37c620f66a2e13e160b516cc4d0e5ad8ae9425", + "reference": "fc37c620f66a2e13e160b516cc4d0e5ad8ae9425", "shasum": "" }, "require": { @@ -1509,20 +1509,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-01-15T20:39:04+00:00" + "time": "2024-01-21T14:44:57+00:00" }, { "name": "filament/infolists", - "version": "v3.2.2", + "version": "v3.2.9", "source": { "type": "git", "url": "https://github.com/filamentphp/infolists.git", - "reference": "6d9e91514beca3a5932251f988c7ca3a62e2e9e8" + "reference": "b071063c45f0cd314c863947d1d841da09d40750" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/infolists/zipball/6d9e91514beca3a5932251f988c7ca3a62e2e9e8", - "reference": "6d9e91514beca3a5932251f988c7ca3a62e2e9e8", + "url": "https://api.github.com/repos/filamentphp/infolists/zipball/b071063c45f0cd314c863947d1d841da09d40750", + "reference": "b071063c45f0cd314c863947d1d841da09d40750", "shasum": "" }, "require": { @@ -1560,20 +1560,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-01-15T20:39:02+00:00" + "time": "2024-01-21T14:44:53+00:00" }, { "name": "filament/notifications", - "version": "v3.2.2", + "version": "v3.2.9", "source": { "type": "git", "url": "https://github.com/filamentphp/notifications.git", - "reference": "ce2daf3389cfff46fd0a15bd49264290d59b077c" + "reference": "c5f4f51d949fafc52643f2be654a4da92422836c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/notifications/zipball/ce2daf3389cfff46fd0a15bd49264290d59b077c", - "reference": "ce2daf3389cfff46fd0a15bd49264290d59b077c", + "url": "https://api.github.com/repos/filamentphp/notifications/zipball/c5f4f51d949fafc52643f2be654a4da92422836c", + "reference": "c5f4f51d949fafc52643f2be654a4da92422836c", "shasum": "" }, "require": { @@ -1612,20 +1612,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-01-15T20:39:02+00:00" + "time": "2024-01-19T14:01:21+00:00" }, { "name": "filament/support", - "version": "v3.2.2", + "version": "v3.2.9", "source": { "type": "git", "url": "https://github.com/filamentphp/support.git", - "reference": "cf74fe9e49042cc269cfdc3941aa9c9435f955e0" + "reference": "3b4d5d197c04e5a0b2a250d97c4761a07da9c85e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/support/zipball/cf74fe9e49042cc269cfdc3941aa9c9435f955e0", - "reference": "cf74fe9e49042cc269cfdc3941aa9c9435f955e0", + "url": "https://api.github.com/repos/filamentphp/support/zipball/3b4d5d197c04e5a0b2a250d97c4761a07da9c85e", + "reference": "3b4d5d197c04e5a0b2a250d97c4761a07da9c85e", "shasum": "" }, "require": { @@ -1669,20 +1669,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-01-15T12:25:07+00:00" + "time": "2024-01-21T14:44:58+00:00" }, { "name": "filament/tables", - "version": "v3.2.2", + "version": "v3.2.9", "source": { "type": "git", "url": "https://github.com/filamentphp/tables.git", - "reference": "ba7384fcbc2412f66f08310dd2786ec6602edcf0" + "reference": "87c55f6a1107d6d70b61a83f2cd7495343fdb19c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/tables/zipball/ba7384fcbc2412f66f08310dd2786ec6602edcf0", - "reference": "ba7384fcbc2412f66f08310dd2786ec6602edcf0", + "url": "https://api.github.com/repos/filamentphp/tables/zipball/87c55f6a1107d6d70b61a83f2cd7495343fdb19c", + "reference": "87c55f6a1107d6d70b61a83f2cd7495343fdb19c", "shasum": "" }, "require": { @@ -1722,11 +1722,11 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-01-15T20:39:12+00:00" + "time": "2024-01-21T14:45:11+00:00" }, { "name": "filament/widgets", - "version": "v3.2.2", + "version": "v3.2.9", "source": { "type": "git", "url": "https://github.com/filamentphp/widgets.git", @@ -2511,30 +2511,30 @@ }, { "name": "laravel/fortify", - "version": "v1.19.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/laravel/fortify.git", - "reference": "1dde858a520f679b4a2f453fa68f8a0e98751875" + "reference": "587a55f906ae4f8448a19019a4b2ee7002d5c001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/fortify/zipball/1dde858a520f679b4a2f453fa68f8a0e98751875", - "reference": "1dde858a520f679b4a2f453fa68f8a0e98751875", + "url": "https://api.github.com/repos/laravel/fortify/zipball/587a55f906ae4f8448a19019a4b2ee7002d5c001", + "reference": "587a55f906ae4f8448a19019a4b2ee7002d5c001", "shasum": "" }, "require": { "bacon/bacon-qr-code": "^2.0", "ext-json": "*", - "illuminate/support": "^8.82|^9.0|^10.0", - "php": "^7.3|^8.0", - "pragmarx/google2fa": "^7.0|^8.0" + "illuminate/support": "^10.0|^11.0", + "php": "^8.1", + "pragmarx/google2fa": "^8.0" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^6.34|^7.31|^8.11", + "orchestra/testbench": "^8.16|^9.0", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.4" }, "type": "library", "extra": { @@ -2571,7 +2571,7 @@ "issues": "https://github.com/laravel/fortify/issues", "source": "https://github.com/laravel/fortify" }, - "time": "2023-12-11T16:16:45+00:00" + "time": "2024-01-15T20:07:11+00:00" }, { "name": "laravel/framework", @@ -2780,16 +2780,16 @@ }, { "name": "laravel/jetstream", - "version": "v4.2.1", + "version": "v4.2.2", "source": { "type": "git", "url": "https://github.com/laravel/jetstream.git", - "reference": "fb22f2d8acf10795ba8eb7672e5661722f998eca" + "reference": "7a11a4fb1426855b7132900af5c113a684b820cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/jetstream/zipball/fb22f2d8acf10795ba8eb7672e5661722f998eca", - "reference": "fb22f2d8acf10795ba8eb7672e5661722f998eca", + "url": "https://api.github.com/repos/laravel/jetstream/zipball/7a11a4fb1426855b7132900af5c113a684b820cc", + "reference": "7a11a4fb1426855b7132900af5c113a684b820cc", "shasum": "" }, "require": { @@ -2845,20 +2845,20 @@ "issues": "https://github.com/laravel/jetstream/issues", "source": "https://github.com/laravel/jetstream" }, - "time": "2023-12-27T14:15:37+00:00" + "time": "2024-01-17T00:49:40+00:00" }, { "name": "laravel/passport", - "version": "v11.10.0", + "version": "v11.10.1", "source": { "type": "git", "url": "https://github.com/laravel/passport.git", - "reference": "966bc8e477d08c86a11dc4c5a86f85fa0abdb89b" + "reference": "e1a651481cabff0ba174aaefbdc04a59e6a096ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/passport/zipball/966bc8e477d08c86a11dc4c5a86f85fa0abdb89b", - "reference": "966bc8e477d08c86a11dc4c5a86f85fa0abdb89b", + "url": "https://api.github.com/repos/laravel/passport/zipball/e1a651481cabff0ba174aaefbdc04a59e6a096ec", + "reference": "e1a651481cabff0ba174aaefbdc04a59e6a096ec", "shasum": "" }, "require": { @@ -2923,7 +2923,7 @@ "issues": "https://github.com/laravel/passport/issues", "source": "https://github.com/laravel/passport" }, - "time": "2023-11-02T17:16:12+00:00" + "time": "2024-01-10T14:44:24+00:00" }, { "name": "laravel/prompts", @@ -4520,16 +4520,16 @@ }, { "name": "nette/utils", - "version": "v4.0.3", + "version": "v4.0.4", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", - "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", + "url": "https://api.github.com/repos/nette/utils/zipball/d3ad0aa3b9f934602cb3e3902ebccf10be34d218", + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218", "shasum": "" }, "require": { @@ -4600,9 +4600,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.3" + "source": "https://github.com/nette/utils/tree/v4.0.4" }, - "time": "2023-10-29T21:02:13+00:00" + "time": "2024-01-17T16:50:36+00:00" }, { "name": "nikic/php-parser", @@ -8829,6 +8829,71 @@ }, "time": "2023-12-08T13:03:43+00:00" }, + { + "name": "tpetry/laravel-postgresql-enhanced", + "version": "0.33.0", + "source": { + "type": "git", + "url": "https://github.com/tpetry/laravel-postgresql-enhanced.git", + "reference": "a75749e45983ae548d4d85a9ef7c3240710a3384" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tpetry/laravel-postgresql-enhanced/zipball/a75749e45983ae548d4d85a9ef7c3240710a3384", + "reference": "a75749e45983ae548d4d85a9ef7c3240710a3384", + "shasum": "" + }, + "require": { + "doctrine/dbal": "^2.6|^3.5", + "illuminate/container": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/database": "^6.0|^7.0|^8.79|^9.0|^10.0", + "illuminate/events": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.19.3|^3.5.0", + "nunomaduro/larastan": "^1.0|^2.1", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^8.5.23|^9.5.13" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Tpetry\\PostgresqlEnhanced\\PostgresqlEnhancedServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Tpetry\\PostgresqlEnhanced\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "tpetry", + "email": "tobias@tpetry.me" + } + ], + "description": "Support for many missing PostgreSQL specific features", + "homepage": "https://github.com/tpetry/laravel-postgresql-enhanced", + "keywords": [ + "laravel", + "postgresql" + ], + "support": { + "issues": "https://github.com/tpetry/laravel-postgresql-enhanced/issues", + "source": "https://github.com/tpetry/laravel-postgresql-enhanced/tree/0.33.0" + }, + "time": "2023-10-23T15:36:21+00:00" + }, { "name": "vlucas/phpdotenv", "version": "v5.6.0", @@ -9047,6 +9112,101 @@ } ], "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v7.3.1", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", + "jean85/pretty-package-versions": "^2.0.5", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "phpunit/php-code-coverage": "^10.1.7", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-timer": "^6.0", + "phpunit/phpunit": "^10.4.2", + "sebastian/environment": "^6.0.1", + "symfony/console": "^6.3.4 || ^7.0.0", + "symfony/process": "^6.3.4 || ^7.0.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "infection/infection": "^0.27.6", + "phpstan/phpstan": "^1.10.40", + "phpstan/phpstan-deprecation-rules": "^1.1.4", + "phpstan/phpstan-phpunit": "^1.3.15", + "phpstan/phpstan-strict-rules": "^1.5.2", + "squizlabs/php_codesniffer": "^3.7.2", + "symfony/filesystem": "^6.3.1 || ^7.0.0" + }, + "bin": [ + "bin/paratest", + "bin/paratest.bat", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.3.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2023-10-31T09:24:17+00:00" + }, { "name": "fakerphp/faker", "version": "v1.23.1", @@ -9110,6 +9270,67 @@ }, "time": "2024-01-02T13:46:09+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "85193c0b0cb5c47894b5eaec906e946f054e7077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/85193c0b0cb5c47894b5eaec906e946f054e7077", + "reference": "85193c0b0cb5c47894b5eaec906e946f054e7077", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.0.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2023-09-17T21:38:23+00:00" + }, { "name": "filp/whoops", "version": "2.15.4", @@ -9232,6 +9453,65 @@ }, "time": "2020-07-09T08:09:16+00:00" }, + { + "name": "jean85/pretty-package-versions", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^0.12.66", + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" + }, + "time": "2021-10-08T21:21:46+00:00" + }, { "name": "larastan/larastan", "version": "v2.8.1", @@ -9335,16 +9615,16 @@ }, { "name": "laravel/pint", - "version": "v1.13.8", + "version": "v1.13.9", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "69def89df9e0babc0f0a8bea184804a7d8a9c5c0" + "reference": "e3e269cc5d874c8efd2dc7962b1c7ff2585fe525" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/69def89df9e0babc0f0a8bea184804a7d8a9c5c0", - "reference": "69def89df9e0babc0f0a8bea184804a7d8a9c5c0", + "url": "https://api.github.com/repos/laravel/pint/zipball/e3e269cc5d874c8efd2dc7962b1c7ff2585fe525", + "reference": "e3e269cc5d874c8efd2dc7962b1c7ff2585fe525", "shasum": "" }, "require": { @@ -9355,13 +9635,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.46.0", - "illuminate/view": "^10.39.0", + "friendsofphp/php-cs-fixer": "^3.47.0", + "illuminate/view": "^10.40.0", "larastan/larastan": "^2.8.1", "laravel-zero/framework": "^10.3.0", "mockery/mockery": "^1.6.7", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.30.0" + "pestphp/pest": "^2.31.0" }, "bin": [ "builds/pint" @@ -9397,20 +9677,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-01-09T18:03:54+00:00" + "time": "2024-01-16T17:39:29+00:00" }, { "name": "laravel/sail", - "version": "v1.27.0", + "version": "v1.27.1", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "65a7764af5daadbd122e3b0d67be371d158a9b9a" + "reference": "9dc648978e4276f2bfd37a076a52e3bd9394777f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/65a7764af5daadbd122e3b0d67be371d158a9b9a", - "reference": "65a7764af5daadbd122e3b0d67be371d158a9b9a", + "url": "https://api.github.com/repos/laravel/sail/zipball/9dc648978e4276f2bfd37a076a52e3bd9394777f", + "reference": "9dc648978e4276f2bfd37a076a52e3bd9394777f", "shasum": "" }, "require": { @@ -9462,7 +9742,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2024-01-03T14:07:34+00:00" + "time": "2024-01-13T18:46:48+00:00" }, { "name": "mockery/mockery", @@ -9815,16 +10095,16 @@ }, { "name": "phpmyadmin/sql-parser", - "version": "5.8.2", + "version": "5.9.0", "source": { "type": "git", "url": "https://github.com/phpmyadmin/sql-parser.git", - "reference": "f1720ae19abe6294cb5599594a8a57bc3c8cc287" + "reference": "011fa18a4e55591fac6545a821921dd1d61c6984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/f1720ae19abe6294cb5599594a8a57bc3c8cc287", - "reference": "f1720ae19abe6294cb5599594a8a57bc3c8cc287", + "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/011fa18a4e55591fac6545a821921dd1d61c6984", + "reference": "011fa18a4e55591fac6545a821921dd1d61c6984", "shasum": "" }, "require": { @@ -9855,6 +10135,7 @@ "bin": [ "bin/highlight-query", "bin/lint-query", + "bin/sql-parser", "bin/tokenize-query" ], "type": "library", @@ -9898,7 +10179,7 @@ "type": "other" } ], - "time": "2023-09-19T12:34:29+00:00" + "time": "2024-01-20T20:34:02+00:00" }, { "name": "phpstan/phpstan", @@ -10285,16 +10566,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.7", + "version": "10.5.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e5c5b397a95cb0db013270a985726fcae93e61b8" + "reference": "08f4fa74d5fbfff1ef22abffee47aaedcaea227e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e5c5b397a95cb0db013270a985726fcae93e61b8", - "reference": "e5c5b397a95cb0db013270a985726fcae93e61b8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/08f4fa74d5fbfff1ef22abffee47aaedcaea227e", + "reference": "08f4fa74d5fbfff1ef22abffee47aaedcaea227e", "shasum": "" }, "require": { @@ -10366,7 +10647,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.7" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.8" }, "funding": [ { @@ -10382,7 +10663,7 @@ "type": "tidelift" } ], - "time": "2024-01-14T16:40:30+00:00" + "time": "2024-01-19T07:07:27+00:00" }, { "name": "sebastian/cli-parser", diff --git a/database/factories/ClientFactory.php b/database/factories/ClientFactory.php new file mode 100644 index 00000000..e80d854c --- /dev/null +++ b/database/factories/ClientFactory.php @@ -0,0 +1,37 @@ + + */ +class ClientFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->company(), + 'organization_id' => Team::factory(), + ]; + } + + public function forOrganization(Team $organization): self + { + return $this->state(function (array $attributes) use ($organization) { + return [ + 'organization_id' => $organization->getKey(), + ]; + }); + } +} diff --git a/database/factories/ProjectFactory.php b/database/factories/ProjectFactory.php new file mode 100644 index 00000000..7b076600 --- /dev/null +++ b/database/factories/ProjectFactory.php @@ -0,0 +1,49 @@ + + */ +class ProjectFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->company(), + 'color' => $this->faker->hexColor(), + 'organization_id' => Team::factory(), + 'client_id' => null, + ]; + } + + public function forOrganization(Team $organization): self + { + return $this->state(function (array $attributes) use ($organization) { + return [ + 'organization_id' => $organization->getKey(), + ]; + }); + } + + public function forClient(?Client $client): self + { + return $this->state(function (array $attributes) use ($client) { + return [ + 'client_id' => $client?->getKey(), + ]; + }); + } +} diff --git a/database/factories/TagFactory.php b/database/factories/TagFactory.php new file mode 100644 index 00000000..6ca77687 --- /dev/null +++ b/database/factories/TagFactory.php @@ -0,0 +1,37 @@ + + */ +class TagFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->name, + 'organization_id' => Team::factory(), + ]; + } + + public function forOrganization(Team $organization): self + { + return $this->state(function (array $attributes) use ($organization) { + return [ + 'organization_id' => $organization->getKey(), + ]; + }); + } +} diff --git a/database/factories/TaskFactory.php b/database/factories/TaskFactory.php new file mode 100644 index 00000000..d5fefbff --- /dev/null +++ b/database/factories/TaskFactory.php @@ -0,0 +1,48 @@ + + */ +class TaskFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->word(), + 'project_id' => Project::factory(), + 'organization_id' => Team::factory(), + ]; + } + + public function forProject(Project $project): self + { + return $this->state(function (array $attributes) use ($project) { + return [ + 'project_id' => $project->getKey(), + ]; + }); + } + + public function forOrganization(Team $organization): self + { + return $this->state(function (array $attributes) use ($organization) { + return [ + 'organization_id' => $organization->getKey(), + ]; + }); + } +} diff --git a/database/factories/TimeEntryFactory.php b/database/factories/TimeEntryFactory.php new file mode 100644 index 00000000..c61e352d --- /dev/null +++ b/database/factories/TimeEntryFactory.php @@ -0,0 +1,74 @@ + + */ +class TimeEntryFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $start = $this->faker->dateTimeBetween('-1 year', '-1 hour'); + + return [ + 'description' => $this->faker->sentence(), + 'start' => $start, + 'end' => $this->faker->dateTimeBetween($start, 'now'), + 'billable' => $this->faker->boolean(), + 'tags' => [], + 'user_id' => User::factory(), + 'organization_id' => Team::factory(), + ]; + } + + public function forUser(User $user): self + { + return $this->state(function (array $attributes) use ($user) { + return [ + 'user_id' => $user->getKey(), + ]; + }); + } + + public function forOrganization(Team $organization): self + { + return $this->state(function (array $attributes) use ($organization) { + return [ + 'organization_id' => $organization->getKey(), + ]; + }); + } + + public function forProject(?Project $project): self + { + return $this->state(function (array $attributes) use ($project) { + return [ + 'project_id' => $project?->getKey(), + ]; + }); + } + + public function forTask(?Task $task): self + { + return $this->state(function (array $attributes) use ($task) { + return [ + 'task_id' => $task?->getKey(), + ]; + }); + } +} diff --git a/database/migrations/2024_01_20_110218_create_clients_table.php b/database/migrations/2024_01_20_110218_create_clients_table.php new file mode 100644 index 00000000..ed762797 --- /dev/null +++ b/database/migrations/2024_01_20_110218_create_clients_table.php @@ -0,0 +1,36 @@ +uuid('id')->primary(); + $table->string('name', 255); + $table->uuid('organization_id'); + $table->foreign('organization_id') + ->references('id') + ->on('teams') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('clients'); + } +}; diff --git a/database/migrations/2024_01_20_110439_create_projects_table.php b/database/migrations/2024_01_20_110439_create_projects_table.php new file mode 100644 index 00000000..f1a8aff9 --- /dev/null +++ b/database/migrations/2024_01_20_110439_create_projects_table.php @@ -0,0 +1,43 @@ +uuid('id')->primary(); + $table->string('name', 255); + $table->string('color', 16); + $table->uuid('client_id')->nullable(); + $table->foreign('client_id') + ->references('id') + ->on('clients') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->uuid('organization_id'); + $table->foreign('organization_id') + ->references('id') + ->on('teams') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('projects'); + } +}; diff --git a/database/migrations/2024_01_20_110444_create_tasks_table.php b/database/migrations/2024_01_20_110444_create_tasks_table.php new file mode 100644 index 00000000..cda838cd --- /dev/null +++ b/database/migrations/2024_01_20_110444_create_tasks_table.php @@ -0,0 +1,42 @@ +uuid('id')->primary(); + $table->string('name', 255); + $table->uuid('project_id'); + $table->foreign('project_id') + ->references('id') + ->on('projects') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->uuid('organization_id'); + $table->foreign('organization_id') + ->references('id') + ->on('teams') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('tasks'); + } +}; diff --git a/database/migrations/2024_01_20_110452_create_tags_table.php b/database/migrations/2024_01_20_110452_create_tags_table.php new file mode 100644 index 00000000..c0dde2b5 --- /dev/null +++ b/database/migrations/2024_01_20_110452_create_tags_table.php @@ -0,0 +1,36 @@ +uuid('id')->primary(); + $table->string('name', 255); + $table->uuid('organization_id'); + $table->foreign('organization_id') + ->references('id') + ->on('teams') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('tags'); + } +}; diff --git a/database/migrations/2024_01_20_110837_create_time_entries_table.php b/database/migrations/2024_01_20_110837_create_time_entries_table.php new file mode 100644 index 00000000..ecaa5c54 --- /dev/null +++ b/database/migrations/2024_01_20_110837_create_time_entries_table.php @@ -0,0 +1,58 @@ +uuid('id')->primary(); + $table->string('description', 255); + $table->dateTime('start'); + $table->dateTime('end')->nullable(); + $table->boolean('billable')->default(false); + $table->uuid('user_id'); + $table->foreign('user_id') + ->references('id') + ->on('users') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->uuid('organization_id'); + $table->foreign('organization_id') + ->references('id') + ->on('teams') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->uuid('project_id')->nullable(); + $table->foreign('project_id') + ->references('id') + ->on('projects') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->uuid('task_id')->nullable(); + $table->foreign('task_id') + ->references('id') + ->on('tasks') + ->cascadeOnUpdate() + ->restrictOnDelete(); + $table->jsonb('tags')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('time_entries'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 07679bee..e9b52ac6 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -5,8 +5,14 @@ declare(strict_types=1); namespace Database\Seeders; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; +use App\Models\Client; +use App\Models\Project; +use App\Models\Task; +use App\Models\Team; +use App\Models\TimeEntry; use App\Models\User; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\DB; class DatabaseSeeder extends Seeder { @@ -15,9 +21,35 @@ class DatabaseSeeder extends Seeder */ public function run(): void { - User::factory()->withPersonalTeam()->create([ + $this->deleteAll(); + $organization = Team::factory()->create([ + 'name' => 'ACME Corp', + ]); + $user1 = User::factory()->withPersonalTeam()->create([ 'name' => 'Test User', 'email' => 'test@example.com', ]); + $user1->teams()->attach($organization); + $client = Client::factory()->create([ + 'name' => 'Big Company', + ]); + $bigCompanyProject = Project::factory()->forClient($client)->create([ + 'name' => 'Big Company Project', + ]); + Task::factory()->forProject($bigCompanyProject)->create(); + + $internalProject = Project::factory()->create([ + 'name' => 'Internal Project', + ]); + } + + private function deleteAll(): void + { + DB::table((new TimeEntry())->getTable())->delete(); + DB::table((new Task())->getTable())->delete(); + DB::table((new Project())->getTable())->delete(); + DB::table((new Client())->getTable())->delete(); + DB::table((new User())->getTable())->delete(); + DB::table((new Team())->getTable())->delete(); } } diff --git a/docker-compose.yml b/docker-compose.yml index b65aaa47..a0a43805 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,6 @@ services: extra_hosts: - 'host.docker.internal:host-gateway' ports: - - '${APP_PORT:-80}:80' - '${VITE_PORT:-5173}:${VITE_PORT:-5173}' environment: WWWUSER: '${WWWUSER}' diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php deleted file mode 100644 index 262903d1..00000000 --- a/tests/Unit/ExampleTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertTrue(true); - } -} diff --git a/tests/Unit/Model/ClientModelTest.php b/tests/Unit/Model/ClientModelTest.php new file mode 100644 index 00000000..bbd99a6a --- /dev/null +++ b/tests/Unit/Model/ClientModelTest.php @@ -0,0 +1,26 @@ +create(); + $client = Client::factory()->forOrganization($organization)->create(); + + // Act + $client->refresh(); + $organizationRel = $client->organization; + + // Assert + $this->assertNotNull($organizationRel); + $this->assertTrue($organizationRel->is($organization)); + } +} diff --git a/tests/Unit/Model/ModelTestAbstract.php b/tests/Unit/Model/ModelTestAbstract.php new file mode 100644 index 00000000..85708edf --- /dev/null +++ b/tests/Unit/Model/ModelTestAbstract.php @@ -0,0 +1,13 @@ +create(); + $project = Project::factory()->forOrganization($organization)->create(); + + // Act + $project->refresh(); + $organizationRel = $project->organization; + + // Assert + $this->assertNotNull($organizationRel); + $this->assertTrue($organizationRel->is($organization)); + } + + public function test_it_can_belong_to_a_client(): void + { + // Arrange + $client = Client::factory()->create(); + $project = Project::factory()->forClient($client)->create(); + + // Act + $project->refresh(); + $clientRel = $project->client; + + // Assert + $this->assertNotNull($clientRel); + $this->assertTrue($clientRel->is($client)); + } + + public function test_it_can_belong_to_no_client(): void + { + // Arrange + $project = Project::factory()->forClient(null)->create(); + + // Act + $project->refresh(); + $clientRel = $project->client; + + // Assert + $this->assertNull($clientRel); + } + + public function test_it_has_many_tasks(): void + { + // Arrange + $project = Project::factory()->create(); + $tasks = Task::factory()->forProject($project)->createMany(3); + + // Act + $project->refresh(); + $tasksRel = $project->tasks; + + // Assert + $this->assertNotNull($tasksRel); + $this->assertCount(3, $tasksRel); + $this->assertTrue($tasksRel->first()->is($tasks->first())); + } +} diff --git a/tests/Unit/Model/TagModelTest.php b/tests/Unit/Model/TagModelTest.php new file mode 100644 index 00000000..8c39c908 --- /dev/null +++ b/tests/Unit/Model/TagModelTest.php @@ -0,0 +1,26 @@ +create(); + $task = Tag::factory()->forOrganization($organization)->create(); + + // Act + $task->refresh(); + $organizationRel = $task->organization; + + // Assert + $this->assertNotNull($organizationRel); + $this->assertTrue($organizationRel->is($organization)); + } +} diff --git a/tests/Unit/Model/TaskModelTest.php b/tests/Unit/Model/TaskModelTest.php new file mode 100644 index 00000000..61c08f99 --- /dev/null +++ b/tests/Unit/Model/TaskModelTest.php @@ -0,0 +1,42 @@ +create(); + $task = Task::factory()->forOrganization($organization)->create(); + + // Act + $task->refresh(); + $organizationRel = $task->organization; + + // Assert + $this->assertNotNull($organizationRel); + $this->assertTrue($organizationRel->is($organization)); + } + + public function test_it_belongs_to_a_project(): void + { + // Arrange + $project = Project::factory()->create(); + $task = Task::factory()->forProject($project)->create(); + + // Act + $task->refresh(); + $projectRel = $task->project; + + // Assert + $this->assertNotNull($projectRel); + $this->assertTrue($projectRel->is($project)); + } +} diff --git a/tests/Unit/Model/TimeEntryModelTest.php b/tests/Unit/Model/TimeEntryModelTest.php new file mode 100644 index 00000000..24091165 --- /dev/null +++ b/tests/Unit/Model/TimeEntryModelTest.php @@ -0,0 +1,100 @@ +create(); + $timeEntry = TimeEntry::factory()->forUser($user)->create(); + + // Act + $timeEntry->refresh(); + $userRel = $timeEntry->user; + + // Assert + $this->assertNotNull($userRel); + $this->assertTrue($userRel->is($user)); + } + + public function test_it_belongs_to_a_organization(): void + { + // Arrange + $organization = Team::factory()->create(); + $timeEntry = TimeEntry::factory()->forOrganization($organization)->create(); + + // Act + $timeEntry->refresh(); + $organizationRel = $timeEntry->organization; + + // Assert + $this->assertNotNull($organizationRel); + $this->assertTrue($organizationRel->is($organization)); + } + + public function test_it_can_belong_to_a_project(): void + { + // Arrange + $project = Project::factory()->create(); + $timeEntry = TimeEntry::factory()->forProject($project)->create(); + + // Act + $timeEntry->refresh(); + $projectRel = $timeEntry->project; + + // Assert + $this->assertNotNull($projectRel); + $this->assertTrue($projectRel->is($project)); + } + + public function test_it_can_belong_to_no_project(): void + { + // Arrange + $timeEntry = TimeEntry::factory()->forProject(null)->create(); + + // Act + $timeEntry->refresh(); + $project = $timeEntry->project; + + // Assert + $this->assertNull($project); + } + + public function test_it_can_belong_to_a_task(): void + { + // Arrange + $task = Task::factory()->create(); + $timeEntry = TimeEntry::factory()->forTask($task)->create(); + + // Act + $timeEntry->refresh(); + $taskRel = $timeEntry->task; + + // Assert + $this->assertNotNull($taskRel); + $this->assertTrue($taskRel->is($task)); + } + + public function test_it_can_belong_to_no_task(): void + { + // Arrange + $timeEntry = TimeEntry::factory()->forTask(null)->create(); + + // Act + $timeEntry->refresh(); + $taskRel = $timeEntry->task; + + // Assert + $this->assertNull($taskRel); + } +} diff --git a/tests/Unit/Model/UserModelTest.php b/tests/Unit/Model/UserModelTest.php new file mode 100644 index 00000000..08a1fee9 --- /dev/null +++ b/tests/Unit/Model/UserModelTest.php @@ -0,0 +1,26 @@ +create(); + $panelProvider = new AdminPanelProvider(app()); + $mainPanel = $panelProvider->panel(Panel::make()); + + // Act + $canAccess = $user->canAccessPanel($mainPanel); + + // Assert + $this->assertFalse($canAccess); + } +}