diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5618bd1..7f25492 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Playground tests +name: 'Tests: Playground' on: push: @@ -26,16 +26,17 @@ jobs: with: php_extensions: xdebug coverage_clover: clover.xml + coverage_text: true - name: Make code coverage badge uses: timkrase/phpunit-coverage-badge@v1.2.1 with: coverage_badge_path: output/coverage.svg push_badge: false - - name: Git push to image-data branch + - name: Git push to testing/develop branch uses: peaceiris/actions-gh-pages@v3 with: publish_dir: ./output - publish_branch: image-data + publish_branch: testing/develop github_token: ${{ secrets.GITHUB_TOKEN }} user_name: 'github-actions[bot]' user_email: 'github-actions[bot]@users.noreply.github.com' diff --git a/.gitignore b/.gitignore index 3e1645c..3b5218b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,10 +27,10 @@ node_modules tmp* temp* vendor -*.derp ################################################################################ # Test and Docs ignores ################################################################################ +output coverage phpunit.xml phpstan.neon diff --git a/README.md b/README.md index 80f21e8..71456e7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Playground -![Test Coverage](https://raw.githubusercontent.com/gammamatrix/playground/image-data/coverage.svg) -![Playground CI Workflow](https://github.com/gammamatrix/playground/actions/workflows/ci.yml/badge.svg?) +[![Playground CI Workflow](https://github.com/gammamatrix/playground/actions/workflows/ci.yml/badge.svg?branch=develop)](https://raw.githubusercontent.com/gammamatrix/playground/testing/develop/testdox.txt) +[![Test Coverage](https://raw.githubusercontent.com/gammamatrix/playground/testing/develop/coverage.svg)](tests) This is the base package for Playground. diff --git a/config/playground.php b/config/playground.php index 38d691c..43665d1 100644 --- a/config/playground.php +++ b/config/playground.php @@ -17,12 +17,18 @@ */ 'verify' => (string) env('PLAYGROUND_AUTH_VERIFY', 'privileges'), // 'verify' => (string) env('PLAYGROUND_AUTH_VERIFY', 'roles'), + 'token' => [ + 'name' => 'app', + ], 'hasPrivilege' => (bool) env('PLAYGROUND_AUTH_HAS_PRIVILEGE', false), 'userPrivileges' => (bool) env('PLAYGROUND_AUTH_USER_PRIVILEGES', false), 'hasRole' => (bool) env('PLAYGROUND_AUTH_HAS_ROLE', false), 'userRole' => (bool) env('PLAYGROUND_AUTH_USER_ROLE', false), 'userRoles' => (bool) env('PLAYGROUND_AUTH_USER_ROLES', false), ], + 'date' => [ + 'sql' => (bool) env('PLAYGROUND_DATE_MYSQL', 'Y-m-d H:i:s'), + ], 'load' => [ 'routes' => (bool) env('PLAYGROUND_LOAD_ROUTES', false), 'views' => (bool) env('PLAYGROUND_LOAD_VIEWS', false), diff --git a/phpunit.xml.dev b/phpunit.xml.dev new file mode 100644 index 0000000..9ab183b --- /dev/null +++ b/phpunit.xml.dev @@ -0,0 +1,76 @@ + + + + + tests/Unit + + + tests/Feature + + + + + + + + + + + + + + + + + + + + + + src + + + + + + + + + + + + + + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 535c145..6121002 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,28 +1,28 @@ - + tests/Unit @@ -31,27 +31,27 @@ tests/Feature - - - - - - - + + + + + + + + + + + + + + + + + src @@ -63,9 +63,9 @@ - + diff --git a/resources/views/components/modal.blade.php b/resources/views/components/modal.blade.php deleted file mode 100644 index 8e8fc5f..0000000 --- a/resources/views/components/modal.blade.php +++ /dev/null @@ -1,39 +0,0 @@ - - -@push('body') - -@endpush diff --git a/resources/views/components/snippets.blade.php b/resources/views/components/snippets.blade.php deleted file mode 100644 index 952084b..0000000 --- a/resources/views/components/snippets.blade.php +++ /dev/null @@ -1,58 +0,0 @@ - __METHOD__, -// '__FILE__' => __FILE__, -// '__LINE__' => __LINE__, -// '$snippets' => $snippets, -// ]); -?> -@foreach ($snippets as $snippet) -@continue(empty($snippet['title']) && empty($snippet['content'])) - -@push($stack) -
- -
- -@if (!empty($snippet['title'])) - ', $h) ?>{{$snippet['title']}}', $h) ?> -@endif - -@if (!empty($snippet['content'])) -
- -{!! $snippet['content'] !!} - -
-@endif - -
- -
-@endpush -@endforeach diff --git a/resources/views/components/table/data-row.blade.php b/resources/views/components/table/data-row.blade.php index 3a9857e..cedb2ee 100644 --- a/resources/views/components/table/data-row.blade.php +++ b/resources/views/components/table/data-row.blade.php @@ -119,17 +119,17 @@ } } // dump([ - // '__METHOD__' => __METHOD__, - // '__FILE__' => __FILE__, - // '__LINE__' => __LINE__, - // '$value' => $value, - // '$column' => $column, - // '$columnMeta' => $columnMeta, - // '$hasLink' => $hasLink, - // '$link' => $link, - // '$record' => $record, - // '$datum' => $datum, - // '$fkModel' => $fkModel ? $fkModel->toArray() : $fkModel, +// '__METHOD__' => __METHOD__, +// '__FILE__' => __FILE__, +// '__LINE__' => __LINE__, +// '$value' => $value, +// '$column' => $column, +// '$columnMeta' => $columnMeta, +// '$hasLink' => $hasLink, +// '$link' => $link, +// '$record' => $record, +// '$datum' => $datum, +// '$fkModel' => $fkModel ? $fkModel->toArray() : $fkModel, // ]); } elseif ($columnMeta['showSpec']) { // Get the label of the type. diff --git a/src/Filters/ModelTrait.php b/src/Filters/ModelTrait.php index 5763bae..63c9a2a 100644 --- a/src/Filters/ModelTrait.php +++ b/src/Filters/ModelTrait.php @@ -15,6 +15,9 @@ * * GammaMatrix filter handler * + * It is expected that the data may not have the proper type from forms. + * These filters correct those values. Parameters must not be cast. + * Allow the method to decide what to return to prevent model cast errors. */ trait ModelTrait { @@ -25,7 +28,7 @@ trait ModelTrait * * @return array Returns an array. */ - public function filterArray($value) + public function filterArray($value): array { if (is_array($value)) { return $value; @@ -45,7 +48,7 @@ public function filterArray($value) * * @return string Returns an array converted to JSON. */ - public function filterArrayToJson($value) + public function filterArrayToJson($value): ?string { if (is_array($value)) { return json_encode($value); @@ -61,20 +64,18 @@ public function filterArrayToJson($value) * * @param integer $value The value to filter. * @param integer $exponent The maximum power of the exponent to sum. - * - * @return integer */ - public function filterBits($value, $exponent = 0) + public function filterBits($value, $exponent = 0): int { $exponent = intval(abs($exponent)); /** - * @var integer $pBits The allowed permission bits: rwx + * @var integer $pBits The summed bit power values. */ $pBits = 0; // $pBits = 4 + 2 + 1; - for ($i=0; $i <= $exponent; $i++) { + for ($i = 0; $i <= $exponent; $i++) { $pBits += pow(2, $i); } @@ -85,10 +86,8 @@ public function filterBits($value, $exponent = 0) * Filter a boolean value * * @param string $value The value to filter. - * - * @return boolean */ - public function filterBoolean($value) + public function filterBoolean($value): bool { if (is_string($value) && !is_numeric($value)) { return 'true' === strtolower($value); @@ -100,18 +99,19 @@ public function filterBoolean($value) } /** - * Filter a date value as a MySQL UTC string. + * Filter a date value as an SQL UTC string. * * @param string $value The date to filter. * @param string $locale i18n - * - * @return string|NULL */ - public function filterDate($value, $locale = 'en-US') + public function filterDate($value, $locale = 'en-US'): ?string { return is_string($value) && !empty($value) - ? gmdate(DATE_MYSQL, strtotime($value)) : null + ? gmdate( + config('playground.date.sql', 'Y-m-d H:i:s'), + strtotime($value) + ) : null ; } @@ -120,10 +120,8 @@ public function filterDate($value, $locale = 'en-US') * * @param string $value The date to filter. * @param string $locale i18n - * - * @return string|NULL */ - public function filterDateAsCarbon($value, $locale = 'en-US') + public function filterDateAsCarbon($value, $locale = 'en-US'): ?Carbon { if (empty($value)) { return null; @@ -136,10 +134,8 @@ public function filterDateAsCarbon($value, $locale = 'en-US') * Filter an email address. * * @param string $email The address to filter. - * - * @return string */ - public function filterEmail($email) + public function filterEmail($email): string { return filter_var($email, FILTER_SANITIZE_EMAIL); } @@ -147,20 +143,19 @@ public function filterEmail($email) /** * Filter a float value * - * NOTE: Implement handling for locales. - * * @param string $value The value to filter. * @param string $locale i18n - * - * @return float|NULL */ - public function filterFloat($value, $locale = 'en-US') + public function filterFloat($value, $locale = 'en-US'): ?float { if ('' === $value || null === $value) { return null; } - return (new \NumberFormatter($locale, \NumberFormatter::DECIMAL))->parse($value); + return (new \NumberFormatter( + $locale, + \NumberFormatter::DECIMAL + ))->parse($value); } /** @@ -169,12 +164,14 @@ public function filterFloat($value, $locale = 'en-US') * FILTER_FLAG_NO_ENCODE_QUOTES - do not encode quotes. * * @param string $content The string to filter. - * - * @return string */ - public function filterHtml($content) + public function filterHtml($content): string { - return filter_var($content, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); + return filter_var( + $content, + FILTER_SANITIZE_STRING, + FILTER_FLAG_NO_ENCODE_QUOTES + ); } /** @@ -182,42 +179,41 @@ public function filterHtml($content) * * @param string $value The value to filter. * @param string $locale i18n - * - * @return integer */ - public function filterInteger($value, $locale = 'en-US') + public function filterInteger($value, $locale = 'en-US'): int { if ('' === $value || null === $value) { return 0; } - $value = (new \NumberFormatter($locale, \NumberFormatter::DECIMAL))->parse($value, \NumberFormatter::TYPE_INT64); + $value = (new \NumberFormatter( + $locale, + \NumberFormatter::DECIMAL + ))->parse($value, \NumberFormatter::TYPE_INT64); - return is_numeric($value) ? $value : 0; + return is_int($value) ? $value : 0; } /** * Filter an integer value ID. * * @param string $value The value to filter. - * - * @return integer|null */ - public function filterIntegerId($value) + public function filterIntegerId($value): ?int { return is_numeric($value) && ($value > 0) ? (int) $value : null; } /** - * Filter a positive integer value or return null. + * Filter a positive integer value or return zero. * * @param string $value The value to filter. - * - * @return integer|null + * @param bool $absolute Use `abs()` on the value to convert negative to positive. */ - public function filterIntegerPositive($value) + public function filterIntegerPositive($value, $absolute = true): int { - return is_numeric($value) && ($value > 0) ? (int) $value : null; + $value = intval($value); + return $absolute && ($value < 0) ? (int) abs($value) : $value; } /** @@ -230,7 +226,7 @@ public function filterIntegerPositive($value) * * @return float */ - public function filterPercent($value, $locale = 'en-US') + public function filterPercent($value, $locale = 'en-US'): ?float { if ('' === $value || null === $value) { return null; @@ -239,31 +235,9 @@ public function filterPercent($value, $locale = 'en-US') return $this->filterFloat(str_replace('%', '', $value), $locale); } - /** - * Filter a phone number. - * - * @param string $value The value to filter. - * @param string $locale i18n - * - * @return string - */ - public function filterPhone($value, $locale = 'en-US') - { - if (empty($value)) { - return ''; - } - - return filter_var( - str_replace(['-', '.', '+'], '', $value), - FILTER_SANITIZE_NUMBER_INT - ); - } - /** * Filter the status * - * TODO Determine if $input should be passed by reference: &$input - * * @param array $input The status input. * * @return integer|NULL @@ -288,25 +262,6 @@ public function filterStatus(array $input = []) return $input; } - /** - * Filter common fields - * - * @param array $input The common fields: locale, style, klass, icon, avatar, image - * - * @return integer|NULL - */ - public function filterCommonFields(array $input = []) - { - $input['locale'] = isset($input['locale']) ? $this->filterHtml($input['locale']) : ''; - $input['style'] = isset($input['style']) ? $this->filterHtml($input['style']) : ''; - $input['klass'] = isset($input['klass']) ? $this->filterHtml($input['klass']) : ''; - $input['icon'] = isset($input['icon']) ? $this->filterHtml($input['icon']) : ''; - $input['avatar'] = isset($input['avatar']) ? $this->filterHtml($input['avatar']) : ''; - $input['image'] = isset($input['image']) ? $this->filterHtml($input['image']) : ''; - - return $input; - } - /** * Filter system fields * @@ -347,7 +302,6 @@ public function filterSystemFields(array $input = []) } return $input; - return $this->filterStatus($input); } /** diff --git a/src/Http/Controllers/IndexController.php b/src/Http/Controllers/IndexController.php index 9e4d9ec..a3488c8 100644 --- a/src/Http/Controllers/IndexController.php +++ b/src/Http/Controllers/IndexController.php @@ -130,7 +130,7 @@ public function sitemap(Request $request): JsonResponse|RedirectResponse|View $configs = []; foreach ($package_config['packages'] as $package) { - if (is_string($package) && ! empty($package) && ! array_key_exists($package, $configs)) { + if (is_string($package) && !empty($package) && !array_key_exists($package, $configs)) { $configs[$package] = config($package); } } @@ -138,11 +138,11 @@ public function sitemap(Request $request): JsonResponse|RedirectResponse|View $sitemaps = []; foreach ($configs as $package => $config) { - if (! empty($config['sitemap']) + if (!empty($config['sitemap']) && is_array($config['sitemap']) - && ! empty($config['sitemap']['enable']) + && !empty($config['sitemap']['enable']) && is_array($config['load']) - && ! empty($config['load']['views']) + && !empty($config['load']['views']) ) { $sitemaps[$package] = $config['sitemap']; } diff --git a/src/Models/Traits/ScopeFilterDates.php b/src/Models/Traits/ScopeFilterDates.php index 31a7d23..94d069b 100644 --- a/src/Models/Traits/ScopeFilterDates.php +++ b/src/Models/Traits/ScopeFilterDates.php @@ -127,12 +127,12 @@ public static function scopeFilterDates( $filter_operator = '>='; } // dump([ - // '__METHOD__' => __METHOD__, - // '__LINE__' => __LINE__, - // '$filter_operator' => $filter_operator, - // '$filter_value' => $filter_value, - // '$filter_parse' => $filter_parse, - // '$validated[filter][$column]' => $validated['filter'][$column], + // '__METHOD__' => __METHOD__, + // '__LINE__' => __LINE__, + // '$filter_operator' => $filter_operator, + // '$filter_value' => $filter_value, + // '$filter_parse' => $filter_parse, + // '$validated[filter][$column]' => $validated['filter'][$column], // ]); } elseif (is_string($validated['filter'][$column]) && !empty($validated['filter'][$column]) diff --git a/src/Policies/PolicyTrait.php b/src/Policies/PolicyTrait.php index f32c235..35cee1a 100644 --- a/src/Policies/PolicyTrait.php +++ b/src/Policies/PolicyTrait.php @@ -63,6 +63,12 @@ public function setToken(?object $token = null): self public function verify(Authenticatable $user, string $ability): bool|Response { $verify = config('playground.auth.verify'); + // dd([ + // '__METHOD__' => __METHOD__, + // '$verify' => $verify, + // '$ability' => $ability, + // '$user' => $user, + // ]); if ('privileges' === $verify) { return $this->hasPrivilege($user, $this->privilege($ability)); } elseif ('roles' === $verify) { diff --git a/src/Policies/PrivilegeTrait.php b/src/Policies/PrivilegeTrait.php index 71b0ce7..5fee975 100644 --- a/src/Policies/PrivilegeTrait.php +++ b/src/Policies/PrivilegeTrait.php @@ -20,6 +20,7 @@ abstract public function getPackage(): string; abstract public function getEntity(): string; abstract public function getToken(): ?object; abstract public function hasToken(): bool; + abstract public function setToken(?object $token = null): self; public function privilege(string $ability = '*'): string { @@ -69,7 +70,7 @@ public function hasPrivilege(Authenticatable $user, string $privilege): bool|Res if (class_implements($user, \Laravel\Sanctum\Contracts\HasApiTokens::class)) { if (!$this->hasToken()) { $token = $user->tokens() - ->where('name', config('playground-auth.token.name')) + ->where('name', config('playground.auth.token.name')) // Get the latest created token. ->orderBy('created_at', 'desc') ->first() diff --git a/src/Policies/RoleTrait.php b/src/Policies/RoleTrait.php index e8fc175..e3f5d6a 100644 --- a/src/Policies/RoleTrait.php +++ b/src/Policies/RoleTrait.php @@ -124,7 +124,7 @@ public function hasRole(Authenticatable $user, string $ability): bool|Response if (config('playground.auth.userRole')) { // Check for any role. foreach ($roles as $role) { - if (!empty($user->role) && $role == $user->role) { + if (!empty($user->role) && $role === $user->role) { return true; } } @@ -143,6 +143,16 @@ public function hasRole(Authenticatable $user, string $ability): bool|Response } } + // dd([ + // '__METHOD__' => __METHOD__, + // '__METHOD__' => __METHOD__, + // '$user' => $user, + // 'userRole' => config('playground.auth.userRole'), + // 'userRoles' => config('playground.auth.userRoles'), + // 'hasRole' => config('playground.auth.hasRole') && method_exists($user, 'hasRole'), + // '$ability' => $ability, + // '$roles' => $roles, + // ]); return Response::denyWithStatus(401, __('playground::auth.unauthorized')); } } diff --git a/src/View/Components/Modal.php b/src/View/Components/Modal.php deleted file mode 100644 index 475bc3c..0000000 --- a/src/View/Components/Modal.php +++ /dev/null @@ -1,59 +0,0 @@ -id = is_string($id) ? $id : ''; - $this->bc = is_string($bc) ? $bc : ''; - $this->wc = is_bool($wc) ? $wc : true; - } - - /** - * Get the view / contents that represent the component. - * - * @return \Illuminate\Contracts\View\View|\Closure|string - */ - public function render() - { - return view(sprintf( - '%1$scomponents.modal', - config('playground.view') - )); - } -} diff --git a/src/View/Components/ModelFlag.php b/src/View/Components/ModelFlag.php index 84fd294..6d987a3 100644 --- a/src/View/Components/ModelFlag.php +++ b/src/View/Components/ModelFlag.php @@ -18,26 +18,7 @@ class ModelFlag extends Component { public function __construct( public array $columnMeta = [], - public mixed $value = null, - // public string $id = 'table-component', - // public string $icon = '', - // public string $badge = 'badge badge-pill badge-success', - // public bool $showPagination = true, - // public bool $showLinks = true, - // public bool $modelActions = false, - // public bool $trashable = true, - // public string $routeEdit = '', - // public string $routeRestore = '', - // public string $routeDelete = '', - // public string $routeDeleteRelationship = '', - // public string $routeDeleteRelationshipId = '', - // public bool $collapsible = true, - // public string $returnUrl = '', - // public ?array $filters = null, - // public ?array $validated = null, - // public ?array $sort = null, - // public ?array $styling = null, - // public string $class = '', + public mixed $value = null ) { } diff --git a/src/View/Components/Snippets.php b/src/View/Components/Snippets.php deleted file mode 100644 index b038c99..0000000 --- a/src/View/Components/Snippets.php +++ /dev/null @@ -1,43 +0,0 @@ -snippets = is_array($snippets) ? $snippets : []; - } - - /** - * Get the view / contents that represent the component. - * - * @return \Illuminate\Contracts\View\View|\Closure|string - */ - public function render() - { - return view(sprintf( - '%1$scomponents.snippets', - config('playground.view') - ), [ - 'snippets' => $this->snippets, - ]); - } -} diff --git a/src/View/Components/Table.php b/src/View/Components/Table.php deleted file mode 100644 index 8ab9ef5..0000000 --- a/src/View/Components/Table.php +++ /dev/null @@ -1,50 +0,0 @@ -get('/about'); + $response = $this->get(route('about')); $response->assertStatus(200); } public function test_route_json_about_as_guest_and_succeed() { - $response = $this->json('GET', '/about'); + $response = $this->json('GET', route('about')); $response->assertStatus(200); } - // public function test_route_about_as_client_and_succeed() - // { - // $this->initAuthRoles(); - // $response = $this->as('client')->get('/about'); - // $response->assertStatus(200); - // } + public function test_route_about_as_client_and_succeed() + { + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'client'; + $response = $this->actingAs($user)->get(route('about')); + $response->assertStatus(200); + } - // public function test_route_json_about_as_manager_and_succeed() - // { - // $this->initAuthRoles(); - // $response = $this->as('manager')->getJson('/about'); - // $response->assertStatus(200); - // } + public function test_route_json_about_as_manager_and_succeed() + { + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'manager'; + $response = $this->actingAs($user)->getJson(route('about')); + $response->assertStatus(200); + } } diff --git a/tests/Feature/Http/Controllers/Index/BootstrapRouteTest.php b/tests/Feature/Http/Controllers/Index/BootstrapRouteTest.php index 61e44b5..4c24279 100644 --- a/tests/Feature/Http/Controllers/Index/BootstrapRouteTest.php +++ b/tests/Feature/Http/Controllers/Index/BootstrapRouteTest.php @@ -6,6 +6,8 @@ namespace Tests\Feature\GammaMatrix\Playground\Http\Controllers\Index; +use GammaMatrix\Playground\Test\Models\User; +use GammaMatrix\Playground\Test\Models\UserWithRole; use Tests\Feature\GammaMatrix\Playground\TestCase; /** @@ -30,27 +32,29 @@ protected function setUp(): void public function test_route_bootstrap_as_guest_and_succeed() { - $response = $this->get('/bootstrap'); + $response = $this->get(route('bootstrap')); $response->assertStatus(200); } public function test_route_json_bootstrap_as_guest_and_succeed() { - $response = $this->json('GET', '/bootstrap'); + $response = $this->json('GET', route('bootstrap')); $response->assertStatus(200); } - // public function test_route_bootstrap_as_vendor_and_succeed() - // { - // $this->initAuthRoles(); - // $response = $this->as('vendor')->get('/bootstrap'); - // $response->assertStatus(200); - // } + public function test_route_bootstrap_as_vendor_and_succeed() + { + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'vendor'; + $response = $this->actingAs($user)->get(route('bootstrap')); + $response->assertStatus(200); + } - // public function test_route_json_bootstrap_as_wheel_and_succeed() - // { - // $this->initAuthRoles(); - // $response = $this->as('wheel')->getJson('/bootstrap'); - // $response->assertStatus(200); - // } + public function test_route_json_bootstrap_as_wheel_and_succeed() + { + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'wheel'; + $response = $this->actingAs($user)->getJson(route('bootstrap')); + $response->assertStatus(200); + } } diff --git a/tests/Feature/Http/Controllers/Index/DashboardRouteTest.php b/tests/Feature/Http/Controllers/Index/DashboardRouteTest.php index 3d2b124..74855c3 100644 --- a/tests/Feature/Http/Controllers/Index/DashboardRouteTest.php +++ b/tests/Feature/Http/Controllers/Index/DashboardRouteTest.php @@ -6,6 +6,8 @@ namespace Tests\Feature\GammaMatrix\Playground\Http\Controllers\Index; +use GammaMatrix\Playground\Test\Models\User; +use GammaMatrix\Playground\Test\Models\UserWithRole; use Tests\Feature\GammaMatrix\Playground\TestCase; /** @@ -29,7 +31,7 @@ public function test_route_dashboard_as_guest_and_redirect_when_disabled_for_all config([ 'playground.dashboard.enable' => false, ]); - $response = $this->get('/dashboard'); + $response = $this->get(route('dashboard')); $response->assertRedirect('/'); } @@ -39,7 +41,7 @@ public function test_route_dashboard_as_guest_and_redirect_when_disabled_for_gue 'playground.dashboard.enable' => true, 'playground.dashboard.guest' => false, ]); - $response = $this->get('/dashboard'); + $response = $this->get(route('dashboard')); $response->assertRedirect('/'); } @@ -49,48 +51,49 @@ public function test_route_json_dashboard_as_guest_and_succeed() 'playground.dashboard.enable' => true, 'playground.dashboard.guest' => true, ]); - $response = $this->json('GET', '/dashboard'); + $response = $this->json('GET', route('dashboard')); $response->assertStatus(200); } - // public function test_route_dashboard_as_user_and_succeed() - // { - // config([ - // 'playground.dashboard.enable' => true, - // 'playground.dashboard.guest' => false, - // ]); - // $this->initAuthRoles(); - // $response = $this->as('user')->get('/dashboard'); - // $response->assertStatus(200); - // } + public function test_route_dashboard_as_user_and_succeed() + { + config([ + 'playground.dashboard.enable' => true, + 'playground.dashboard.guest' => false, + ]); + $user = User::factory()->create(); + $response = $this->actingAs($user)->get(route('dashboard')); + $response->assertStatus(200); + } - // public function test_route_dashboard_as_user_and_fail_when_disabled_for_all() - // { - // config([ - // 'playground.dashboard.enable' => false, - // ]); - // $this->initAuthRoles(); - // $response = $this->as('user')->get('/dashboard'); - // $response->assertRedirect('/'); - // } + public function test_route_dashboard_as_user_and_fail_when_disabled_for_all() + { + config([ + 'playground.dashboard.enable' => false, + ]); + $user = User::factory()->create(); + $response = $this->actingAs($user)->get(route('dashboard')); + $response->assertRedirect('/'); + } - // public function test_route_dashboard_as_user_and_fail_when_disabled_for_all_and_no_redirect() - // { - // config([ - // 'playground.dashboard.enable' => false, - // ]); - // $this->initAuthRoles(); - // $response = $this->get('/dashboard?noredirect'); - // $response->assertStatus(400); - // } + public function test_route_dashboard_as_user_and_fail_when_disabled_for_all_and_no_redirect() + { + config([ + 'playground.dashboard.enable' => false, + ]); + $user = User::factory()->create(); + $response = $this->actingAs($user)->get(route('dashboard', ['noredirect' => 1])); + $response->assertStatus(400); + } - // public function test_route_json_dashboard_as_admin_and_succeed() - // { - // config([ - // 'playground.dashboard.enable' => true, - // ]); - // $this->initAuthRoles(); - // $response = $this->as('admin')->getJson('/dashboard'); - // $response->assertStatus(200); - // } + public function test_route_json_dashboard_as_admin_and_succeed() + { + config([ + 'playground.dashboard.enable' => true, + ]); + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'admin'; + $response = $this->actingAs($user)->getJson(route('dashboard')); + $response->assertStatus(200); + } } diff --git a/tests/Feature/Http/Controllers/Index/HomeRouteTest.php b/tests/Feature/Http/Controllers/Index/HomeRouteTest.php index da05660..64e2090 100644 --- a/tests/Feature/Http/Controllers/Index/HomeRouteTest.php +++ b/tests/Feature/Http/Controllers/Index/HomeRouteTest.php @@ -6,6 +6,8 @@ namespace Tests\Feature\GammaMatrix\Playground\Http\Controllers\Index; +use GammaMatrix\Playground\Test\Models\User; +use GammaMatrix\Playground\Test\Models\UserWithRole; use Tests\Feature\GammaMatrix\Playground\TestCase; /** @@ -16,27 +18,29 @@ class HomeRouteTest extends TestCase { public function test_route_home_as_guest_and_succeed() { - $response = $this->get('/'); + $response = $this->get(route('home')); $response->assertStatus(200); } public function test_route_json_home_as_guest_and_succeed() { - $response = $this->json('GET', '/'); + $response = $this->json('GET', route('home')); $response->assertStatus(200); } - // public function test_route_home_as_user_admin_and_succeed() - // { - // $this->initAuthRoles(); - // $response = $this->as('user-admin')->get('/'); - // $response->assertStatus(200); - // } + public function test_route_home_as_user_admin_and_succeed() + { + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'user-admin'; + $response = $this->actingAs($user)->get(route('home')); + $response->assertStatus(200); + } - // public function test_route_json_home_as_admin_and_succeed() - // { - // $this->initAuthRoles(); - // $response = $this->as('root')->getJson('/'); - // $response->assertStatus(200); - // } + public function test_route_json_home_as_admin_and_succeed() + { + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'root'; + $response = $this->actingAs($user)->getJson(route('home')); + $response->assertStatus(200); + } } diff --git a/tests/Feature/Http/Controllers/Index/SitemapRouteTest.php b/tests/Feature/Http/Controllers/Index/SitemapRouteTest.php index 5be75c6..3c7190c 100644 --- a/tests/Feature/Http/Controllers/Index/SitemapRouteTest.php +++ b/tests/Feature/Http/Controllers/Index/SitemapRouteTest.php @@ -6,6 +6,8 @@ namespace Tests\Feature\GammaMatrix\Playground\Http\Controllers\Index; +use GammaMatrix\Playground\Test\Models\User; +use GammaMatrix\Playground\Test\Models\UserWithRole; use Tests\Feature\GammaMatrix\Playground\TestCase; /** @@ -29,7 +31,7 @@ public function test_route_sitemap_as_guest_and_redirect_when_disabled_for_all() config([ 'playground.sitemap.enable' => false, ]); - $response = $this->get('/sitemap'); + $response = $this->get(route('sitemap')); $response->assertRedirect('/'); } @@ -39,7 +41,7 @@ public function test_route_sitemap_as_guest_and_redirect_when_disabled_for_guest 'playground.sitemap.enable' => true, 'playground.sitemap.guest' => false, ]); - $response = $this->get('/sitemap'); + $response = $this->get(route('sitemap')); $response->assertRedirect('/'); } @@ -49,48 +51,63 @@ public function test_route_json_sitemap_as_guest_and_succeed() 'playground.sitemap.enable' => true, 'playground.sitemap.guest' => true, ]); - $response = $this->json('GET', '/sitemap'); + $response = $this->json('GET', route('sitemap')); $response->assertStatus(200); } - // public function test_route_sitemap_as_user_and_succeed() - // { - // config([ - // 'playground.sitemap.enable' => true, - // 'playground.sitemap.guest' => false, - // ]); - // $this->initAuthRoles(); - // $response = $this->as('user')->get('/sitemap'); - // $response->assertStatus(200); - // } + public function test_route_sitemap_as_user_and_succeed() + { + config([ + 'playground.sitemap.enable' => true, + 'playground.sitemap.guest' => false, + ]); + $user = User::factory()->create(); + $response = $this->actingAs($user)->get(route('sitemap')); + $response->assertStatus(200); + } - // public function test_route_sitemap_as_support_and_fail_when_disabled_for_all() - // { - // config([ - // 'playground.sitemap.enable' => false, - // ]); - // $this->initAuthRoles(); - // $response = $this->as('support')->get('/sitemap'); - // $response->assertRedirect('/'); - // } + public function test_route_sitemap_as_support_and_fail_when_disabled_for_all() + { + config([ + 'playground.sitemap.enable' => false, + ]); + $user = User::factory()->create(); + $response = $this->actingAs($user)->get(route('sitemap')); + $response->assertRedirect('/'); + } - // public function test_route_sitemap_as_user_and_fail_when_disabled_for_all_and_no_redirect() - // { - // config([ - // 'playground.sitemap.enable' => false, - // ]); - // $this->initAuthRoles(); - // $response = $this->get('/sitemap?noredirect'); - // $response->assertStatus(400); - // } + public function test_route_sitemap_as_user_and_fail_when_disabled_for_all_and_no_redirect() + { + config([ + 'playground.sitemap.enable' => false, + ]); + $user = User::factory()->create(); + $response = $this->actingAs($user)->get(route('sitemap', ['noredirect' => 1])); + $response->assertStatus(400); + } + + public function test_route_json_sitemap_as_admin_and_succeed() + { + config([ + 'playground.sitemap.enable' => true, + ]); + $user = UserWithRole::find(User::factory()->create()->id); + // The role is not saved since the column may not exist. + $user->role = 'admin'; + $response = $this->actingAs($user)->getJson(route('sitemap')); + $response->assertStatus(200); + } - // public function test_route_json_sitemap_as_support_admin_and_succeed() - // { - // config([ - // 'playground.sitemap.enable' => true, - // ]); - // $this->initAuthRoles(); - // $response = $this->as('support-admin')->getJson('/sitemap'); - // $response->assertStatus(200); - // } + public function test_route_sitemap_as_user_and_succeed_with_package_sitemaps() + { + config([ + 'playground.load.views' => true, + 'playground.sitemap.enable' => true, + 'playground.sitemap.guest' => false, + 'playground.sitemap.packages' => 'playground-auth', + ]); + $user = User::factory()->create(); + $response = $this->actingAs($user)->get(route('sitemap')); + $response->assertStatus(200); + } } diff --git a/tests/Feature/Http/Controllers/Index/ThemeRouteTest.php b/tests/Feature/Http/Controllers/Index/ThemeRouteTest.php index 2ffa6eb..233e3ff 100644 --- a/tests/Feature/Http/Controllers/Index/ThemeRouteTest.php +++ b/tests/Feature/Http/Controllers/Index/ThemeRouteTest.php @@ -6,6 +6,8 @@ namespace Tests\Feature\GammaMatrix\Playground\Http\Controllers\Index; +use GammaMatrix\Playground\Test\Models\User; +use GammaMatrix\Playground\Test\Models\UserWithRole; use Tests\Feature\GammaMatrix\Playground\TestCase; /** @@ -16,27 +18,34 @@ class ThemeRouteTest extends TestCase { public function test_route_theme_as_guest_and_succeed() { - $response = $this->get('/theme'); + $response = $this->get(route('theme')); $response->assertRedirect('/'); } public function test_route_theme_as_guest_with_preview_and_succeed() { - $response = $this->json('GET', '/theme?preview'); + $response = $this->json('GET', route('theme', ['preview' => true])); $response->assertStatus(200); } - // public function test_route_theme_as_partner_and_succeed() - // { - // $this->initAuthRoles(); - // $response = $this->as('partner')->get('/theme?appTheme=bootstrap'); - // $response->assertRedirect('/'); - // } + public function test_route_theme_as_partner_and_succeed() + { + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'partner'; + $response = $this->actingAs($user)->get(route('theme', [ + 'appTheme' => 'dark', + ])); + $response->assertRedirect('/'); + } - // public function test_route_theme_as_sales_and_succeed_with_theme_and_redirect() - // { - // $this->initAuthRoles(); - // $response = $this->as('sales')->getJson('/theme?appTheme=bootstrap-dark&_return_url=%2Fsitemap'); - // $response->assertRedirect('/sitemap'); - // } + public function test_route_theme_as_sales_and_succeed_with_theme_and_redirect() + { + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'sales'; + $response = $this->actingAs($user)->get(route('theme', [ + 'appTheme' => 'dark', + '_return_url' => route('sitemap'), + ])); + $response->assertRedirect(route('sitemap')); + } } diff --git a/tests/Feature/Http/Controllers/Index/WelcomeRouteTest.php b/tests/Feature/Http/Controllers/Index/WelcomeRouteTest.php index 484cb54..24102b1 100644 --- a/tests/Feature/Http/Controllers/Index/WelcomeRouteTest.php +++ b/tests/Feature/Http/Controllers/Index/WelcomeRouteTest.php @@ -6,6 +6,8 @@ namespace Tests\Feature\GammaMatrix\Playground\Http\Controllers\Index; +use GammaMatrix\Playground\Test\Models\User; +use GammaMatrix\Playground\Test\Models\UserWithRole; use Tests\Feature\GammaMatrix\Playground\TestCase; /** @@ -16,27 +18,28 @@ class WelcomeRouteTest extends TestCase { public function test_route_welcome_as_guest_and_succeed() { - $response = $this->get('/welcome'); + $response = $this->get(route('welcome')); $response->assertStatus(200); } public function test_route_json_welcome_as_guest_and_succeed() { - $response = $this->json('GET', '/welcome'); + $response = $this->json('GET', route('welcome')); $response->assertStatus(200); } - // public function test_route_welcome_as_user_and_succeed() - // { - // $this->initAuthRoles(); - // $response = $this->as('user')->get('/welcome'); - // $response->assertStatus(200); - // } + public function test_route_welcome_as_user_and_succeed() + { + $user = User::factory()->create(); + $response = $this->actingAs($user)->getJson(route('welcome')); + $response->assertStatus(200); + } - // public function test_route_json_welcome_as_manager_admin_and_succeed() - // { - // $this->initAuthRoles(); - // $response = $this->as('manager-admin')->getJson('/welcome'); - // $response->assertStatus(200); - // } + public function test_route_json_welcome_as_manager_admin_and_succeed() + { + $user = UserWithRole::find(User::factory()->create()->id); + $user->role = 'manager-admin'; + $response = $this->actingAs($user)->getJson(route('welcome')); + $response->assertStatus(200); + } } diff --git a/tests/Feature/Policies/PrivilegeTrait/TraitTest.php b/tests/Feature/Policies/PrivilegeTrait/TraitTest.php new file mode 100644 index 0000000..5f1bcb5 --- /dev/null +++ b/tests/Feature/Policies/PrivilegeTrait/TraitTest.php @@ -0,0 +1,188 @@ +mock = $this->getMockForTrait( + static::TRAIT_CLASS, + [], + '', + true, + true, + true, + $methods = [] + ); + } + + public function test_hasPrivilege_and_fail_with_empty_privilege() + { + $user = UserWithSanctum::factory()->make(); + $privilege = ''; + + $this->assertInstanceOf(Response::class, $this->mock->hasPrivilege( + $user, + $privilege + )); + } + + public function test_hasPrivilege_with_app_without_token_and_fail() + { + $user = UserWithSanctum::factory()->make(); + $privilege = 'app:*'; + + $this->assertInstanceOf(Response::class, $this->mock->hasPrivilege( + $user, + $privilege + )); + } + + public function test_hasPrivilege_with_app_with_wildcard_token_privileges_and_succeed() + { + $this->mock->expects($this->any()) + ->method('hasToken') + ->will($this->returnValue(true)) + ; + + $user = UserWithSanctum::factory()->create(); + $privilege = 'app:*'; + + $name = 'app'; + $privileges = [ + 'app:*', + 'view:*', + ]; + $expiresAt = new Carbon('+5 minutes'); + + $token = $user->createToken($name, $privileges, $expiresAt); + // $token->plainTextToken; + + $access_token = $user->tokens() + ->where('name', config('playground.auth.token.name')) + // Get the latest created token. + ->orderBy('created_at', 'desc') + ->firstOrFail() + ; + + $this->assertInstanceOf(PersonalAccessToken::class, $access_token); + + $this->mock->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($access_token)) + ; + + $this->assertTrue($this->mock->hasPrivilege( + $user, + $privilege + )); + } + + public function test_hasPrivilege_with_app_with_wildcard_secondary_token_privileges_and_succeed() + { + $this->mock->expects($this->any()) + ->method('hasToken') + ->will($this->returnValue(true)) + ; + + $user = UserWithSanctum::factory()->create(); + $privilege = 'playground-matrix-resource:backlog:view'; + $privilege = 'playground-matrix-resource:backlog:*'; + + $name = 'app'; + $privileges = [ + 'playground-matrix-resource:backlog:*', + 'playground-matrix:view', + 'playground-matrix-resource:view', + ]; + $expiresAt = new Carbon('+5 minutes'); + + $token = $user->createToken($name, $privileges, $expiresAt); + // $token->plainTextToken; + + $access_token = $user->tokens() + ->where('name', config('playground.auth.token.name')) + // Get the latest created token. + ->orderBy('created_at', 'desc') + ->firstOrFail() + ; + + $this->assertInstanceOf(PersonalAccessToken::class, $access_token); + + $this->mock->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($access_token)) + ; + + $this->assertTrue($this->mock->hasPrivilege( + $user, + $privilege + )); + } + + public function test_hasPrivilege_with_app_with_token_privileges_and_succeed() + { + $this->mock->expects($this->any()) + ->method('hasToken') + ->will($this->returnValue(true)) + ; + + $user = UserWithSanctum::factory()->create(); + $privilege = 'app'; + + $name = 'app'; + $privileges = [ + 'app', + 'view', + ]; + $expiresAt = new Carbon('+5 minutes'); + + $token = $user->createToken($name, $privileges, $expiresAt); + // $token->plainTextToken; + + $access_token = $user->tokens() + ->where('name', config('playground.auth.token.name')) + // Get the latest created token. + ->orderBy('created_at', 'desc') + ->firstOrFail() + ; + + $this->assertInstanceOf(PersonalAccessToken::class, $access_token); + + $this->mock->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($access_token)) + ; + + $this->assertTrue($this->mock->hasPrivilege( + $user, + $privilege + )); + } +} diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index 91cb941..8087d39 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -26,6 +26,22 @@ protected function getPackageProviders($app) ]; } + /** + * Setup the test environment. + */ + protected function setUp(): void + { + parent::setUp(); + // dd([ + // '__METHOD__' => __METHOD__, + // 'path' => dirname(dirname(__DIR__)) . '/database/migrations', + // ]); + if (!empty(env('TEST_DB_MIGRATIONS'))) { + // $this->loadLaravelMigrations(); + $this->loadMigrationsFrom(dirname(dirname(__DIR__)) . '/database/migrations-laravel'); + } + } + /** * Set up the environment. * diff --git a/tests/Unit/Filters/ModelTrait/ArrayTraitTest.php b/tests/Unit/Filters/ModelTrait/ArrayTraitTest.php new file mode 100644 index 0000000..e3d3cdb --- /dev/null +++ b/tests/Unit/Filters/ModelTrait/ArrayTraitTest.php @@ -0,0 +1,92 @@ +assertSame([], $this->mock->filterArray([])); + + // NULL + $this->assertSame([], $this->mock->filterArray(null)); + + // false + $this->assertSame([], $this->mock->filterArray(false)); + + // true + $this->assertSame([], $this->mock->filterArray(true)); + + $value = 'just-a-string-value'; + + // A string is converted to any array. + $this->assertSame([$value], $this->mock->filterArray($value)); + + // A test array should not be altered. + $value = [ + 'i' => 'am-a-test-array', + 'someNullValue' => null, + 'aString' => 'thanks!', + 'object' => (object) ['ok' => true,], + ]; + + // Returns the same array. + $this->assertSame($value, $this->mock->filterArray($value)); + } + + /** + * filterArrayToJson + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterArrayToJson() + */ + public function test_filterArray_to_json() + { + // Unexpected values return a json encoded empty array.. + + // empty array + $this->assertSame(json_encode([]), $this->mock->filterArrayToJson([])); + + // NULL + $this->assertSame(json_encode([]), $this->mock->filterArrayToJson(null)); + + // false + $this->assertSame(json_encode([]), $this->mock->filterArrayToJson(false)); + + // true + $this->assertSame(json_encode([]), $this->mock->filterArrayToJson(true)); + + $value = 'just-a-string-value'; + + // A string will remain a string, unchanged. + $this->assertSame($value, $this->mock->filterArrayToJson($value)); + + // A test array should not be altered. + $value = [ + 'i' => 'am-a-test-array', + 'someNullValue' => null, + 'aString' => 'thanks!', + 'object' => (object) ['ok' => true,], + ]; + + // Returns the same array. + $this->assertSame(json_encode($value), $this->mock->filterArrayToJson($value)); + } +} diff --git a/tests/Unit/Filters/ModelTrait/BitsTraitTest.php b/tests/Unit/Filters/ModelTrait/BitsTraitTest.php new file mode 100644 index 0000000..6285691 --- /dev/null +++ b/tests/Unit/Filters/ModelTrait/BitsTraitTest.php @@ -0,0 +1,60 @@ +assertSame(0, $this->mock->filterBits(0)); + + $value = 1 + 2 + 4 + 8 + 16 + 32; + $expected = 1; + $this->assertSame($expected, $this->mock->filterBits($value)); + } + + /** + * filterBits: $exponent > 0 + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterBits() + */ + public function test_filterBits_for_exponent_greater_than_zero() + { + $value = 1 + 2 + 4 + 8 + 16 + 32; + + $exponent = 1; + $expected = 1 + 2; + $this->assertSame($expected, $this->mock->filterBits($value, $exponent)); + + $exponent = 2; + $expected = 1 + 2 + 4; + $this->assertSame($expected, $this->mock->filterBits($value, $exponent)); + + $exponent = 3; + $expected = 1 + 2 + 4 + 8; + $this->assertSame($expected, $this->mock->filterBits($value, $exponent)); + + $exponent = 4; + $expected = 1 + 2 + 4 + 8 + 16; + $this->assertSame($expected, $this->mock->filterBits($value, $exponent)); + + $exponent = 5; + $expected = 1 + 2 + 4 + 8 + 16 + 32; + $this->assertSame($expected, $this->mock->filterBits($value, $exponent)); + } +} diff --git a/tests/Unit/Filters/ModelTrait/DateTraitTest.php b/tests/Unit/Filters/ModelTrait/DateTraitTest.php new file mode 100644 index 0000000..be3e95c --- /dev/null +++ b/tests/Unit/Filters/ModelTrait/DateTraitTest.php @@ -0,0 +1,46 @@ +assertNull($this->mock->filterDate('')); + + $this->assertSame( + gmdate(config('playground.date.sql', 'Y-m-d H:i:s'), strtotime('now')), + $this->mock->filterDate('now') + ); + + } + + /** + * filterDateAsCarbon + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterDateAsCarbon() + */ + public function test_filterDateAsCarbon() + { + $this->assertNull($this->mock->filterDateAsCarbon('')); + + $date = 'now'; + $this->assertInstanceOf(\DateTime::class, $this->mock->filterDateAsCarbon($date)); + $this->assertInstanceOf(\Carbon\Carbon::class, $this->mock->filterDateAsCarbon($date)); + } +} diff --git a/tests/Unit/Filters/ModelTrait/NumberTraitTest.php b/tests/Unit/Filters/ModelTrait/NumberTraitTest.php new file mode 100644 index 0000000..d012113 --- /dev/null +++ b/tests/Unit/Filters/ModelTrait/NumberTraitTest.php @@ -0,0 +1,133 @@ +assertNull($this->mock->filterFloat('')); + $this->assertNull($this->mock->filterFloat(null)); + + $this->assertSame(0.0, $this->mock->filterFloat(0)); + $this->assertSame(1.0, $this->mock->filterFloat(1)); + } + + /** + * filterInteger + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterInteger() + */ + public function test_filterInteger() + { + $this->assertSame(0, $this->mock->filterInteger('')); + $this->assertSame(0, $this->mock->filterInteger(null)); + $this->assertSame(0, $this->mock->filterInteger(false)); + + $this->assertSame(1000, $this->mock->filterInteger('1,000')); + $this->assertSame(2000, $this->mock->filterInteger('2,000.01')); + $this->assertSame(0, $this->mock->filterInteger(0)); + $this->assertSame(1, $this->mock->filterInteger(1)); + $this->assertSame(-1001, $this->mock->filterInteger(-1001)); + } + + /** + * filterIntegerId + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterIntegerId() + */ + public function test_filterIntegerId() + { + $this->assertNull($this->mock->filterIntegerId('')); + $this->assertNull($this->mock->filterIntegerId(null)); + $this->assertNull($this->mock->filterIntegerId(false)); + + $this->assertNull($this->mock->filterIntegerId('1,000')); + $this->assertNull($this->mock->filterIntegerId('2,000.01')); + $this->assertNull($this->mock->filterIntegerId(0)); + $this->assertSame(1, $this->mock->filterIntegerId(1)); + $this->assertNull($this->mock->filterIntegerId(-1001)); + $this->assertNull($this->mock->filterIntegerId('-1001')); + $this->assertSame(1, $this->mock->filterIntegerId('1')); + $this->assertSame(100, $this->mock->filterIntegerId('100')); + $this->assertSame(2000000, $this->mock->filterIntegerId('2000000.0')); + } + + /** + * filterIntegerPositive + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterIntegerPositive() + */ + public function test_filterIntegerPositive() + { + $this->assertSame(0, $this->mock->filterIntegerPositive('')); + $this->assertSame(0, $this->mock->filterIntegerPositive(null)); + $this->assertSame(0, $this->mock->filterIntegerPositive(false)); + + $this->assertSame(1, $this->mock->filterIntegerPositive('1,000')); + $this->assertSame(2, $this->mock->filterIntegerPositive('2,000.01')); + $this->assertSame(0, $this->mock->filterIntegerPositive(0)); + $this->assertSame(1, $this->mock->filterIntegerPositive(1)); + $this->assertSame(1001, $this->mock->filterIntegerPositive(-1001)); + $this->assertSame(1001, $this->mock->filterIntegerPositive(-1001)); + + $absolute = true; + $this->assertSame(0, $this->mock->filterIntegerPositive('', $absolute)); + $this->assertSame(0, $this->mock->filterIntegerPositive(null, $absolute)); + $this->assertSame(0, $this->mock->filterIntegerPositive(false, $absolute)); + + $this->assertSame(1, $this->mock->filterIntegerPositive('1,000', $absolute)); + $this->assertSame(2, $this->mock->filterIntegerPositive('2,000.01', $absolute)); + $this->assertSame(0, $this->mock->filterIntegerPositive(0, $absolute)); + $this->assertSame(1, $this->mock->filterIntegerPositive(1, $absolute)); + $this->assertSame(1001, $this->mock->filterIntegerPositive(-1001, $absolute)); + $this->assertSame(1001, $this->mock->filterIntegerPositive(-1001, $absolute)); + + $absolute = false; + $this->assertSame(0, $this->mock->filterIntegerPositive('', $absolute)); + $this->assertSame(0, $this->mock->filterIntegerPositive(null, $absolute)); + $this->assertSame(0, $this->mock->filterIntegerPositive(false, $absolute)); + + $this->assertSame(1, $this->mock->filterIntegerPositive('1,000', $absolute)); + $this->assertSame(2, $this->mock->filterIntegerPositive('2,000.01', $absolute)); + $this->assertSame(0, $this->mock->filterIntegerPositive(0, $absolute)); + $this->assertSame(1, $this->mock->filterIntegerPositive(1, $absolute)); + $this->assertSame(-1001, $this->mock->filterIntegerPositive(-1001, $absolute)); + $this->assertSame(-1001, $this->mock->filterIntegerPositive(-1001, $absolute)); + } + + /** + * filterPercent + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterPercent() + */ + public function test_filterPercent() + { + $this->assertNull($this->mock->filterPercent('')); + $this->assertNull($this->mock->filterPercent(null)); + $this->assertNull($this->mock->filterPercent(false)); + + $this->assertSame(1000.0, $this->mock->filterPercent('1,000%')); + + $this->assertSame(1000.0, $this->mock->filterPercent('1,000')); + $this->assertSame(2000.01, $this->mock->filterPercent('2,000.01')); + $this->assertSame(0.0, $this->mock->filterPercent(0)); + $this->assertSame(1.0, $this->mock->filterPercent(1)); + $this->assertSame(-1001.0, $this->mock->filterPercent('-1001 %')); + } +} diff --git a/tests/Unit/Filters/ModelTrait/StatusTraitTest.php b/tests/Unit/Filters/ModelTrait/StatusTraitTest.php new file mode 100644 index 0000000..b2f5b56 --- /dev/null +++ b/tests/Unit/Filters/ModelTrait/StatusTraitTest.php @@ -0,0 +1,45 @@ +assertSame([], $this->mock->filterStatus([])); + + $expected = ['status' => 1]; + $this->assertSame($expected, $this->mock->filterStatus(['status' => 1])); + $this->assertSame($expected, $this->mock->filterStatus(['status' => '1'])); + $this->assertSame($expected, $this->mock->filterStatus(['status' => '-1.0'])); + + $expected = [ + 'status' => [ + 'active' => true, + 'lock' => true, + 'public' => false, + ], + ]; + $this->assertSame($expected, $this->mock->filterStatus([ + 'status' => [ + 'active' => 1, + 'lock' => true, + 'public' => 0, + ], + ])); + } +} diff --git a/tests/Unit/Filters/ModelTrait/SystemFieldsTraitTest.php b/tests/Unit/Filters/ModelTrait/SystemFieldsTraitTest.php new file mode 100644 index 0000000..da7c6c2 --- /dev/null +++ b/tests/Unit/Filters/ModelTrait/SystemFieldsTraitTest.php @@ -0,0 +1,97 @@ +assertSame([], $this->mock->filterSystemFields([])); + + $expected = ['gids' => 1]; + $this->assertSame($expected, $this->mock->filterSystemFields(['gids' => 1])); + } + + /** + * filterSystemFields: gids + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterSystemFields() + */ + public function test_filterSystemFields_for_permissions() + { + $this->assertSame([], $this->mock->filterSystemFields([])); + + $expected = [ + 'po' => 7, + 'pg' => 4, + 'pw' => 4, + ]; + $this->assertSame($expected, $this->mock->filterSystemFields([ + 'po' => 7, + 'pg' => 4, + 'pw' => 4, + ])); + + // Only the permission bits may be set. + + $expected = [ + 'po' => 4, + 'pg' => 4, + 'pw' => 4, + ]; + $this->assertSame($expected, $this->mock->filterSystemFields([ + 'po' => 100, + 'pg' => 4, + 'pw' => 4, + ])); + } + + /** + * filterSystemFields: rank + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterSystemFields() + */ + public function test_filterSystemFields_for_rank() + { + $expected = ['rank' => 0]; + $this->assertSame($expected, $this->mock->filterSystemFields(['rank' => 0])); + + $expected = ['rank' => 1]; + $this->assertSame($expected, $this->mock->filterSystemFields(['rank' => 1])); + + $expected = ['rank' => -1]; + $this->assertSame($expected, $this->mock->filterSystemFields(['rank' => -1])); + } + + /** + * filterSystemFields: size + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterSystemFields() + */ + public function test_filterSystemFields_for_size() + { + $expected = ['size' => 0]; + $this->assertSame($expected, $this->mock->filterSystemFields(['size' => 0])); + + $expected = ['size' => 1]; + $this->assertSame($expected, $this->mock->filterSystemFields(['size' => 1])); + + $expected = ['size' => -1]; + $this->assertSame($expected, $this->mock->filterSystemFields(['size' => -1])); + } +} diff --git a/tests/Unit/Filters/ModelTrait/TraitTest.php b/tests/Unit/Filters/ModelTrait/TraitTestCase.php similarity index 50% rename from tests/Unit/Filters/ModelTrait/TraitTest.php rename to tests/Unit/Filters/ModelTrait/TraitTestCase.php index 296c737..70e1c18 100644 --- a/tests/Unit/Filters/ModelTrait/TraitTest.php +++ b/tests/Unit/Filters/ModelTrait/TraitTestCase.php @@ -9,10 +9,11 @@ use Tests\Unit\GammaMatrix\Playground\TestCase; /** - * \Tests\Unit\GammaMatrix\Playground\Filters\ModelTrait\TraitTest + * \Tests\Unit\App\Filter\ModelTrait\AbstractTraitTest + * * */ -class TraitTest extends TestCase +abstract class TraitTestCase extends TestCase { public const TRAIT_CLASS = \GammaMatrix\Playground\Filters\ModelTrait::class; @@ -37,25 +38,4 @@ protected function setUp(): void $methods = [] ); } - - public function test_filterArray() - { - $expected = ['some-string']; - - $this->assertSame($expected, $this->mock->filterArray($expected)); - } - - public function test_filterArray_with_string() - { - $expected = 'some-string'; - - $this->assertSame([$expected], $this->mock->filterArray($expected)); - } - - public function test_filterArray_with_null() - { - $expected = []; - - $this->assertSame($expected, $this->mock->filterArray(null)); - } } diff --git a/tests/Unit/Filters/ModelTrait/UuidTraitTest.php b/tests/Unit/Filters/ModelTrait/UuidTraitTest.php new file mode 100644 index 0000000..e5eb10c --- /dev/null +++ b/tests/Unit/Filters/ModelTrait/UuidTraitTest.php @@ -0,0 +1,28 @@ +uuid; + $this->assertNotEmpty($uuid); + $this->assertSame($uuid, $this->mock->filterUuid($uuid)); + } +} diff --git a/tests/Unit/Filters/ModelTrait/ValueTraitTest.php b/tests/Unit/Filters/ModelTrait/ValueTraitTest.php new file mode 100644 index 0000000..7b617cd --- /dev/null +++ b/tests/Unit/Filters/ModelTrait/ValueTraitTest.php @@ -0,0 +1,102 @@ +assertFalse($this->mock->filterBoolean([])); + + // NULL + $this->assertFalse($this->mock->filterBoolean(null)); + + // false + $this->assertFalse($this->mock->filterBoolean([])); + + // true + $this->assertTrue($this->mock->filterBoolean(true)); + + // Positive numbers are true + $this->assertTrue($this->mock->filterBoolean(10)); + $this->assertTrue($this->mock->filterBoolean(1)); + $this->assertFalse($this->mock->filterBoolean(0)); + $this->assertFalse($this->mock->filterBoolean(-10)); + + // Empty string + $this->assertFalse($this->mock->filterBoolean('')); + + $this->assertTrue($this->mock->filterBoolean('true')); + + $this->assertFalse($this->mock->filterBoolean('not-true')); + + $value = [ + 'i' => 'am-a-test-array', + 'someNullValue' => null, + 'aString' => 'thanks!', + 'object' => (object) ['ok' => true,], + ]; + + $this->assertTrue($this->mock->filterBoolean($value)); + } + + /** + * filterEmail + * + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterEmail() + */ + public function test_filterEmail() + { + $value = false; + $this->assertSame('', $this->mock->filterEmail($value)); + + $value = 'not valid email with spaces @ example.com'; + $this->assertSame('notvalidemailwithspaces@example.com', $this->mock->filterEmail($value)); + + $value = 'test@'; + $this->assertSame($value, $this->mock->filterEmail($value)); + + $value = 'test@example.com'; + $this->assertSame($value, $this->mock->filterEmail($value)); + $value = ''; + $this->assertSame('test@example.com', $this->mock->filterEmail($value)); + + $value = 'test+with.plus-addressing@example.com'; + $this->assertSame($value, $this->mock->filterEmail($value)); + } + + /** + * filterHtml + * + * @see \GammaMatrix\Playground\Filters\ContentTrait::purify() HTMLPurifier + * @see \GammaMatrix\Playground\Filters\ModelTrait::filterHtml() + */ + public function test_filterHtml() + { + $value = 'Tags should be removed.'; + $this->assertSame('Tags should be removed.', $this->mock->filterHtml($value)); + + $value = 'No links allowed either.'; + $this->assertSame('No links allowed either.', $this->mock->filterHtml($value)); + } +} diff --git a/tests/Unit/Policies/Policy/AbstractTest.php b/tests/Unit/Policies/Policy/AbstractTest.php index 50b04f3..700cd69 100644 --- a/tests/Unit/Policies/Policy/AbstractTest.php +++ b/tests/Unit/Policies/Policy/AbstractTest.php @@ -139,6 +139,27 @@ public function test_view_with_admin() ]; $user->role = $role; + $user->roles = $roles; + + $this->assertTrue($this->mock->view($user)); + } + + /** + * Test view(). + * + */ + public function test_view_with_admin_in_roles() + { + $user = User::factory()->make(); + + $role = 'user-external'; + $roles = [ + 'wheel', + 'user', + ]; + + $user->role = $role; + $user->roles = $roles; $this->assertTrue($this->mock->view($user)); } diff --git a/tests/Unit/Policies/PolicyTrait/TraitTest.php b/tests/Unit/Policies/PolicyTrait/TraitTest.php index c248183..ab205ed 100644 --- a/tests/Unit/Policies/PolicyTrait/TraitTest.php +++ b/tests/Unit/Policies/PolicyTrait/TraitTest.php @@ -124,4 +124,17 @@ public function test_verify_roles() $this->assertFalse($this->mock->verify($user, $ability)); } + + public function test_verify_user() + { + $user = User::factory()->make(); + + $verify = 'user'; + + config(['playground.auth.verify' => $verify]); + + $ability = 'view'; + + $this->assertTrue($this->mock->verify($user, $ability)); + } } diff --git a/tests/Unit/Policies/PrivilegeTrait/TraitTest.php b/tests/Unit/Policies/PrivilegeTrait/TraitTest.php index ec6d32d..e2a901d 100644 --- a/tests/Unit/Policies/PrivilegeTrait/TraitTest.php +++ b/tests/Unit/Policies/PrivilegeTrait/TraitTest.php @@ -50,16 +50,4 @@ public function test_hasPrivilege() $privilege )); } - - // Requires a database - // public function test_hasPrivilege_with_app() - // { - // $user = UserWithSanctum::factory()->make(); - // $privilege = 'app:*'; - - // $this->assertInstanceOf(Response::class, $this->mock->hasPrivilege( - // $user, - // $privilege - // )); - // } } diff --git a/tests/Unit/Policies/RoleTrait/TraitTest.php b/tests/Unit/Policies/RoleTrait/TraitTest.php index 49882d5..d738786 100644 --- a/tests/Unit/Policies/RoleTrait/TraitTest.php +++ b/tests/Unit/Policies/RoleTrait/TraitTest.php @@ -83,4 +83,15 @@ public function test_hasRole() $ability )); } + + public function test_hasRole_advanced_role() + { + $user = User::factory()->make(); + + $ability = 'some-advanded-role'; + $this->assertInstanceOf(Response::class, $this->mock->hasRole( + $user, + $ability + )); + } } diff --git a/tests/Unit/View/Components/Forms/Column/ComponentTest.php b/tests/Unit/View/Components/Forms/Column/ComponentTest.php new file mode 100644 index 0000000..3f2728f --- /dev/null +++ b/tests/Unit/View/Components/Forms/Column/ComponentTest.php @@ -0,0 +1,56 @@ + true, + ]); + } + + public function test_component_instance() + { + $instance = new Column(); + + $this->assertInstanceOf(Column::class, $instance); + } + + public function test_component_can_render_view_without_parameters() + { + $instance = new Column(); + + $this->assertInstanceOf(View::class, $instance->render()); + } + + public function test_component_can_render_view_with_label_input() + { + $advanced = true; + $autocomplete = null; + $column = 'label'; + $instance = new Column($advanced, $autocomplete, $column); + + $this->assertInstanceOf(View::class, $instance->render()); + } +} diff --git a/tests/Unit/View/Components/Forms/ColumnEditor/ComponentTest.php b/tests/Unit/View/Components/Forms/ColumnEditor/ComponentTest.php new file mode 100644 index 0000000..f5f29ca --- /dev/null +++ b/tests/Unit/View/Components/Forms/ColumnEditor/ComponentTest.php @@ -0,0 +1,56 @@ + true, + ]); + } + + public function test_component_instance() + { + $instance = new ColumnEditor(); + + $this->assertInstanceOf(ColumnEditor::class, $instance); + } + + public function test_component_can_render_view_without_parameters() + { + $instance = new ColumnEditor(); + + $this->assertInstanceOf(View::class, $instance->render()); + } + + public function test_component_can_render_view_with_content_textarea() + { + $advanced = true; + $class = 'editor'; + $column = 'content'; + $instance = new ColumnEditor($advanced, $class, $column); + + $this->assertInstanceOf(View::class, $instance->render()); + } +} diff --git a/tests/Unit/View/Components/Forms/ColumnSelect/ComponentTest.php b/tests/Unit/View/Components/Forms/ColumnSelect/ComponentTest.php new file mode 100644 index 0000000..22a996e --- /dev/null +++ b/tests/Unit/View/Components/Forms/ColumnSelect/ComponentTest.php @@ -0,0 +1,57 @@ + true, + ]); + } + + public function test_component_instance() + { + $instance = new ColumnSelect(); + + $this->assertInstanceOf(ColumnSelect::class, $instance); + } + + public function test_component_can_render_view_without_parameters() + { + $instance = new ColumnSelect(); + + $this->assertInstanceOf(View::class, $instance->render()); + } + + public function test_component_can_render_view_with_select() + { + $advanced = true; + $autocomplete = true; + $class = ''; + $column = 'owned_by_id'; + $instance = new ColumnSelect($advanced, $autocomplete, $class, $column); + + $this->assertInstanceOf(View::class, $instance->render()); + } +} diff --git a/tests/Unit/View/Components/ModelFlag/ComponentTest.php b/tests/Unit/View/Components/ModelFlag/ComponentTest.php new file mode 100644 index 0000000..d655d3c --- /dev/null +++ b/tests/Unit/View/Components/ModelFlag/ComponentTest.php @@ -0,0 +1,83 @@ + true, + ]); + } + + public function test_component_instance() + { + $columnMeta = []; + $value = null; + $instance = new ModelFlag($columnMeta, $value); + + $this->assertInstanceOf(ModelFlag::class, $instance); + } + + public function test_component_can_render_view_with_null_value() + { + $columnMeta = [ + 'onFalseClass' => '', + 'onFalseLabel' => '', + 'onTrueClass' => '', + 'onTrueLabel' => '', + ]; + $value = null; + $instance = new ModelFlag($columnMeta, $value); + + $this->assertInstanceOf(View::class, $instance->render()); + } + + public function test_component_can_render_view_with_true_value() + { + $columnMeta = [ + 'onFalseClass' => 'fa-solid fa-user', + 'onFalseLabel' => 'Civilian', + 'onTrueClass' => 'fa-solid fa-user-secret', + 'onTrueLabel' => 'Spy', + ]; + $value = true; + $instance = new ModelFlag($columnMeta, $value); + + $this->assertInstanceOf(View::class, $instance->render()); + } + + public function test_component_can_render_view_with_false_value() + { + $columnMeta = [ + 'onFalseClass' => 'fa-solid fa-user', + 'onFalseLabel' => 'Civilian', + 'onTrueClass' => 'fa-solid fa-user-secret', + 'onTrueLabel' => 'Spy', + ]; + $value = false; + $instance = new ModelFlag($columnMeta, $value); + + $this->assertInstanceOf(View::class, $instance->render()); + } +} diff --git a/tests/Unit/View/Components/PaginationSort/ComponentTest.php b/tests/Unit/View/Components/PaginationSort/ComponentTest.php new file mode 100644 index 0000000..24a57c5 --- /dev/null +++ b/tests/Unit/View/Components/PaginationSort/ComponentTest.php @@ -0,0 +1,92 @@ + true, + ]); + } + + public function test_component_instance() + { + $sort = [ + 'title' => ['label' => 'Title'], + ]; + // $validated = []; + // $action = ''; + // $id = ''; + // $sorts = 3; + + $instance = new PaginationSort($sort); + + $this->assertInstanceOf(PaginationSort::class, $instance); + } + + public function test_component_can_render_view_with_empty_sort() + { + $sort = []; + $validated = [ + 'sort' => ['title' , '-active'], + 'perPage' => 1, + 'page' => 1, + ]; + $instance = new PaginationSort($sort); + + $this->assertInstanceOf(View::class, $instance->render()); + } + + public function test_component_can_render_view_with_invalid_sort() + { + // Will fail since array is associative. + $sort = [ + 'title', + ]; + $validated = [ + 'sort' => ['title' , '-active'], + 'perPage' => 1, + 'page' => 1, + ]; + $instance = new PaginationSort($sort); + + $this->assertInstanceOf(View::class, $instance->render()); + } + + public function test_component_can_render_view() + { + $sort = [ + 'title' => ['label' => 'Title'], + 'active' => ['label' => 'Active'], + ]; + $validated = [ + 'sort' => ['title' , '-active'], + 'perPage' => 1, + 'page' => 1, + ]; + $instance = new PaginationSort($sort); + + $this->assertInstanceOf(View::class, $instance->render()); + } +} diff --git a/tests/Unit/View/Components/Table/ComponentTest.php b/tests/Unit/View/Components/Table/ComponentTest.php new file mode 100644 index 0000000..b84863c --- /dev/null +++ b/tests/Unit/View/Components/Table/ComponentTest.php @@ -0,0 +1,46 @@ + true, + ]); + } + + public function test_component_instance() + { + $instance = new DataTable(); + + $this->assertInstanceOf(DataTable::class, $instance); + } + + public function test_component_can_render_view() + { + $instance = new DataTable(); + + $this->assertInstanceOf(View::class, $instance->render()); + } +}