Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test suite #10

Merged
merged 3 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,26 @@ and then you will got this routes:
- [POST] '/api/blogs' - Create resource
- [DELETE] '/api/blogs/1' - Delete resource


On CreateHandler, you need to be create your custom request validation.

Im using `"spatie/laravel-query-builder": "^5.3"` to handle query and filtering. u can see `"spatie/laravel-query-builder": "^5.3"` [https://spatie.be/docs/laravel-query-builder/v5/introduction](documentation)

Im using `"spatie/laravel-query-builder": "^5.3"` to handle query selecting, sorting and filtering. Check out [the spatie/laravel-query-builder documentation](https://spatie.be/docs/laravel-query-builder/v5/introduction) for more information.

You can specified `allowedFilters` and `allowedFields` in your model
You can specified `allowedFilters` and `allowedFields` in your model. For example:

Example
```php
class User extends Model {
// Which fields can be selected from the database through the query string
public static array $allowedFields = [
'name'
];

// Which fields can be used to sort the results through the query string
public static array $allowedSorts = [
'name',
'created_at'
];

// Which fields can be used to filter the results through the query string
public static array $allowedFilters = [
'name'
];
Expand Down Expand Up @@ -107,7 +107,7 @@ class BlogTransformer extends JsonResource

return [
"modified_name" => $this->name . ' so Cool!'
]
];
}
}
```
Expand Down
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
"orchestra/testbench": "^8.0",
"pestphp/pest": "^2.0",
"pestphp/pest-plugin-arch": "^2.0",
"pestphp/pest-plugin-laravel": "^2.0"
"pestphp/pest-plugin-laravel": "^2.0",
"phpunit/phpunit": "^10.0.17"
},
"autoload": {
"psr-4": {
"Rupadana\\ApiService\\": "src/",
"Rupadana\\ApiService\\Database\\Factories\\": "database/factories/"
"Rupadana\\ApiService\\": "src/"
}
},
"autoload-dev": {
Expand Down Expand Up @@ -68,4 +68,4 @@
},
"minimum-stability": "dev",
"prefer-stable": true
}
}
19 changes: 0 additions & 19 deletions database/factories/ModelFactory.php

This file was deleted.

19 changes: 0 additions & 19 deletions database/migrations/create_api_service_table.php.stub

This file was deleted.

3 changes: 0 additions & 3 deletions src/ApiServiceServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ public function packageBooted(): void
], 'api-service-stubs');
}
}

// Testing
Testable::mixin(new TestsApiService());
}

protected function getAssetPackageName(): ?string
Expand Down
13 changes: 0 additions & 13 deletions src/Testing/TestsApiService.php

This file was deleted.

131 changes: 131 additions & 0 deletions tests/ApiServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

use Illuminate\Routing\Route as RoutingRoute;
use Rupadana\ApiService\Tests\Fixtures\Database\Seeders\ProductsSeeder;
use Rupadana\ApiService\Tests\Fixtures\Models\Product;

it('can make routes for a product resource', function () {
$routes = collect(app('router')->getRoutes())->map(function (RoutingRoute $route) {
return implode('|', $route->methods()) . ' ' . $route->uri();
});

// The route name is customized to `our-products` in the `ProductApiService` class
expect($routes)->toContain('POST api/our-products');
expect($routes)->toContain('PUT api/our-products/{id}');
expect($routes)->toContain('DELETE api/our-products/{id}');
expect($routes)->toContain('GET|HEAD api/our-products');
expect($routes)->toContain('GET|HEAD api/our-products/{id}');
});

it('can return a list of products with allowed attributes', function () {
$this->seed(ProductsSeeder::class);

$response = $this->get('/api/our-products')
->assertStatus(200);

$products = Product::all()->map(function ($product) {
return [
'name' => $product['name'],
'description' => $product['description'],
'price' => $product['price'],
];
})->toArray();

foreach ($products as $product) {
$response->assertJsonFragment($product);
}

// Check that the slug (hidden) is not returned
$response->assertJsonMissing([
'slug' => 't-shirt',
]);
});

it('can return a list of products with selected fields', function () {
$this->seed(ProductsSeeder::class);

$response = $this->get('/api/our-products?fields[products]=name,price')
->assertStatus(200);

$response->assertJsonFragment([
'name' => 'T-Shirt',
'price' => 500,
]);
});

it('throws when selecting a field that is not allowed', function () {
$this->seed(ProductsSeeder::class);

$this->get('/api/our-products?fields[products]=name,slug,price')
->assertStatus(400)
->assertJsonFragment([
'message' => 'Requested field(s) `products.slug` are not allowed.',
]);
})->throws(\Spatie\QueryBuilder\Exceptions\InvalidFieldQuery::class);

