diff --git a/app/Models/Organization.php b/app/Models/Organization.php index 315a1a6a..bf254c71 100644 --- a/app/Models/Organization.php +++ b/app/Models/Organization.php @@ -8,9 +8,11 @@ use App\Models\Concerns\HasUuids; use Database\Factories\OrganizationFactory; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Carbon; +use Illuminate\Support\Str; use Laravel\Jetstream\Events\TeamCreated; use Laravel\Jetstream\Events\TeamDeleted; use Laravel\Jetstream\Events\TeamUpdated; @@ -123,4 +125,21 @@ class Organization extends JetstreamTeam return $this->users() ->where('is_placeholder', false); } + + /** + * This method prevents an unhandled exception when the ID is not a UUID. + * Normally this can be fixed with a route pattern, but Jetstream does not use route model binding. + * + * @param array $columns + */ + public function findOrFail(string $id, array $columns = ['*']): \Laravel\Jetstream\Team + { + if (! Str::isUuid($id)) { + throw (new ModelNotFoundException)->setModel( + self::class, $id + ); + } + + return parent::findOrFail($id, $columns); + } } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index eaef14c7..b29e595c 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -116,6 +116,7 @@ class UserFactory extends Factory ->when(is_callable($callback), $callback) ->create(); + $organization->owner()->associate($user); $organization->users()->attach($user, ['role' => Role::Owner->value]); $user->currentTeam()->associate($organization); $user->save(); diff --git a/tests/Feature/UpdateTeamNameTest.php b/tests/Feature/UpdateTeamTest.php similarity index 69% rename from tests/Feature/UpdateTeamNameTest.php rename to tests/Feature/UpdateTeamTest.php index c196e330..40efd4ea 100644 --- a/tests/Feature/UpdateTeamNameTest.php +++ b/tests/Feature/UpdateTeamTest.php @@ -8,10 +8,23 @@ use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; -class UpdateTeamNameTest extends TestCase +class UpdateTeamTest extends TestCase { use RefreshDatabase; + public function test_team_update_page_shows_not_found_if_id_is_not_uuid(): void + { + // Arrange + $user = User::factory()->withPersonalOrganization()->create(); + $this->actingAs($user); + + // Act + $response = $this->get('/teams/1'); + + // Assert + $response->assertStatus(404); + } + public function test_team_names_can_be_updated(): void { // Arrange diff --git a/tests/Unit/Endpoint/Api/V1/OrganizationEndpointTest.php b/tests/Unit/Endpoint/Api/V1/OrganizationEndpointTest.php index 456551d2..ad40ac2b 100644 --- a/tests/Unit/Endpoint/Api/V1/OrganizationEndpointTest.php +++ b/tests/Unit/Endpoint/Api/V1/OrganizationEndpointTest.php @@ -14,6 +14,21 @@ use PHPUnit\Framework\Attributes\UsesClass; #[UsesClass(OrganizationController::class)] class OrganizationEndpointTest extends ApiEndpointTestAbstract { + public function test_show_endpoint_fails_with_not_found_if_id_is_not_uuid(): void + { + // Arrange + $data = $this->createUserWithPermission([ + 'organizations:view', + ]); + Passport::actingAs($data->user); + + // Act + $response = $this->getJson(route('api.v1.organizations.show', ['not-uuid'])); + + // Assert + $response->assertNotFound(); + } + public function test_show_endpoint_fails_if_user_has_no_permission_to_view_organizations(): void { // Arrange