post('/register', [ 'email' => 'flow-user@example.com', ]); $register->assertRedirect('/register/notice'); $user = User::query()->where('email', 'flow-user@example.com')->firstOrFail(); expect($user->onboarding_step)->toBe('email'); $token = (string) DB::table('user_verification_tokens') ->where('user_id', $user->id) ->value('token'); $this->get('/verify/' . $token)->assertRedirect('/setup/password'); $user->refresh(); expect($user->onboarding_step)->toBe('verified'); $this->actingAs($user) ->post('/setup/password', [ 'password' => 'StrongPass1!', 'password_confirmation' => 'StrongPass1!', ])->assertRedirect('/setup/username'); $this->actingAs($user) ->post('/setup/username', [ 'username' => 'flow_user_final', ])->assertRedirect('/@flow_user_final'); $user->refresh(); expect($user->onboarding_step)->toBe('complete'); expect($user->username)->toBe('flow_user_final'); }); it('rejects invalid verification token', function () { $response = $this->from('/login')->get('/verify/not-a-real-token'); $response->assertRedirect('/login'); $response->assertSessionHasErrors('email'); }); it('rejects expired verification token', function () { $user = User::factory()->create([ 'email_verified_at' => null, 'onboarding_step' => 'email', 'is_active' => false, ]); DB::table('user_verification_tokens')->insert([ 'user_id' => $user->id, 'token' => 'expired-checklist-token', 'expires_at' => now()->subHour(), 'created_at' => now(), 'updated_at' => now(), ]); $response = $this->from('/login')->get('/verify/expired-checklist-token'); $response->assertRedirect('/login'); $response->assertSessionHasErrors('email'); expect($user->fresh()->email_verified_at)->toBeNull(); }); it('rejects duplicate email at registration', function () { User::factory()->create([ 'email' => 'duplicate-check@example.com', ]); $response = $this->from('/register')->post('/register', [ 'email' => 'duplicate-check@example.com', ]); $response->assertRedirect('/register'); $response->assertSessionHasErrors('email'); }); it('rejects username conflict during username setup', function () { User::factory()->create([ 'username' => 'taken_username', 'onboarding_step' => 'complete', ]); $user = User::factory()->create([ 'username' => 'candidate_username', 'onboarding_step' => 'password', ]); $this->actingAs($user) ->from('/setup/username') ->post('/setup/username', [ 'username' => 'taken_username', ]) ->assertRedirect('/setup/username') ->assertSessionHasErrors('username'); expect($user->fresh()->onboarding_step)->toBe('password'); }); it('resumes onboarding by redirecting user to current required step', function () { $user = User::factory()->create([ 'onboarding_step' => 'verified', ]); $this->actingAs($user) ->get('/profile') ->assertRedirect('/setup/password'); $user->forceFill(['onboarding_step' => 'password'])->save(); $this->actingAs($user) ->get('/upload') ->assertRedirect('/setup/username'); });