it('can return a list of products with selected sorts', function () {
$this->seed(ProductsSeeder::class);

$response = $this->get('/api/our-products?sort=-price')
->assertStatus(200);

$data = Product::all()
->sortByDesc('price')
->values()
->map(function ($product) {
return [
'name' => $product['name'],
'price' => $product['price'],
];
})
->toArray();

foreach ($data as $product) {
$response->assertJsonFragment($product);
}
});

it('throws when sorting by a field that is not allowed', function () {
$this->seed(ProductsSeeder::class);

$this->get('/api/our-products?sort=-slug')
->assertStatus(400)
->assertJsonFragment([
'message' => 'Requested sort(s) `products.slug` are not allowed.',
]);
})->throws(\Spatie\QueryBuilder\Exceptions\InvalidSortQuery::class);

it('can return a list of products with selected filters', function () {
$this->seed(ProductsSeeder::class);

$response = $this->get('/api/our-products?filter[name]=T-Shirt&filter[price]=500')
->assertStatus(200);

$response->assertJsonFragment([
'name' => 'T-Shirt',
'price' => 500,
]);
});

it('throws when filtering by a field that is not allowed', function () {
$this->seed(ProductsSeeder::class);

$this->get('/api/our-products?filter[name]=T-Shirt&filter[slug]=t-shirt')
->assertStatus(400)
->assertJsonFragment([
'message' => 'Requested filter(s) `products.slug` are not allowed.',
]);
})->throws(\Spatie\QueryBuilder\Exceptions\InvalidFilterQuery::class);

it('can return a list of products with a custom transformer', function () {
$this->seed(ProductsSeeder::class);

$response = $this->get('/api/our-products')
->assertStatus(200);

$product = Product::first();

$response->assertJsonFragment([
'hash' => md5($product['name']),
]);
});
5 changes: 0 additions & 5 deletions tests/ExampleTest.php

This file was deleted.

28 changes: 28 additions & 0 deletions tests/Fixtures/Database/Migrations/01_create_products_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();

$table->string('name');
$table->string('slug')->unique();

$table->text('description')->nullable();

$table->decimal('price', 15, 2)->default(0);

$table->timestamps();
});
}

public function down()
{
Schema::dropIfExists('products');
}
};
61 changes: 61 additions & 0 deletions tests/Fixtures/Database/Seeders/ProductsSeeder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Rupadana\ApiService\Tests\Fixtures\Database\Seeders;

use Illuminate\Database\Seeder;
use Rupadana\ApiService\Tests\Fixtures\Models\Product;

class ProductsSeeder extends Seeder
{
public function run()
{
Product::create([
'name' => 'Jeans',
'slug' => 'jeans',
'description' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'price' => 1000,
]);

Product::create([
'name' => 'T-Shirt',
'slug' => 't-shirt',
'description' => 'Donec sed odio dui. Nullam quis risus eget urna mollis ornare vel eu leo.',
'price' => 500,
]);

Product::create([
'name' => 'Shoes',
'slug' => 'shoes',
'description' => 'Maecenas faucibus mollis interdum. Nullam id dolor id nibh ultricies vehicula ut id elit.',
'price' => 2000,
]);

Product::create([
'name' => 'Hat',
'slug' => 'hat',
'description' => 'Maecenas sed diam eget risus varius blandit sit amet non magna.',
'price' => 100,
]);

Product::create([
'name' => 'Socks',
'slug' => 'socks',
'description' => 'Cras mattis consectetur purus sit amet fermentum.',
'price' => 50,
]);

Product::create([
'name' => 'Gloves',
'slug' => 'gloves',
'description' => 'Maecenas sed diam eget risus varius blandit sit amet non magna.',
'price' => 150,
]);

Product::create([
'name' => 'Jacket',
'slug' => 'jacket',
'description' => 'Maecenas sed diam eget risus varius blandit sit amet non magna.',
'price' => 200,
]);
}
}
34 changes: 34 additions & 0 deletions tests/Fixtures/Models/Product.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Rupadana\ApiService\Tests\Fixtures\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
public static array $allowedFields = [
'name',
// 'slug' is not allowed
'description',
'price',
'created_at',
];

public static array $allowedSorts = [
'name',
'price',
'created_at',
];

public static array $allowedFilters = [
'name',
'price',
'created_at',
];

protected $guarded = [];

protected $hidden = [
'slug',
];
}
Loading
Loading