Compare commits

...

2 Commits

Author SHA1 Message Date
Constantin Graf
5b756be058 Updated composer dependencies 2026-05-22 16:18:02 +02:00
Constantin Graf
dc70eb7130 Add more tests 2026-05-22 16:06:51 +02:00
28 changed files with 2419 additions and 1253 deletions

View File

@@ -50,7 +50,7 @@ class FailedJobResource extends Resource
TextInput::make('queue')->disabled(),
// make text a little bit smaller because often a complete Stack Trace is shown:
TextArea::make('exception')->disabled()->columnSpan(4)->extraInputAttributes(['style' => 'font-size: 80%;']),
Textarea::make('exception')->disabled()->columnSpan(4)->extraInputAttributes(['style' => 'font-size: 80%;']),
PrettyJsonField::make('payload')->disabled()->columnSpan(4),
])->columns(4);
}

View File

@@ -39,7 +39,7 @@ class OrganizationInvitationResource extends Resource
->required(),
Select::make('role')
->options(Role::class),
Forms\Components\Select::make('organization_id')
Select::make('organization_id')
->label('Organization')
->relationship(name: 'organization', titleAttribute: 'name')
->searchable(['name'])

View File

@@ -55,7 +55,7 @@ class OrganizationResource extends Resource
->label('Is personal?')
->hiddenOn(['create'])
->required(),
Forms\Components\Select::make('user_id')
Select::make('user_id')
->label('Owner')
->relationship(name: 'owner', titleAttribute: 'email')
->searchable(['name', 'email'])
@@ -76,7 +76,7 @@ class OrganizationResource extends Resource
Select::make('time_format')
->options(TimeFormat::toSelectArray())
->required(),
Forms\Components\Select::make('currency')
Select::make('currency')
->label('Currency')
->options(function (): array {
$currencies = ISOCurrencyProvider::getInstance()->getAvailableCurrencies();
@@ -114,22 +114,22 @@ class OrganizationResource extends Resource
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')
TextColumn::make('name')
->searchable()
->sortable(),
Tables\Columns\IconColumn::make('personal_team')
->boolean()
->label('Is personal?')
->sortable(),
Tables\Columns\TextColumn::make('owner.email')
TextColumn::make('owner.email')
->sortable(),
Tables\Columns\TextColumn::make('currency'),
TextColumn::make('currency'),
TextColumn::make('billable_rate')
->money(fn (Organization $resource) => $resource->currency, divideBy: 100),
Tables\Columns\TextColumn::make('created_at')
TextColumn::make('created_at')
->dateTime()
->sortable(),
Tables\Columns\TextColumn::make('updated_at')
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
@@ -223,7 +223,7 @@ class OrganizationResource extends Resource
return $select;
}),
Forms\Components\Select::make('timezone')
Select::make('timezone')
->label('Timezone')
->options(fn (): array => app(TimezoneService::class)->getSelectOptions())
->searchable()

View File

