diff --git a/app/cdash/tests/CMakeLists.txt b/app/cdash/tests/CMakeLists.txt index a175237008..480e06a336 100644 --- a/app/cdash/tests/CMakeLists.txt +++ b/app/cdash/tests/CMakeLists.txt @@ -127,8 +127,6 @@ add_legacy_unit_test(/PHPUnitTest) add_legacy_unit_test(/CDash/ServiceContainer) -add_unit_test(/Unit/Console/Command/ValidateXmlCommandTest) - add_unit_test(/Unit/GraphQL/SchemaValidationTest) add_php_test(seconds_from_interval) @@ -253,15 +251,17 @@ add_feature_test_in_transaction(/Feature/Monitor) add_feature_test_in_transaction(/Feature/PasswordRotation) -add_feature_test_in_transaction(/Feature/PurgeUnusedProjectsCommand) +add_feature_test_in_transaction(/Feature/Console/PurgeUnusedProjectsCommandTest) add_feature_test_in_transaction(/Feature/Jobs/PruneJobsTest) add_feature_test_in_transaction(/Feature/Jobs/PruneAuthTokensTest) -add_feature_test_in_transaction(/Feature/UserCommand) +add_feature_test_in_transaction(/Feature/Console/SaveUserCommandTest) + +add_feature_test_in_transaction(/Feature/Console/RemoveUserCommandTest) -add_feature_test_in_transaction(/Feature/EmailTestCommandTest) +add_feature_test_in_transaction(/Feature/Console/EmailTestCommandTest) add_feature_test_in_transaction(/Feature/Jobs/NotifyExpiringAuthTokensTest) @@ -341,10 +341,14 @@ add_feature_test_in_transaction(/Feature/Services/SiteServiceTest) add_feature_test_in_transaction(/Feature/Middleware/AuthenticateTokenTest) -add_feature_test_in_transaction(/Feature/AutoRemoveBuildsCommand) +add_feature_test_in_transaction(/Feature/Console/AutoRemoveBuildsCommandTest) add_feature_test_in_transaction(/Feature/Console/QueueSubmissionsCommandTest) +add_feature_test_in_transaction(/Feature/Console/DeleteProjectCommandTest) + +add_feature_test_in_transaction(/Feature/Console/ValidateXmlCommandTest) + add_feature_test_in_transaction(/Feature/Submission/XmlSubmissionTest) add_feature_test_in_transaction(/Feature/Submission/UnparsedSubmissionTest) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d465221ea3..8fbee57a8b 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -21678,11 +21678,20 @@ parameters: count: 1 path: tests/BrowserTestCase.php + - + rawMessage: ''' + Call to deprecated method forceRootUrl() of class Illuminate\Routing\UrlGenerator: + Use useOrigin + ''' + identifier: staticMethod.deprecated + count: 4 + path: tests/Feature/CDashTest.php + - rawMessage: Access to an undefined property CDash\Model\Build::$Endime. identifier: property.notFound count: 2 - path: tests/Feature/AutoRemoveBuildsCommand.php + path: tests/Feature/Console/AutoRemoveBuildsCommandTest.php - rawMessage: ''' @@ -21691,7 +21700,7 @@ parameters: ''' identifier: method.deprecated count: 3 - path: tests/Feature/AutoRemoveBuildsCommand.php + path: tests/Feature/Console/AutoRemoveBuildsCommandTest.php - rawMessage: ''' @@ -21700,28 +21709,25 @@ parameters: ''' identifier: method.deprecated count: 1 - path: tests/Feature/AutoRemoveBuildsCommand.php + path: tests/Feature/Console/AutoRemoveBuildsCommandTest.php - rawMessage: 'Cannot call method fetchColumn() on PDOStatement|false.' identifier: method.nonObject count: 3 - path: tests/Feature/AutoRemoveBuildsCommand.php + path: tests/Feature/Console/AutoRemoveBuildsCommandTest.php - rawMessage: 'Parameter #1 $stmt of method CDash\Database::execute() expects PDOStatement, PDOStatement|false given.' identifier: argument.type count: 3 - path: tests/Feature/AutoRemoveBuildsCommand.php + path: tests/Feature/Console/AutoRemoveBuildsCommandTest.php - - rawMessage: ''' - Call to deprecated method forceRootUrl() of class Illuminate\Routing\UrlGenerator: - Use useOrigin - ''' - identifier: staticMethod.deprecated - count: 4 - path: tests/Feature/CDashTest.php + rawMessage: 'You should use assertSame() instead of assertEquals(), because both values are scalars of the same type' + identifier: phpunit.assertEquals + count: 2 + path: tests/Feature/Console/ValidateXmlCommandTest.php - rawMessage: 'Method Tests\Feature\GlobalInvitationAcceptanceTest::createInvitation() throws checked exception OverflowException but it''s missing from the PHPDoc @throws tag.' @@ -22116,18 +22122,6 @@ parameters: count: 1 path: tests/Feature/ProjectPermissions.php - - - rawMessage: Class Tests\Feature\PurgeUnusedProjectsCommand has an uninitialized property $project1. Give it default value or assign it in the constructor. - identifier: property.uninitialized - count: 1 - path: tests/Feature/PurgeUnusedProjectsCommand.php - - - - rawMessage: Class Tests\Feature\PurgeUnusedProjectsCommand has an uninitialized property $project2. Give it default value or assign it in the constructor. - identifier: property.uninitialized - count: 1 - path: tests/Feature/PurgeUnusedProjectsCommand.php - - rawMessage: ''' Call to deprecated method forceRootUrl() of class Illuminate\Routing\UrlGenerator: @@ -22146,36 +22140,6 @@ parameters: count: 1 path: tests/Feature/Services/ProjectServiceTest.php - - - rawMessage: Cannot access property $admin on App\Models\User|null. - identifier: property.nonObject - count: 1 - path: tests/Feature/UserCommand.php - - - - rawMessage: Cannot access property $institution on App\Models\User|null. - identifier: property.nonObject - count: 1 - path: tests/Feature/UserCommand.php - - - - rawMessage: Property Tests\Feature\UserCommand::$user has no type specified. - identifier: missingType.property - count: 1 - path: tests/Feature/UserCommand.php - - - - rawMessage: 'You should use assertSame() instead of assertEquals(), because both values are scalars of the same type' - identifier: phpunit.assertEquals - count: 5 - path: tests/Feature/UserCommand.php - - - - rawMessage: 'You should use assertSame() instead of assertEquals(), because both values are scalars of the same type' - identifier: phpunit.assertEquals - count: 2 - path: tests/Unit/Console/Command/ValidateXmlCommandTest.php - - rawMessage: 'Parameter #1 $objectOrClass of class ReflectionClass constructor expects class-string|T of object, array|string given.' identifier: argument.type diff --git a/tests/Feature/AutoRemoveBuildsCommand.php b/tests/Feature/Console/AutoRemoveBuildsCommandTest.php similarity index 96% rename from tests/Feature/AutoRemoveBuildsCommand.php rename to tests/Feature/Console/AutoRemoveBuildsCommandTest.php index 7ff1838404..91b57aa48b 100644 --- a/tests/Feature/AutoRemoveBuildsCommand.php +++ b/tests/Feature/Console/AutoRemoveBuildsCommandTest.php @@ -1,6 +1,6 @@ makePublicProject(); + + $this->expectOutputString("Deleting project: {$project->name}\n"); + Artisan::call('projects:clean'); + + self::assertModelMissing($project); + } + + public function testKeepsProjectsWithBuilds(): void + { + $project = $this->makePublicProject(); + $project->builds()->create([ + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + $this->expectOutputString(''); + Artisan::call('projects:clean'); + + self::assertModelExists($project); + } +} diff --git a/tests/Feature/Console/RemoveUserCommandTest.php b/tests/Feature/Console/RemoveUserCommandTest.php new file mode 100644 index 0000000000..d21400844f --- /dev/null +++ b/tests/Feature/Console/RemoveUserCommandTest.php @@ -0,0 +1,37 @@ + 'does-not-exist@example.com']); + + self::assertSame("User does-not-exist@example.com does not exist\n", Artisan::output()); + } + + public function testDeletesUser(): void + { + $user = User::factory()->create(); + + Artisan::call('user:remove', ['--email' => $user->email]); + + self::assertSame("Deleted user {$user->email}\n", Artisan::output()); + self::assertModelMissing($user); + } +} diff --git a/tests/Feature/Console/SaveUserCommandTest.php b/tests/Feature/Console/SaveUserCommandTest.php new file mode 100644 index 0000000000..df591853ec --- /dev/null +++ b/tests/Feature/Console/SaveUserCommandTest.php @@ -0,0 +1,110 @@ + 'Jane']); + + self::assertSame("You must specify the --email option\n", Artisan::output()); + } + + public function testCreatesUser(): void + { + $email = Str::uuid()->toString() . '@example.com'; + + Artisan::call('user:save', [ + '--email' => $email, + '--firstname' => 'Jane', + '--lastname' => 'Doe', + '--institution' => 'Kitware', + '--password' => 'correct horse battery staple', + ]); + + self::assertSame("Created $email\n", Artisan::output()); + $user = User::where('email', $email)->firstOrFail(); + self::assertSame([ + 'firstname' => 'Jane', + 'lastname' => 'Doe', + 'institution' => 'Kitware', + 'admin' => false, + ], $user->only(['firstname', 'lastname', 'institution', 'admin'])); + self::assertTrue(Hash::check('correct horse battery staple', $user->password)); + } + + /** + * @return array + */ + public static function requiredNewUserOptionCases(): array + { + return [ + 'firstname' => ['firstname'], + 'lastname' => ['lastname'], + 'institution' => ['institution'], + 'password' => ['password'], + ]; + } + + #[DataProvider('requiredNewUserOptionCases')] + public function testCreateRequiresOption(string $option): void + { + $email = Str::uuid()->toString() . '@example.com'; + $options = [ + '--email' => $email, + '--firstname' => 'Jane', + '--lastname' => 'Doe', + '--institution' => 'Kitware', + '--password' => 'correct horse battery staple', + ]; + unset($options["--$option"]); + + Artisan::call('user:save', $options); + + self::assertSame("You must specify the --$option option when creating a new user\n", Artisan::output()); + self::assertDatabaseMissing(User::class, ['email' => $email]); + } + + public function testUpdatesOnlyGivenOptions(): void + { + $user = User::factory()->create([ + 'firstname' => 'Jane', + 'lastname' => 'Doe', + 'institution' => 'Kitware', + ]); + $password = $user->password; + + Artisan::call('user:save', ['--email' => $user->email, '--institution' => 'CTest']); + + self::assertSame("Updated {$user->email}\n", Artisan::output()); + $user->refresh(); + self::assertSame([ + 'firstname' => 'Jane', + 'lastname' => 'Doe', + 'institution' => 'CTest', + 'admin' => false, + ], $user->only(['firstname', 'lastname', 'institution', 'admin'])); + self::assertSame($password, $user->password); + } + + public function testGrantsAdmin(): void + { + $user = User::factory()->create(); + + Artisan::call('user:save', ['--email' => $user->email, '--admin' => '1']); + + self::assertSame("Updated {$user->email}\n", Artisan::output()); + self::assertTrue($user->refresh()->admin); + } +} diff --git a/tests/Unit/Console/Command/ValidateXmlCommandTest.php b/tests/Feature/Console/ValidateXmlCommandTest.php similarity index 99% rename from tests/Unit/Console/Command/ValidateXmlCommandTest.php rename to tests/Feature/Console/ValidateXmlCommandTest.php index 62fc22dedd..0ec4581692 100644 --- a/tests/Unit/Console/Command/ValidateXmlCommandTest.php +++ b/tests/Feature/Console/ValidateXmlCommandTest.php @@ -1,6 +1,6 @@ project1 = $this->makePublicProject('DontRemoveProject'); - - // Make a build for the project. - $build = new Build(); - $build->Name = 'test'; - $build->ProjectId = $this->project1->id; - $build->SetStamp('20090223-0115-Experimental'); - $build->StartTime = '2009-02-23 01:15:00'; - $build->EndTime = '2009-02-23 01:15:00'; - $build->SubmitTime = '2009-02-23 01:15:00'; - $build->AddBuild(); - - // Make another project. - $this->project2 = $this->makePublicProject('RemoveProject'); - - // Run the command. - $this->artisan('projects:clean'); - - // Confirm that project 2 was deleted but project 1 was not - self::assertEquals(0, DB::select("SELECT COUNT(*) AS c FROM project WHERE name = 'RemoveProject'")[0]->c); - self::assertEquals(1, DB::select("SELECT COUNT(*) AS c FROM project WHERE name = 'DontRemoveProject'")[0]->c); - } - - public function tearDown(): void - { - if (isset($this->project1)) { - $this->project1->delete(); - } - if (isset($this->project2)) { - $this->project2->delete(); - } - - parent::tearDown(); - } -} diff --git a/tests/Feature/UserCommand.php b/tests/Feature/UserCommand.php deleted file mode 100644 index 16e87ed98a..0000000000 --- a/tests/Feature/UserCommand.php +++ /dev/null @@ -1,86 +0,0 @@ -user = User::where('email', $email)->first(); - if ($this->user !== null) { - $this::fail('User already exists'); - } - - // Cover errors for not providing enough info to create the user. - $params = ['--email' => $email]; - Artisan::call('user:save', $params); - $found = trim(Artisan::output()); - $this::assertEquals('You must specify the --firstname option when creating a new user', $found); - - $params['--firstname'] = 'UserCommand'; - Artisan::call('user:save', $params); - $found = trim(Artisan::output()); - $this::assertEquals('You must specify the --lastname option when creating a new user', $found); - - $params['--lastname'] = 'Tester'; - Artisan::call('user:save', $params); - $found = trim(Artisan::output()); - $this::assertEquals('You must specify the --institution option when creating a new user', $found); - - $params['--institution'] = 'CDash'; - Artisan::call('user:save', $params); - $found = trim(Artisan::output()); - $this::assertEquals('You must specify the --password option when creating a new user', $found); - - // Create the user and verify that it exists now. - $params['--password'] = 'UserCommandTester'; - Artisan::call('user:save', $params); - $this->user = User::where('email', $email)->first(); - if ($this->user === null) { - $this::fail('User was not created successfully'); - } - - // Update the user. - $params['--institution'] = 'CTest'; - $params['--admin'] = 1; - Artisan::call('user:save', $params); - - // Verify that changes were made successfully. - $this->user = User::where('email', $email)->first(); - $this::assertEquals('CTest', $this->user->institution); - $this::assertEquals(1, $this->user->admin); - - // Delete the user. - Artisan::call('user:remove', $params = ['--email' => $email]); - - // Verify that the user no longer exists. - $this->user = User::where('email', $email)->first(); - if ($this->user !== null) { - $this::fail('User still exists after remove'); - } - $this->user = null; - } - - public function tearDown(): void - { - if ($this->user) { - $this->user->delete(); - } - - parent::tearDown(); - } -}