Skip to content

Commit 86b7c61

Browse files
authored
Unit testing (#20)
* Config unit tests + CI * Apply fixes from StyleCI (#14) * Parser test + minor namespace refactor * Request parameter tests * Apply fixes from StyleCI (#15) * Started callback testing + minor adjustments * Apply fixes from StyleCI (#16) * Simplified code * Minor refactor * Testing callbacks * Apply fixes from StyleCI (#17) * Simplified bool types + added test * Apply fixes from StyleCI (#18) * Categorized values test * Being honest about features * Apply fixes from StyleCI (#19) * JSON query testing + doctrine fix * Apply fixes from StyleCI (#21) * Replaced constant with abstract * Apply fixes from StyleCI (#22) * Test * Test2 * Added interface to parameter * Reverted abstract type hints because of PHPStan * Removed leftovers * Minor * Added abstract check
1 parent 29f9e04 commit 86b7c61

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2178
-153
lines changed

.github/workflows/pull_request.yml

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: PR pipeline
2+
3+
on:
4+
pull_request:
5+
branches: [ master ]
6+
7+
jobs:
8+
build:
9+
10+
runs-on: Ubuntu-20.04
11+
12+
steps:
13+
- uses: actions/checkout@v2
14+
15+
- name: Setup PHP
16+
uses: shivammathur/setup-php@v2
17+
with:
18+
php-version: '7.4'
19+
extensions: mbstring, intl
20+
ini-values: post_max_size=256M, max_execution_time=180
21+
coverage: xdebug
22+
tools: php-cs-fixer, phpunit
23+
24+
- name: Validate composer.json and composer.lock
25+
run: composer validate
26+
27+
- name: Cache Composer packages
28+
id: composer-cache
29+
uses: actions/cache@v2
30+
with:
31+
path: vendor
32+
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
33+
restore-keys: |
34+
${{ runner.os }}-php-
35+
36+
- name: Install dependencies
37+
if: steps.composer-cache.outputs.cache-hit != 'true'
38+
run: composer install --prefer-dist --no-progress --no-suggest
39+
40+
- name: Execute PHPUnit tests
41+
run:
42+
vendor/phpunit/phpunit/phpunit
43+
44+
- name: PHP STatic ANalyser (phpstan)
45+
uses: chindit/actions-phpstan@1.0.1
46+
with:
47+
# Arguments to add to PHPStan CLI
48+
arguments: --level 0 src

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ Using ``order_by`` key does an 'order by' based on the given key(s). Order of th
171171
matters!
172172

173173
Arguments are presumed to be in a ``"column": "direction"`` fashion, where `direction`
174-
MUST be ``asc`` (ascending) or `desc` (descending); everything else will throw an
175-
exception.
174+
MUST be ``asc`` (ascending) or `desc` (descending). In case that only column is provided,
175+
direction will be assumed to be an ascending order.
176176

177177
Example:
178178
```
@@ -519,6 +519,6 @@ You can extend this functionality by adding your own custom parameter. It
519519
needs to extend ``Asseco\JsonQueryBuilder\RequestParameters\AbstractParameter``
520520
in order to work.
521521
- operators are registered under ``operators`` config key. Those can be
522-
extended by adding a class which extends ``Asseco\JsonQueryBuilder\SearchCallbacks\AbstractCallback``
522+
extended by adding a class which extends ``Asseco\JsonQueryBuilder\SearchCallbacks\AbstractCallback``.
523523
- types are registered under ``types`` config key. Those can be extended
524-
by adding a class which extends ``Asseco\JsonQueryBuilder\Types\AbstractType``
524+
by adding a class which extends ``Asseco\JsonQueryBuilder\Types\AbstractType``.

composer.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,20 @@
1212
},
1313
"require-dev": {
1414
"phpunit/phpunit": "^9.0",
15-
"mockery/mockery": "^1.3.1"
15+
"mockery/mockery": "^1.3.1",
16+
"fakerphp/faker": "^1.9.1",
17+
"orchestra/testbench": "^6.0"
1618
},
1719
"autoload": {
1820
"psr-4": {
1921
"Asseco\\JsonQueryBuilder\\": "src/"
2022
}
2123
},
24+
"autoload-dev": {
25+
"psr-4": {
26+
"Asseco\\JsonQueryBuilder\\Tests\\": "tests/"
27+
}
28+
},
2229
"extra": {
2330
"laravel": {
2431
"providers": [

config/asseco-json-query-builder.php

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
],
6969

7070
/**
71+
* TODO: these options are currently disabled and will not work
7172
* Refined options for a single model.
7273
* Use if you want to enforce rules on a specific model without affecting globally all models.
7374
*/

phpunit.xml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
colors="true">
6+
7+
<testsuites>
8+
<testsuite name="Feature tests">
9+
<directory suffix=".php">./tests/Feature</directory>
10+
</testsuite>
11+
12+
<testsuite name="Unit tests">
13+
<directory suffix=".php">./tests/Unit</directory>
14+
</testsuite>
15+
</testsuites>
16+
<coverage processUncoveredFiles="true">
17+
<include>
18+
<directory suffix=".php">./app</directory>
19+
</include>
20+
</coverage>
21+
<php>
22+
<server name="APP_ENV" value="testing"/>
23+
<server name="CACHE_DRIVER" value="array"/>
24+
<server name="DB_CONNECTION" value="testing"/>
25+
<server name="QUEUE_CONNECTION" value="sync"/>
26+
<server name="SESSION_DRIVER" value="array"/>
27+
</php>
28+
</phpunit>

src/CategorizedValues.php

+7-12
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
namespace Asseco\JsonQueryBuilder;
44

5-
use Asseco\JsonQueryBuilder\Config\OperatorsConfig;
65
use Asseco\JsonQueryBuilder\Config\TypesConfig;
7-
use Asseco\JsonQueryBuilder\RequestParameters\Models\Search;
86

97
class CategorizedValues
108
{
@@ -16,8 +14,7 @@ class CategorizedValues
1614
const IS_NULL = 'null';
1715
const IS_NOT_NULL = '!null';
1816

19-
protected OperatorsConfig $operatorsConfig;
20-
protected Search $searchModel;
17+
protected SearchParser $searchParser;
2118

2219
public array $and = [];
2320
public array $andLike = [];
@@ -28,14 +25,12 @@ class CategorizedValues
2825

2926
/**
3027
* CategorizedValues constructor.
31-
* @param OperatorsConfig $operatorsConfig
32-
* @param Search $searchModel
28+
* @param SearchParser $searchParser
3329
* @throws Exceptions\JsonQueryBuilderException
3430
*/
35-
public function __construct(OperatorsConfig $operatorsConfig, Search $searchModel)
31+
public function __construct(SearchParser $searchParser)
3632
{
37-
$this->operatorsConfig = $operatorsConfig;
38-
$this->searchModel = $searchModel;
33+
$this->searchParser = $searchParser;
3934

4035
$this->prepare();
4136
$this->categorize();
@@ -46,13 +41,13 @@ public function __construct(OperatorsConfig $operatorsConfig, Search $searchMode
4641
*/
4742
public function prepare()
4843
{
49-
$type = (new TypesConfig())->getCallbackByTypeName($this->searchModel->type);
50-
$this->searchModel->values = $type->prepare($this->searchModel->values);
44+
$type = (new TypesConfig())->getTypeClassFromTypeName($this->searchParser->type);
45+
$this->searchParser->values = $type->prepare($this->searchParser->values);
5146
}
5247

5348
public function categorize()
5449
{
55-
foreach ($this->searchModel->values as $value) {
50+
foreach ($this->searchParser->values as $value) {
5651
if ($value === self::IS_NULL) {
5752
$this->null = true;
5853
continue;

src/Config/ModelConfig.php

+41-22
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
namespace Asseco\JsonQueryBuilder\Config;
66

7-
use Doctrine\DBAL\DBALException;
7+
use Exception;
88
use Illuminate\Database\Eloquent\Model;
9+
use Illuminate\Support\Arr;
910
use Illuminate\Support\Facades\Cache;
10-
use Illuminate\Support\Facades\Config;
1111
use Illuminate\Support\Facades\DB;
1212
use Illuminate\Support\Facades\Schema;
1313

@@ -35,19 +35,19 @@ protected function getConfig(): array
3535
return config('asseco-json-query-builder.model_options.' . get_class($this->model));
3636
}
3737

38-
public function getReturns()
38+
public function getReturns(): array
3939
{
4040
if (array_key_exists('returns', $this->config) && $this->config['returns']) {
41-
return $this->config['returns'];
41+
return Arr::wrap($this->config['returns']);
4242
}
4343

4444
return ['*'];
4545
}
4646

47-
public function getRelations()
47+
public function getRelations(): array
4848
{
4949
if (array_key_exists('relations', $this->config) && $this->config['relations']) {
50-
return $this->config['relations'];
50+
return Arr::wrap($this->config['relations']);
5151
}
5252

5353
return [];
@@ -82,27 +82,29 @@ public function getForbidden(array $forbiddenKeys)
8282

8383
protected function getEloquentExclusion($forbiddenKeys): array
8484
{
85-
if (array_key_exists('eloquent_exclusion', $this->config) && $this->config['eloquent_exclusion']) {
86-
$guarded = $this->model->getGuarded();
87-
$fillable = $this->model->getFillable();
88-
89-
if ($guarded[0] != '*') { // Guarded property is never empty. It is '*' by default.
90-
$forbiddenKeys = array_merge($forbiddenKeys, $guarded);
91-
} elseif (count($fillable) > 0) {
92-
$forbiddenKeys = array_diff(array_keys($this->getModelColumns()), $fillable);
93-
}
85+
if (!array_key_exists('eloquent_exclusion', $this->config) || !($this->config['eloquent_exclusion'])) {
86+
return $forbiddenKeys;
87+
}
88+
89+
$guarded = $this->model->getGuarded();
90+
$fillable = $this->model->getFillable();
91+
92+
if ($guarded[0] != '*') { // Guarded property is never empty. It is '*' by default.
93+
$forbiddenKeys = array_merge($forbiddenKeys, $guarded);
94+
} elseif (count($fillable) > 0) {
95+
$forbiddenKeys = array_diff(array_keys($this->getModelColumns()), $fillable);
9496
}
9597

9698
return $forbiddenKeys;
9799
}
98100

99101
protected function getForbiddenColumns(array $forbiddenKeys): array
100102
{
101-
if (array_key_exists('forbidden_columns', $this->config) && $this->config['forbidden_columns']) {
102-
$forbiddenKeys = array_merge($forbiddenKeys, $this->config['forbidden_columns']);
103+
if (!array_key_exists('forbidden_columns', $this->config) || !($this->config['forbidden_columns'])) {
104+
return $forbiddenKeys;
103105
}
104106

105-
return $forbiddenKeys;
107+
return array_merge($forbiddenKeys, $this->config['forbidden_columns']);
106108
}
107109

108110
/**
@@ -122,19 +124,36 @@ public function getModelColumns(): array
122124
$columns = Schema::getColumnListing($table);
123125
$modelColumns = [];
124126

125-
// having 'enum' in table definition will throw Doctrine error because it is not defined in their types.
126-
// Registering it manually.
127-
DB::connection()->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
127+
$this->registerEnumTypeForDoctrine();
128+
128129
try {
129130
foreach ($columns as $column) {
130131
$modelColumns[$column] = DB::getSchemaBuilder()->getColumnType($table, $column);
131132
}
132-
} catch (DBALException $e) {
133+
} catch (Exception $e) {
133134
// leave model columns as an empty array and cache it.
134135
}
135136

136137
Cache::put(self::CACHE_PREFIX . $table, $modelColumns, self::CACHE_TTL);
137138

138139
return $modelColumns;
139140
}
141+
142+
/**
143+
* Having 'enum' in table definition will throw Doctrine error because it is not defined in their types.
144+
* Registering it manually.
145+
*/
146+
protected function registerEnumTypeForDoctrine(): void
147+
{
148+
$connection = DB::connection();
149+
150+
if (!class_exists('Doctrine\DBAL\Driver\AbstractSQLiteDriver')) {
151+
return;
152+
}
153+
154+
$connection
155+
->getDoctrineSchemaManager()
156+
->getDatabasePlatform()
157+
->registerDoctrineTypeMapping('enum', 'string');
158+
}
140159
}

src/Config/OperatorsConfig.php

+10-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
class OperatorsConfig extends SearchConfig
1111
{
12-
const CONFIG_KEY = 'operators';
12+
protected function configKey(): string
13+
{
14+
return 'operators';
15+
}
1316

1417
protected function operatorCallbackMapping(): array
1518
{
@@ -19,12 +22,16 @@ protected function operatorCallbackMapping(): array
1922
return array_combine($operators, $callbacks);
2023
}
2124

22-
public function getOperators()
25+
/**
26+
* Extract operators from registered 'operator' classes.
27+
* @return array
28+
*/
29+
public function getOperators(): array
2330
{
2431
/**
2532
* @var AbstractCallback $callback
2633
*/
27-
return array_map(fn ($callback) => $callback::getCallbackOperator(), $this->registered);
34+
return array_map(fn ($callback) => $callback::operator(), $this->registered);
2835
}
2936

3037
/**

src/Config/RequestParametersConfig.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@
66

77
class RequestParametersConfig extends SearchConfig
88
{
9-
const CONFIG_KEY = 'request_parameters';
9+
protected function configKey(): string
10+
{
11+
return 'request_parameters';
12+
}
1013
}

src/Config/SearchConfig.php

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Asseco\JsonQueryBuilder\Config;
66

77
use Asseco\JsonQueryBuilder\Exceptions\JsonQueryBuilderException;
8-
use Illuminate\Support\Facades\Config;
98

109
abstract class SearchConfig
1110
{
@@ -27,13 +26,16 @@ public function __construct()
2726
*
2827
* @throws JsonQueryBuilderException
2928
*/
30-
public function register(): void
29+
protected function register(): void
3130
{
32-
$key = static::CONFIG_KEY;
31+
$key = $this->configKey();
32+
3333
if (!array_key_exists($key, $this->config)) {
3434
throw new JsonQueryBuilderException("Config file is missing '$key'");
3535
}
3636

3737
$this->registered = $this->config[$key];
3838
}
39+
40+
abstract protected function configKey(): string;
3941
}

0 commit comments

Comments
 (0)