@@ -49,13 +49,13 @@ class UsersRelationManager extends RelationManager
return $table
->recordTitleAttribute('name')
->columns([
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('role'),
TextColumn::make('name'),
TextColumn::make('role'),
TextColumn::make('billable_rate')
->money($organization->currency, divideBy: 100),
])
->headerActions([
Tables\Actions\AttachAction::make()
AttachAction::make()
->recordTitle(fn (User $record): string => "{$record->name} ({$record->email})")
->form(fn (AttachAction $action): array => [
$action->getRecordSelect(),

View File

@@ -63,11 +63,11 @@ class ReportResource extends Resource
return $record->getRawOriginal('properties');
})
->disabled(),
Forms\Components\DateTimePicker::make('created_at')
DateTimePicker::make('created_at')
->label('Created At')
->hiddenOn(['create'])
->disabled(),
Forms\Components\DateTimePicker::make('updated_at')
DateTimePicker::make('updated_at')
->label('Updated At')
->hiddenOn(['create'])
->disabled(),
@@ -78,10 +78,10 @@ class ReportResource extends Resource
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')
TextColumn::make('name')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('description')
TextColumn::make('description')
->searchable()
->sortable(),
ToggleColumn::make('is_public')
@@ -90,10 +90,10 @@ class ReportResource extends Resource
TextColumn::make('organization.name')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('created_at')
TextColumn::make('created_at')
->dateTime()
->sortable(),
Tables\Columns\TextColumn::make('updated_at')
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),

View File

@@ -93,11 +93,11 @@ class TimeEntryResource extends Resource
($record->end?->toDateTimeString('minute') ?? '...').')';
})
->label('Time'),
Tables\Columns\TextColumn::make('organization.name')
TextColumn::make('organization.name')
->sortable(),
Tables\Columns\TextColumn::make('created_at')
TextColumn::make('created_at')
->sortable(),
Tables\Columns\TextColumn::make('updated_at')
TextColumn::make('updated_at')
->sortable(),
])
->filters([

View File

@@ -47,17 +47,17 @@ class UserResource extends Resource
return $form
->columns(1)
->schema([
Forms\Components\TextInput::make('id')
TextInput::make('id')
->label('ID')
->disabled()
->visibleOn(['update', 'show'])
->readOnly()
->maxLength(255),
Forms\Components\TextInput::make('name')
TextInput::make('name')
->label('Name')
->required()
->maxLength(255),
Forms\Components\TextInput::make('email')
TextInput::make('email')
->label('Email')
->required()
->rules($record?->is_placeholder ? [] : [

View File

@@ -59,7 +59,7 @@ use Spatie\TemporaryDirectory\TemporaryDirectory;
class TimeEntryController extends Controller
{
private function assertNoOverlap(Organization $organization, Member $member, \Illuminate\Support\Carbon $start, ?\Illuminate\Support\Carbon $end, ?TimeEntry $exclude = null): void
private function assertNoOverlap(Organization $organization, Member $member, Carbon $start, ?Carbon $end, ?TimeEntry $exclude = null): void
{
if (! $organization->prevent_overlapping_time_entries) {
return;

View File

@@ -4,9 +4,37 @@ declare(strict_types=1);
namespace App\Http;
use App\Http\Middleware\Authenticate;
use App\Http\Middleware\CheckOrganizationBlocked;
use App\Http\Middleware\EncryptCookies;
use App\Http\Middleware\EnsureEmailIsVerified;
use App\Http\Middleware\ForceHttps;
use App\Http\Middleware\ForceJsonResponse;
use App\Http\Middleware\HandleInertiaRequests;
use App\Http\Middleware\PreventRequestsDuringMaintenance;
use App\Http\Middleware\RedirectIfAuthenticated;
use App\Http\Middleware\ShareInertiaData;
use App\Http\Middleware\TrimStrings;
use App\Http\Middleware\TrustProxies;
use App\Http\Middleware\ValidateSignature;
use App\Http\Middleware\VerifyCsrfToken;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Auth\Middleware\RequirePassword;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets;
use Illuminate\Http\Middleware\HandleCors;
use Illuminate\Http\Middleware\SetCacheHeaders;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
class Kernel extends HttpKernel
{
@@ -18,13 +46,13 @@ class Kernel extends HttpKernel
* @var array<int, class-string|string>
*/
protected $middleware = [
\App\Http\Middleware\ForceHttps::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
ForceHttps::class,
TrustProxies::class,
HandleCors::class,
PreventRequestsDuringMaintenance::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
];
/**
@@ -34,21 +62,21 @@ class Kernel extends HttpKernel
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\HandleInertiaRequests::class,
\App\Http\Middleware\ShareInertiaData::class,
\Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
HandleInertiaRequests::class,
ShareInertiaData::class,
AddLinkHeadersForPreloadedAssets::class,
CreateFreshApiToken::class,
],
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
ThrottleRequests::class.':api',
SubstituteBindings::class,
ForceJsonResponse::class,
],
@@ -64,17 +92,17 @@ class Kernel extends HttpKernel
* @var array<string, class-string|string>
*/
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class,
'auth' => Authenticate::class,
'auth.basic' => AuthenticateWithBasicAuth::class,
'auth.session' => AuthenticateSession::class,
'cache.headers' => SetCacheHeaders::class,
'can' => Authorize::class,
'guest' => RedirectIfAuthenticated::class,
'password.confirm' => RequirePassword::class,
'precognitive' => HandlePrecognitiveRequests::class,
'signed' => ValidateSignature::class,
'throttle' => ThrottleRequests::class,
'verified' => EnsureEmailIsVerified::class,
'check-organization-blocked' => CheckOrganizationBlocked::class,
];
}

View File

@@ -14,7 +14,7 @@ class ForceHttps
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @param Closure(Request): (Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{

View File

@@ -13,7 +13,7 @@ class ForceJsonResponse
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @param Closure(Request): (Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{

View File

@@ -15,7 +15,7 @@ class RedirectIfAuthenticated
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @param Closure(Request): (Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{

View File

@@ -7,6 +7,7 @@ namespace App\Http\Requests\V1\Member;
use App\Http\Requests\V1\BaseFormRequest;
use App\Models\Member;
use App\Models\Organization;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Database\Eloquent\Builder;
use Korridor\LaravelModelValidationRules\Rules\ExistsEloquent;
@@ -19,7 +20,7 @@ class MemberMergeIntoRequest extends BaseFormRequest
/**
* Get the validation rules that apply to the request.
*
* @return array<string, array<string|ValidationRule|\Illuminate\Contracts\Validation\Rule>>
* @return array<string, array<string|ValidationRule|Rule>>
*/
public function rules(): array
{

View File

@@ -6,6 +6,7 @@ namespace App\Http\Requests\V1\Organization;
use App\Http\Requests\V1\BaseFormRequest;
use App\Models\Organization;
use Illuminate\Contracts\Validation\Rule;
/**
* @property Organization $organization Organization from model binding
@@ -15,7 +16,7 @@ class OrganizationStoreRequest extends BaseFormRequest
/**
* Get the validation rules that apply to the request.
*
* @return array<string, array<string|\Illuminate\Contracts\Validation\Rule>>
* @return array<string, array<string|Rule>>
*/
public function rules(): array
{

View File

@@ -24,6 +24,7 @@ use Illuminate\Support\Str;
use Laravel\Jetstream\Events\TeamCreated;
use Laravel\Jetstream\Events\TeamDeleted;
use Laravel\Jetstream\Events\TeamUpdated;
use Laravel\Jetstream\Team;
use Laravel\Jetstream\Team as JetstreamTeam;
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;
@@ -177,7 +178,7 @@ class Organization extends JetstreamTeam implements AuditableContract
*
* @param array<string> $columns
*/
public function findOrFail(string $id, array $columns = ['*']): \Laravel\Jetstream\Team
public function findOrFail(string $id, array $columns = ['*']): Team
{
if (! Str::isUuid($id)) {
throw (new ModelNotFoundException)->setModel(

View File

@@ -10,6 +10,9 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\File;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage;
use League\Csv\CannotInsertRecord;
use League\Csv\Exception;
use League\Csv\UnavailableStream;
use League\Csv\Writer;
use Spatie\TemporaryDirectory\TemporaryDirectory;
@@ -58,9 +61,9 @@ abstract class CsvExport
abstract public function mapRow(Model $model): array;
/**
* @throws \League\Csv\CannotInsertRecord
* @throws \League\Csv\Exception
* @throws \League\Csv\UnavailableStream
* @throws CannotInsertRecord
* @throws Exception
* @throws UnavailableStream
*/
public function export(): void
{
@@ -72,6 +75,7 @@ abstract class CsvExport
$writer->insertOne(static::HEADER);
$this->builder->chunk($this->chunk, function (Collection $models) use ($writer): void {
/** @var T $model */
foreach ($models as $model) {
$data = $this->mapRow($model);
$row = $this->convertRow($data);

View File

@@ -1,6 +1,10 @@
<?php
declare(strict_types=1);
use App\Exceptions\Handler;
use App\Http\Kernel;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Foundation\Application;
/*
|--------------------------------------------------------------------------
@@ -13,7 +17,7 @@ declare(strict_types=1);
|
*/
$app = new Illuminate\Foundation\Application(
$app = new Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
@@ -30,7 +34,7 @@ $app = new Illuminate\Foundation\Application(
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
Kernel::class
);
$app->singleton(
@@ -39,8 +43,8 @@ $app->singleton(
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
ExceptionHandler::class,
Handler::class
);
/*

3383
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,13 @@ use App\Enums\DateFormat;
use App\Enums\IntervalFormat;
use App\Enums\NumberFormat;
use App\Enums\TimeFormat;
use App\Providers\AppServiceProvider;
use App\Providers\AuthServiceProvider;
use App\Providers\EventServiceProvider;
use App\Providers\Filament\AdminPanelProvider;
use App\Providers\FortifyServiceProvider;
use App\Providers\JetstreamServiceProvider;
use App\Providers\RouteServiceProvider;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider;
use Nwidart\Modules\LaravelModulesServiceProvider;
@@ -190,13 +197,13 @@ return [
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\Filament\AdminPanelProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\FortifyServiceProvider::class,
App\Providers\JetstreamServiceProvider::class,
AppServiceProvider::class,
AuthServiceProvider::class,
EventServiceProvider::class,
AdminPanelProvider::class,
RouteServiceProvider::class,
FortifyServiceProvider::class,
JetstreamServiceProvider::class,
// Warning: Do not add TelescopeServiceProvider here since it is already conditionally registered in AppServiceProvider
LaravelModulesServiceProvider::class,
])->toArray(),

View File

@@ -1,6 +1,11 @@
<?php
declare(strict_types=1);
use App\Extensions\Auditing\Resolvers\CustomIpAddressResolver;
use OwenIt\Auditing\Models\Audit;
use OwenIt\Auditing\Resolvers\UrlResolver;
use OwenIt\Auditing\Resolvers\UserAgentResolver;
use OwenIt\Auditing\Resolvers\UserResolver;
return [
@@ -15,7 +20,7 @@ return [
|
*/
'implementation' => OwenIt\Auditing\Models\Audit::class,
'implementation' => Audit::class,
/*
|--------------------------------------------------------------------------
@@ -32,7 +37,7 @@ return [
'web',
'api',
],
'resolver' => OwenIt\Auditing\Resolvers\UserResolver::class,
'resolver' => UserResolver::class,
],
/*
@@ -44,9 +49,9 @@ return [
|
*/
'resolvers' => [
'ip_address' => App\Extensions\Auditing\Resolvers\CustomIpAddressResolver::class,
'user_agent' => OwenIt\Auditing\Resolvers\UserAgentResolver::class,
'url' => OwenIt\Auditing\Resolvers\UrlResolver::class,
'ip_address' => CustomIpAddressResolver::class,
'user_agent' => UserAgentResolver::class,
'url' => UrlResolver::class,
],
/*

View File

@@ -1,6 +1,7 @@
<?php
declare(strict_types=1);
use App\Models\User;
return [
@@ -69,7 +70,7 @@ return [
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
'model' => User::class,
],
],

View File

@@ -2,6 +2,7 @@
declare(strict_types=1);
use Maatwebsite\Excel\DefaultValueBinder;
use Maatwebsite\Excel\Excel;
use PhpOffice\PhpSpreadsheet\Reader\Csv;
@@ -226,7 +227,7 @@ return [
|
*/
'value_binder' => [
'default' => Maatwebsite\Excel\DefaultValueBinder::class,
'default' => DefaultValueBinder::class,
],
'cache' => [

View File

@@ -9,6 +9,7 @@ use App\Enums\Weekday;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Http\FileHelpers;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
@@ -90,7 +91,7 @@ class UserFactory extends Factory
public function withProfilePicture(): static
{
$profilePhoto = $this->faker->image(null, 500, 500);
/** @see \Illuminate\Http\FileHelpers::hashName */
/** @see FileHelpers::hashName */
$path = 'profile-photos/'.Str::random(40).'.png';
Storage::disk(config('jetstream.profile_photo_disk', 'public'))->put($path, $profilePhoto);

View File

@@ -27,7 +27,7 @@ return new class extends Migration
if ($duplicateEmails->isNotEmpty()) {
$duplicateEmailMessage = $duplicateEmails
->take(20)
->map(fn (\stdClass $duplicateEmail): string => sprintf(
->map(fn (stdClass $duplicateEmail): string => sprintf(
'%s (%d users: %s)',
$duplicateEmail->normalized_email,
$duplicateEmail->user_count,

View File

@@ -14,5 +14,4 @@ parameters:
noEnvCallsOutsideOfConfig: true
ignoreErrors:
- '# is not subtype of native type Illuminate\\Database\\Eloquent\\Builder#'
- '# is not subtype of native type Illuminate\\Database\\Eloquent\\Relations\\Relation#'

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Tests\Unit\Endpoint\Api\V1;
use App\Enums\Role;
use App\Http\Controllers\Api\V1\TaskController;
use App\Models\Project;
use App\Models\ProjectMember;
@@ -755,7 +756,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_store_endpoint_allows_employee_to_create_task_in_public_project_when_employees_can_manage_tasks_is_enabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = true;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPublic()->create();
@@ -779,7 +780,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_store_endpoint_allows_employee_to_create_task_in_accessible_private_project_when_employees_can_manage_tasks_is_enabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = true;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPrivate()->create();
@@ -804,7 +805,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_store_endpoint_fails_for_employee_creating_task_in_inaccessible_private_project_when_employees_can_manage_tasks_is_enabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = true;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPrivate()->create();
@@ -827,7 +828,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_store_endpoint_fails_for_employee_when_employees_can_manage_tasks_is_disabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = false;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPublic()->create();
@@ -849,7 +850,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_update_endpoint_allows_employee_to_update_task_in_public_project_when_employees_can_manage_tasks_is_enabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = true;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPublic()->create();
@@ -872,7 +873,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_update_endpoint_allows_employee_to_update_task_in_accessible_private_project_when_employees_can_manage_tasks_is_enabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = true;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPrivate()->create();
@@ -896,7 +897,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_update_endpoint_fails_for_employee_updating_task_in_inaccessible_private_project_when_employees_can_manage_tasks_is_enabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = true;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPrivate()->create();
@@ -920,7 +921,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_update_endpoint_fails_for_employee_when_employees_can_manage_tasks_is_disabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = false;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPublic()->create();
@@ -944,7 +945,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_delete_endpoint_allows_employee_to_delete_task_in_public_project_when_employees_can_manage_tasks_is_enabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = true;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPublic()->create();
@@ -964,7 +965,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_delete_endpoint_allows_employee_to_delete_task_in_accessible_private_project_when_employees_can_manage_tasks_is_enabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = true;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPrivate()->create();
@@ -985,7 +986,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_delete_endpoint_fails_for_employee_deleting_task_in_inaccessible_private_project_when_employees_can_manage_tasks_is_enabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = true;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPrivate()->create();
@@ -1005,7 +1006,7 @@ class TaskEndpointTest extends ApiEndpointTestAbstract
public function test_delete_endpoint_fails_for_employee_when_employees_can_manage_tasks_is_disabled(): void
{
// Arrange
$data = $this->createUserWithRole(\App\Enums\Role::Employee);
$data = $this->createUserWithRole(Role::Employee);
$data->organization->employees_can_manage_tasks = false;
$data->organization->save();
$project = Project::factory()->forOrganization($data->organization)->isPublic()->create();

View File

@@ -240,6 +240,22 @@ class UserEndpointTest extends ApiEndpointTestAbstract
$response->assertJsonValidationErrors(['name']);
}
public function test_update_fails_if_given_user_is_not_the_authenticated_user(): void
{
// Arrange
$data = $this->createUserWithPermission();
$otherData = $this->createUserWithPermission();
Passport::actingAs($otherData->user);
// Act
$response = $this->putJson(route('api.v1.users.update', $data->user->getKey()), [
'name' => 'Updated Name',
]);
// Assert
$response->assertForbidden();
}
public function test_update_fails_if_name_is_too_long(): void
{
// Arrange

View File

@@ -251,4 +251,27 @@ class OrganizationInvitationEndpointTest extends EndpointTestAbstract
// Assert
$response->assertForbidden();
}
public function test_fails_if_invitation_has_owner_role(): void
{
// Arrange
$user = $this->createUserWithPermission();
$invitation = OrganizationInvitation::factory()
->forOrganization($user->organization)
->create([
'role' => Role::Owner->value,
]);
// Act
$acceptUrl = URL::to(URL::temporarySignedRoute(
'organization-invitations.accept',
now()->addMinutes(60),
[$invitation->getKey()],
false
));
$response = $this->get($acceptUrl);
// Assert
$response->assertStatus(500);
}
}