user = User::factory()->create(); $this->token = $this->user->createToken('AppToken')->accessToken; Storage::fake('local'); } /** * Test user can list their portfolios. */ public function test_user_can_list_portfolios() { Portfolio::factory(3)->create(['user_id' => $this->user->id]); $response = $this->getJson('/api/portfolios', [ 'Authorization' => "Bearer $this->token", ]); $response->assertStatus(200) ->assertJsonStructure([ 'success', 'message', 'data' => [ '*' => ['id', 'name', 'domain', 'user_id'], ] ]) ->assertJson(['success' => true]); } /** * Test user can create a portfolio. */ public function test_user_can_create_portfolio() { $response = $this->postJson('/api/portfolios', [ 'name' => 'My Portfolio', 'domain' => 'myportfolio.com', ], [ 'Authorization' => "Bearer $this->token", ]); $response->assertStatus(201) ->assertJsonStructure([ 'success', 'message', 'data' => ['id', 'name', 'domain', 'user_id'], ]) ->assertJson([ 'success' => true, 'data' => [ 'name' => 'My Portfolio', 'domain' => 'myportfolio.com', ] ]); $this->assertDatabaseHas('portfolios', [ 'name' => 'My Portfolio', 'domain' => 'myportfolio.com', 'user_id' => $this->user->id, ]); } /** * Test portfolio creation fails with duplicate domain. */ public function test_portfolio_creation_fails_with_duplicate_domain() { Portfolio::factory()->create(['domain' => 'myportfolio.com']); $response = $this->postJson('/api/portfolios', [ 'name' => 'Another Portfolio', 'domain' => 'myportfolio.com', ], [ 'Authorization' => "Bearer $this->token", ]); $response->assertStatus(422) ->assertJsonValidationErrors('domain'); } /** * Test user can view their own portfolio. */ public function test_user_can_view_own_portfolio() { $portfolio = Portfolio::factory()->create(['user_id' => $this->user->id]); $response = $this->getJson("/api/portfolios/{$portfolio->id}", [ 'Authorization' => "Bearer $this->token", ]); $response->assertStatus(200) ->assertJsonStructure([ 'success', 'message', 'data' => ['id', 'name', 'domain', 'user_id'], ]) ->assertJson([ 'success' => true, 'data' => [ 'id' => $portfolio->id, 'name' => $portfolio->name, ] ]); } /** * Test user cannot view another user's portfolio. */ public function test_user_cannot_view_another_users_portfolio() { $otherUser = User::factory()->create(); $portfolio = Portfolio::factory()->create(['user_id' => $otherUser->id]); $response = $this->getJson("/api/portfolios/{$portfolio->id}", [ 'Authorization' => "Bearer $this->token", ]); $response->assertStatus(403); } /** * Test user can update their portfolio. */ public function test_user_can_update_portfolio() { $portfolio = Portfolio::factory()->create(['user_id' => $this->user->id]); $response = $this->putJson("/api/portfolios/{$portfolio->id}", [ 'name' => 'Updated Portfolio', 'domain' => 'updated.com', ], [ 'Authorization' => "Bearer $this->token", ]); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'name' => 'Updated Portfolio', 'domain' => 'updated.com', ] ]); $this->assertDatabaseHas('portfolios', [ 'id' => $portfolio->id, 'name' => 'Updated Portfolio', ]); } /** * Test user cannot update another user's portfolio. */ public function test_user_cannot_update_another_users_portfolio() { $otherUser = User::factory()->create(); $portfolio = Portfolio::factory()->create(['user_id' => $otherUser->id]); $response = $this->putJson("/api/portfolios/{$portfolio->id}", [ 'name' => 'Hacked', ], [ 'Authorization' => "Bearer $this->token", ]); $response->assertStatus(403); } /** * Test user can delete their portfolio. */ public function test_user_can_delete_portfolio() { $portfolio = Portfolio::factory()->create(['user_id' => $this->user->id]); $response = $this->deleteJson("/api/portfolios/{$portfolio->id}", [], [ 'Authorization' => "Bearer $this->token", ]); $response->assertStatus(200) ->assertJson(['success' => true]); $this->assertDatabaseMissing('portfolios', ['id' => $portfolio->id]); } /** * Test user cannot delete another user's portfolio. */ public function test_user_cannot_delete_another_users_portfolio() { $otherUser = User::factory()->create(); $portfolio = Portfolio::factory()->create(['user_id' => $otherUser->id]); $response = $this->deleteJson("/api/portfolios/{$portfolio->id}", [], [ 'Authorization' => "Bearer $this->token", ]); $response->assertStatus(403); } /** * Test user can upload file to their portfolio. */ public function test_user_can_upload_file_to_portfolio() { $portfolio = Portfolio::factory()->create([ 'user_id' => $this->user->id, 'active' => true, ]); $file = UploadedFile::fake()->create('site.html', 100); $response = $this->postJson( "/api/portfolios/{$portfolio->id}/upload", ['file' => $file], ['Authorization' => "Bearer $this->token"], ); $response->assertStatus(200) ->assertJson(['success' => true]); $this->assertDatabaseHas('portfolios', [ 'id' => $portfolio->id, ]); } /** * Test upload fails for inactive portfolio. */ public function test_upload_fails_for_inactive_portfolio() { $portfolio = Portfolio::factory()->create([ 'user_id' => $this->user->id, 'active' => false, ]); $file = UploadedFile::fake()->create('site.html', 100); $response = $this->postJson( "/api/portfolios/{$portfolio->id}/upload", ['file' => $file], ['Authorization' => "Bearer $this->token"], ); $response->assertStatus(403); } /** * Test upload fails for another user's portfolio. */ public function test_user_cannot_upload_to_another_users_portfolio() { $otherUser = User::factory()->create(); $portfolio = Portfolio::factory()->create([ 'user_id' => $otherUser->id, 'active' => true, ]); $file = UploadedFile::fake()->create('site.html', 100); $response = $this->postJson( "/api/portfolios/{$portfolio->id}/upload", ['file' => $file], ['Authorization' => "Bearer $this->token"], ); $response->assertStatus(403); } /** * Test upload fails with file too large. */ public function test_upload_fails_with_file_too_large() { $portfolio = Portfolio::factory()->create([ 'user_id' => $this->user->id, 'active' => true, ]); $file = UploadedFile::fake()->create('site.html', 11 * 1024); // 11MB $response = $this->postJson( "/api/portfolios/{$portfolio->id}/upload", ['file' => $file], ['Authorization' => "Bearer $this->token"], ); $response->assertStatus(422) ->assertJsonValidationErrors('file'); } /** * Test user can deploy portfolio. */ public function test_user_can_deploy_portfolio() { $portfolio = Portfolio::factory()->create(['user_id' => $this->user->id]); $response = $this->postJson( "/api/portfolios/{$portfolio->id}/deploy", [], ['Authorization' => "Bearer $this->token"], ); $response->assertStatus(200) ->assertJson(['success' => true]); } /** * Test deploy fails for another user's portfolio. */ public function test_user_cannot_deploy_another_users_portfolio() { $otherUser = User::factory()->create(); $portfolio = Portfolio::factory()->create(['user_id' => $otherUser->id]); $response = $this->postJson( "/api/portfolios/{$portfolio->id}/deploy", [], ['Authorization' => "Bearer $this->token"], ); $response->assertStatus(403); } /** * Test get random portfolio returns a portfolio domain. */ public function test_get_random_portfolio() { Portfolio::factory(5)->create(); $response = $this->getJson('/api/portfolio/random'); $response->assertStatus(200) ->assertJsonStructure([ 'success', 'message', 'data' => ['host'], ]) ->assertJson(['success' => true]); } /** * Test authenticated requests fail without token. */ public function test_authenticated_endpoints_require_token() { $response = $this->getJson('/api/portfolios'); $response->assertStatus(401); } }