diff --git a/app-modules/onboarding/src/DTOs/GitChallengeDTO.php b/app-modules/onboarding/src/DTOs/GitChallengeDTO.php new file mode 100644 index 00000000..9206608b --- /dev/null +++ b/app-modules/onboarding/src/DTOs/GitChallengeDTO.php @@ -0,0 +1,49 @@ + $data + */ + public function __construct( + public array $data = [], + ) {} + + /** + * @throws ValidationException + */ + public function validate(array $payload): static + { + $validated = Validator::make($payload, [ + 'data' => ['sometimes', 'array'], + ])->validate(); + + $data = $validated['data'] ?? []; + + return new self( + data: is_array($data) ? $data : [], + ); + } + + /** + * @return array + */ + public function toArray(): array + { + return $this->data; + } +} diff --git a/app-modules/onboarding/src/Flows/SquadsOnboardingFlow.php b/app-modules/onboarding/src/Flows/SquadsOnboardingFlow.php index bd9e72cb..76d619a4 100644 --- a/app-modules/onboarding/src/Flows/SquadsOnboardingFlow.php +++ b/app-modules/onboarding/src/Flows/SquadsOnboardingFlow.php @@ -7,6 +7,7 @@ use He4rt\Identity\ExternalIdentity\Enums\IdentityProvider; use He4rt\Onboarding\Contracts\OnboardingFlow; use He4rt\Onboarding\Contracts\OnboardingStepDTO; +use He4rt\Onboarding\DTOs\GitChallengeDTO; use He4rt\Onboarding\DTOs\WelcomeFormDTO; use He4rt\Onboarding\Enums\OnboardingStatus; use He4rt\Onboarding\Enums\OnboardingStepStatus; @@ -26,6 +27,7 @@ public function stepDto(string $stepKey): OnboardingStepDTO { return match ($stepKey) { 'form' => new WelcomeFormDTO(), + 'git_challenge' => new GitChallengeDTO(), default => throw new InvalidArgumentException('Step não suportado: '.$stepKey), }; } diff --git a/app-modules/onboarding/tests/Feature/SquadsOnboardingFlowTest.php b/app-modules/onboarding/tests/Feature/SquadsOnboardingFlowTest.php index 053b1ea0..ffb80882 100644 --- a/app-modules/onboarding/tests/Feature/SquadsOnboardingFlowTest.php +++ b/app-modules/onboarding/tests/Feature/SquadsOnboardingFlowTest.php @@ -5,7 +5,9 @@ use He4rt\Identity\ExternalIdentity\Enums\IdentityProvider; use He4rt\Identity\ExternalIdentity\Models\ExternalIdentity; use He4rt\Identity\User\Models\User; +use He4rt\Onboarding\Actions\AdvanceStep; use He4rt\Onboarding\Actions\StartOnboarding; +use He4rt\Onboarding\Contracts\OnboardingCompletionGate; use He4rt\Onboarding\Enums\OnboardingStatus; use He4rt\Onboarding\Enums\OnboardingStepStatus; use He4rt\Onboarding\Enums\OnboardingType; @@ -109,3 +111,56 @@ expect($step->status)->toBe(OnboardingStepStatus::Pending); }); + +test('concluding git_challenge completes the Squads onboarding and opens the APTO gate', function (): void { + $user = User::factory()->create(); + Onboarding::factory()->for($user)->completed()->create(); + + ExternalIdentity::factory()->create([ + 'model_type' => $user->getMorphClass(), + 'model_id' => $user->id, + 'provider' => IdentityProvider::GitHub, + 'connected_at' => now(), + 'disconnected_at' => null, + ]); + + $onboarding = resolve(StartOnboarding::class)->handle($user, OnboardingType::Squads); + + resolve(AdvanceStep::class)->handle($onboarding, ['data' => ['terms' => true]]); + + resolve(AdvanceStep::class)->handle($onboarding, []); + + $onboarding->refresh(); + $step = $onboarding->steps()->where('step_key', 'git_challenge')->sole(); + + expect($step->status)->toBe(OnboardingStepStatus::Done) + ->and($step->completed_at)->not->toBeNull() + ->and($onboarding->status)->toBe(OnboardingStatus::Completed) + ->and($onboarding->completed_at)->not->toBeNull() + ->and(resolve(OnboardingCompletionGate::class)->isCompleted($user, OnboardingType::Squads)) + ->toBeTrue(); +}); + +test('the form step alone does not make the user APTO', function (): void { + $user = User::factory()->create(); + Onboarding::factory()->for($user)->completed()->create(); + + ExternalIdentity::factory()->create([ + 'model_type' => $user->getMorphClass(), + 'model_id' => $user->id, + 'provider' => IdentityProvider::GitHub, + 'connected_at' => now(), + 'disconnected_at' => null, + ]); + + $onboarding = resolve(StartOnboarding::class)->handle($user, OnboardingType::Squads); + + resolve(AdvanceStep::class)->handle($onboarding, ['data' => ['terms' => true]]); + + $onboarding->refresh(); + + expect($onboarding->status)->toBe(OnboardingStatus::InProgress) + ->and($onboarding->completed_at)->toBeNull() + ->and(resolve(OnboardingCompletionGate::class)->isCompleted($user, OnboardingType::Squads)) + ->toBeFalse(); +});