REST API - Refactoring of routes file, more tests added (#3345)

* Toggles the disabled state of auto_increment_prefix

To insert a prefix you had to toggle the checkbox, save the settings and reload. With this script it is immediate. Fixes #1390

* Delete asset image: made checkbox more visible

Related to #3153

* Added personal-access-token component

* Created basic API testing configuration

* First version of /components endpoind cest

* On-the-fly bearer token generation

* Completed testing of PATCH and PUT methods

* Added /components/{id}/assets route with tests

* Updated route and dataTable in view

* Completed test assertion

* Added links to assets in ComponentsAssets view

* Linked Company in AssetView page

* Fixed purchase_cost format expectation in ApiComponentsCest

* Refactored api routes file

Sorted all prefixes in alphabetical order, removed duplicate routes. For every prefix I placed first Route::resource and then any additional route in a Route::group. Expanded arrays for readability and consistency. Removed useless calls as create and edit everywhere.

* Refactored and added one more test to ApiComponentsAssetsCest

* Marked one test as incomplete, 404 response should return json

* Fixed value expectation

* Refactored getToken()

* Added API debugging routes

* Added more information to ValidationException reporting

Now the payload contains the validation errors for each invalid
attribute.

* /apitests: refactored expectations in component assertions

* Created ApiAssetsCest

* /apitests: Cleanup in Exceptions/Handler

* Reverted change to use

* Marked two tests as incomplete, looking for solutions
This commit is contained in:
Andrea Bergamasco 2017-02-24 01:32:35 +01:00 committed by snipe
parent 9ccaad8b9c
commit c0c02eebd2
6 changed files with 727 additions and 348 deletions

View file

@ -6,6 +6,7 @@ use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use App\Helpers\Helper;
use Illuminate\Validation\ValidationException;
class Handler extends ExceptionHandler
{
@ -40,7 +41,7 @@ class Handler extends ExceptionHandler
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
@ -61,6 +62,10 @@ class Handler extends ExceptionHandler
return response()->json(Helper::formatStandardApiResponse('error', null, $className . ' not found'), 200);
}
if ($e instanceof \Illuminate\Validation\ValidationException) {
return response()->json(Helper::formatStandardApiResponse('error', $e->response['messages'], $e->getMessage(), 400));
}
if ($this->isHttpException($e)) {
$statusCode = $e->getStatusCode();

View file

@ -1,9 +1,6 @@
<?php
use Illuminate\Http\Request;
use App\Models\CheckoutRequest;
use App\Models\Location;
use App\Models\Statuslabel;
/*
|--------------------------------------------------------------------------
@ -18,273 +15,8 @@ use App\Models\Statuslabel;
Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
/*---Hardware API---*/
Route::resource('users', 'UsersController',
['names' =>
[
'index' => 'api.users.index',
'show' => 'api.users.show',
'update' => 'api.users.update',
'store' => 'api.users.store',
'destroy' => 'api.users.destroy'
],
'except' => ['edit'],
'parameters' => ['user' => 'user_id']
]
);
Route::resource('licenses', 'LicensesController',
['names' =>
[
'index' => 'api.licenses.index',
'show' => 'api.licenses.show',
'update' => 'api.licenses.update',
'store' => 'api.licenses.store',
'destroy' => 'api.licenses.destroy'
],
'except' => ['edit'],
'parameters' => ['license' => 'license_id']
]
);
Route::post('imports/process/{import_id}', [ 'as' => 'api.imports.importFile', 'uses'=> 'ImportController@process']);
Route::resource('imports', 'ImportController',
['names' =>
[
'index' => 'api.imports.index',
'show' => 'api.imports.show',
'update' => 'api.imports.update',
'store' => 'api.imports.store',
'destroy' => 'api.imports.destroy'
],
'except' => ['edit']
]
);
Route::get('models/{id}/assets',
[
'as' => 'api.models.assets',
'uses'=> 'AssetModelsController@assets'
]);
Route::resource('models', 'AssetModelsController',
['names' =>
[
'index' => 'api.models.index',
'show' => 'api.models.show',
'update' => 'api.models.update',
'store' => 'api.models.store',
'destroy' => 'api.models.destroy'
],
'except' => ['edit', 'create'],
'parameters' => ['model' => 'model_id']
]
);
Route::resource('categories', 'CategoriesController',
['names' =>
[
'index' => 'api.categories.index',
'show' => 'api.categories.asset.show',
'update' => 'api.categories.update',
'store' => 'api.categories.store',
'destroy' => 'api.categories.destroy'
],
'except' => ['edit', 'create'],
'parameters' => ['category' => 'category_id']
]
);
Route::resource('companies', 'CompaniesController',
['names' =>
[
'index' => 'api.companies.index',
'show' => 'api.companies.show',
'update' => 'api.companies.update',
'store' => 'api.companies.store',
'destroy' => 'api.companies.destroy'
],
'except' => ['edit'],
'parameters' => ['component' => 'component_id']
]
);
Route::resource('locations', 'LocationsController',
['names' =>
[
'index' => 'api.locations.index',
'show' => 'api.locations.show',
'update' => 'api.locations.update',
'store' => 'api.locations.store',
'destroy' => 'api.locations.destroy'
],
'except' => ['edit'],
'parameters' => ['locations' => 'locations_id']
]
);
Route::resource('components', 'ComponentsController',
['names' =>
[
'index' => 'api.components.index',
'show' => 'api.components.show',
'update' => 'api.components.update',
'store' => 'api.components.store',
'destroy' => 'api.components.destroy'
],
'parameters' =>
['component' => 'component_id']
]
);
Route::get('components/{id}/assets', [
'as' =>'api.components.assets',
'uses' => 'ComponentsController@getAssets',
]);
Route::resource('suppliers', 'SuppliersController',
['names' =>
[
'index' => 'api.suppliers.index',
'create' => 'api.suppliers.create',
'destroy' => 'api.suppliers.destroy'
],
'parameters' =>
['supplier' => 'supplier_id']
]
);
Route::resource('groups', 'GroupsController',
['names' =>
[
'index' => 'api.groups.index',
'create' => 'api.groups.create',
'store' => 'api.groups.store',
'destroy' => 'api.groups.destroy'
],
'parameters' =>
['group' => 'group_id']
]
);
Route::resource('depreciations', 'DepreciationsController',
['names' =>
[
'index' => 'api.depreciations.index',
'create' => 'api.depreciations.create',
'destroy' => 'api.depreciations.destroy'
],
'parameters' =>
['depreciation' => 'depreciation_id']
]
);
Route::resource('users', 'UsersController',
['names' =>
[
'index' => 'api.users.index',
'create' => 'api.users.create',
'destroy' => 'api.users.destroy'
],
'parameters' =>
['user' => 'user_id']
]
);
Route::resource('settings', 'SettingsController',
['names' =>
[
'index' => 'api.settings.index',
'create' => 'api.settings.create'
],
'parameters' =>
['setting' => 'setting_id']
]
);
/*---Status Label API---*/
Route::group([ 'prefix' => 'statuslabels'], function () {
Route::get('{id}/assetlist',
[ 'as' => 'api.statuslabels.assets', 'uses' => 'StatuslabelsController@assets' ]);
Route::get('{id}/deployable',
[ 'as' => 'api.statuslabels.deployable', 'uses' => 'StatuslabelsController@checkIfDeployable' ]);
// Pie chart for dashboard
Route::get('assets', [ 'as' => 'api.statuslabels.assets.bytype', 'uses' => 'StatuslabelsController@getAssetCountByStatuslabel' ]);
});
Route::resource('statuslabels', 'StatuslabelsController',
['names' =>
[
'index' => 'api.statuslabels.index',
'create' => 'api.statuslabels.create',
'destroy' => 'api.statuslabels.destroy'
],
'parameters' =>
['statuslabel' => 'statuslabel_id']
]
);
Route::resource('consumables', 'ConsumablesController',
['names' =>
[
'index' => 'api.consumables.index',
'create' => 'api.consumables.create',
'show' => 'api.consumables.show',
'destroy' => 'api.consumables.destroy'
],
'parameters' =>
['consumable' => 'consumable_id']
]
);
Route::resource('manufacturers', 'ManufacturersController',
['names' =>
[
'index' => 'api.manufacturers.index',
'show' => 'api.manufacturers.show',
'update' => 'api.manufacturers.update',
'store' => 'api.manufacturers.store',
'destroy' => 'api.manufacturers.destroy'
],
'except' => ['edit'],
'parameters' => ['manufacturer' => 'manufacturer_id']
]
);
Route::group([ 'prefix' => 'accessories' ], function () {
Route::match(['DELETE'], '{id}', ['uses' => 'AccessoriesController@destroy','as' => 'api.accessories.destroy']);
Route::get(
'{id}/checkedout',
[ 'as' => 'api.accessories.checkedout', 'uses' => 'AccessoriesController@checkedout' ]
);
});
/*--- Accessories API ---*/
Route::resource('accessories', 'AccessoriesController',
['names' =>
@ -295,72 +27,478 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
'store' => 'api.accessories.store',
'destroy' => 'api.accessories.destroy'
],
'except' => ['edit'],
'except' => ['create', 'edit'],
'parameters' => ['accessory' => 'accessory_id']
]
); // Accessories resource
Route::group(['prefix' => 'accessories'], function () {
Route::get('{accessory}/checkedout',
[
'as' => 'api.accessories.checkedout',
'uses' => 'AccessoriesController@checkedout'
]
);
}); // Accessories group
/*--- Categories API ---*/
Route::resource('categories', 'CategoriesController',
[
'names' =>
[
'index' => 'api.categories.index',
'show' => 'api.categories.show',
'store' => 'api.categories.store',
'update' => 'api.categories.update',
'destroy' => 'api.categories.destroy'
],
'except' => ['edit', 'create'],
'parameters' => ['category' => 'category_id']
]
); // Categories resource
/*--- Companies API ---*/
Route::resource('companies', 'CompaniesController',
[
'names' =>
[
'index' => 'api.companies.index',
'show' => 'api.companies.show',
'store' => 'api.companies.store',
'update' => 'api.companies.update',
'destroy' => 'api.companies.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['component' => 'component_id']
]
); // Companies resource
/*--- Components API ---*/
Route::resource('components', 'ComponentsController',
[
'names' =>
[
'index' => 'api.components.index',
'show' => 'api.components.show',
'store' => 'api.components.store',
'update' => 'api.components.update',
'destroy' => 'api.components.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['component' => 'component_id']
]
); // Components resource
Route::group(['prefix' => 'components'], function () {
Route::get('{component}/assets',
[
'as' =>'api.components.assets',
'uses' => 'ComponentsController@getAssets',
]
);
}); // Components group
/*--- Consumables API ---*/
Route::resource('consumables', 'ConsumablesController',
[
'names' =>
[
'index' => 'api.consumables.index',
'show' => 'api.consumables.show',
'store' => 'api.consumables.store',
'update' => 'api.consumables.update',
'destroy' => 'api.consumables.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['consumable' => 'consumable_id']
]
); // Consumables resource
/*--- Depreciations API ---*/
Route::resource('depreciations', 'DepreciationsController',
[
'names' =>
[
'index' => 'api.depreciations.index',
'show' => 'api.depreciations.show',
'store' => 'api.depreciations.store',
'update' => 'api.depreciations.update',
'destroy' => 'api.depreciations.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['depreciation' => 'depreciation_id']
]
); // Depreciations resource
/*--- Fields API ---*/
Route::group(['prefix' => 'fields'], function () {
Route::post('fieldsets/{id}/order',
[
'as' => 'api.customfields.order',
'uses' => 'CustomFieldsController@postReorder'
]
);
}); // Fields group
/*--- Groups API ---*/
Route::resource('groups', 'GroupsController',
[
'names' =>
[
'index' => 'api.groups.index',
'show' => 'api.groups.show',
'store' => 'api.groups.store',
'update' => 'api.groups.update',
'destroy' => 'api.groups.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['group' => 'group_id']
]
); // Groups resource
/*--- Hardware API ---*/
Route::resource('hardware', 'AssetsController',
[
'names' =>
[
'index' => 'api.assets.index',
'show' => 'api.assets.show',
'store' => 'api.assets.store',
'update' => 'api.assets.update',
'destroy' => 'api.assets.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['asset' => 'asset_id']
]
); // Hardware resource
/*--- Imports API ---*/
Route::resource('imports', 'ImportController',
[
'names' =>
[
'index' => 'api.imports.index',
'show' => 'api.imports.show',
'store' => 'api.imports.store',
'update' => 'api.imports.update',
'destroy' => 'api.imports.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['import' => 'import_id']
]
); // Imports resource
Route::group(['prefix' => 'imports'], function () {
Route::post('process/{import}',
[
'as' => 'api.imports.importFile',
'uses'=> 'ImportController@process'
]
);
}); // Imports group
/*--- Licenses API ---*/
Route::resource('licenses', 'LicensesController',
[
'names' =>
[
'index' => 'api.licenses.index',
'show' => 'api.licenses.show',
'store' => 'api.licenses.store',
'update' => 'api.licenses.update',
'destroy' => 'api.licenses.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['license' => 'license_id']
]
); // Licenses resource
/*--- Locations API ---*/
Route::resource('locations', 'LocationsController',
[
'names' =>
[
'index' => 'api.locations.index',
'show' => 'api.locations.show',
'store' => 'api.locations.store',
'update' => 'api.locations.update',
'destroy' => 'api.locations.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['location' => 'location_id']
]
); // Locations resource
Route::group(['prefix' => 'locations'], function () {
Route::get('{location}/users',
[
'as'=>'api.locations.viewusers',
'uses'=>'LocationsController@getDataViewUsers'
]
);
Route::get('{location}/assets',
[
'as'=>'api.locations.viewassets',
'uses'=>'LocationsController@getDataViewAssets'
]
);
// Do we actually still need this, now that we have an API?
Route::get('{location}/check',
[
'as' => 'api.locations.check',
'uses' => 'LocationsController@show'
]
);
}); // Locations group
/*--- Manufacturers API ---*/
Route::resource('manufacturers', 'ManufacturersController',
[
'names' =>
[
'index' => 'api.manufacturers.index',
'show' => 'api.manufacturers.show',
'store' => 'api.manufacturers.store',
'update' => 'api.manufacturers.update',
'destroy' => 'api.manufacturers.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['manufacturer' => 'manufacturer_id']
]
); // Manufacturers resource
/*--- Models API ---*/
Route::resource('models', 'AssetModelsController',
[
'names' =>
[
'index' => 'api.models.index',
'show' => 'api.models.show',
'store' => 'api.models.store',
'update' => 'api.models.update',
'destroy' => 'api.models.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['model' => 'model_id']
]
); // Models resource
Route::group(['prefix' => 'models'], function () {
Route::get('assets',
[
'as' => 'api.models.assets',
'uses'=> 'AssetModelsController@assets'
]
);
}); // Models group
/*--- Settings API ---*/
Route::resource('settings', 'SettingsController',
[
'names' =>
[
'index' => 'api.settings.index',
'store' => 'api.settings.store',
'show' => 'api.settings.show',
'update' => 'api.settings.update'
],
'except' => ['create', 'edit', 'destroy'],
'parameters' => ['setting' => 'setting_id']
]
); // Settings resource
/*--- Status Labels API ---*/
Route::resource('statuslabels', 'StatuslabelsController',
[
'names' =>
[
'index' => 'api.statuslabels.index',
'store' => 'api.statuslabels.store',
'show' => 'api.statuslabels.show',
'update' => 'api.statuslabels.update',
'destroy' => 'api.statuslabels.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['statuslabel' => 'statuslabel_id']
]
);
Route::group(['prefix' => 'statuslabels'], function () {
/*---Hardware API---*/
Route::match(['DELETE'], 'hardware/{id}', ['uses' => 'AssetsController@destroy','as' => 'api.assets.destroy']);
Route::resource('hardware', 'AssetsController',
['names' =>
Route::get('{statuslabel}/assetlist',
[
'index' => 'api.assets.index',
'create' => 'api.assets.create',
'destroy' => 'api.assets.destroy'
],
'parameters' =>
['asset' => 'asset_id']
]);
/*---Locations API---*/
Route::group(array('prefix'=>'locations'), function () {
Route::get('{locationID}/users', array('as'=>'api.locations.viewusers', 'uses'=>'LocationsController@getDataViewUsers'));
Route::get('{locationID}/assets', array('as'=>'api.locations.viewassets', 'uses'=>'LocationsController@getDataViewAssets'));
// Do we actually still need this, now that we have an API?
Route::get('{id}/check',
[ 'as' => 'api.locations.check', 'uses' => 'LocationsController@show' ]);
});
/*---Suppliers API---*/
Route::group(array('prefix'=>'suppliers'), function () {
Route::get('list', array('as'=>'api.suppliers.list', 'uses'=>'SuppliersController@getDatatable'));
});
/*---Users API---*/
Route::group([ 'prefix' => 'users' ], function () {
Route::post('/', [ 'as' => 'api.users.store', 'uses' => 'UsersController@store' ]);
Route::post('two_factor_reset', [ 'as' => 'api.users.two_factor_reset', 'uses' => 'UsersController@postTwoFactorReset' ]);
Route::get('list/{status?}', [ 'as' => 'api.users.list', 'uses' => 'UsersController@getDatatable' ]);
Route::get('{userId}/assets', [ 'as' => 'api.users.assetlist', 'uses' => 'UsersController@getAssetList' ]);
Route::post('{userId}/upload', [ 'as' => 'upload/user', 'uses' => 'UsersController@postUpload' ]);
});
Route::group([ 'prefix' => 'fields' ], function () {
Route::post(
'fieldsets/{id}/order',
[ 'as' => 'api.customfields.order', 'uses' => 'CustomFieldsController@postReorder' ]
'as' => 'api.statuslabels.assets',
'uses' => 'StatuslabelsController@assets'
]
);
Route::get('{statuslabel}/deployable',
[
'as' => 'api.statuslabels.deployable',
'uses' => 'StatuslabelsController@checkIfDeployable'
]
);
// Pie chart for dashboard
Route::get('assets',
[
'as' => 'api.statuslabels.assets.bytype',
'uses' => 'StatuslabelsController@getAssetCountByStatuslabel'
]
);
}); // Status labels group
/*--- Suppliers API ---*/
Route::resource('suppliers', 'SuppliersController',
[
'names' =>
[
'index' => 'api.suppliers.index',
'show' => 'api.suppliers.show',
'store' => 'api.suppliers.store',
'update' => 'api.suppliers.update',
'destroy' => 'api.suppliers.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['supplier' => 'supplier_id']
]
); // Suppliers resource
Route::group(['prefix' => 'suppliers'], function () {
Route::get('list',
[
'as'=>'api.suppliers.list',
'uses'=>'SuppliersController@getDatatable'
]
);
}); // Suppliers group
/*--- Users API ---*/
Route::resource('users', 'UsersController',
[
'names' =>
[
'index' => 'api.users.index',
'show' => 'api.users.show',
'store' => 'api.users.store',
'update' => 'api.users.update',
'destroy' => 'api.users.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['user' => 'user_id']
]
); // Users resource
Route::group([ 'prefix' => 'users' ], function () {
Route::post('two_factor_reset',
[
'as' => 'api.users.two_factor_reset',
'uses' => 'UsersController@postTwoFactorReset'
]
);
Route::get('list/{status?}',
[
'as' => 'api.users.list',
'uses' => 'UsersController@getDatatable'
]
);
Route::get('{user}/assets',
[
'as' => 'api.users.assetlist',
'uses' => 'UsersController@getAssetList'
]
);
Route::post('{user}/upload',
[
'as' => 'api.users.uploads',
'uses' => 'UsersController@postUpload'
]
);
}); // Users group
### DEBUG ROUTES ###
Route::group(['prefix' => 'me'], function () {
if (env('APP_ENV') == 'production') {
abort(404);
}
Route::get('/profile', function () {
return json_encode([
'name' => \Auth::user()->first_name . ' ' . \Auth::user()->last_name,
'email' => \Auth::user()->email,
]);
});
Route::get('/authenticated', function () {
return json_encode([
'authenticated' => \Auth::check()
]);
});
Route::get('/permissions/{scope}/{action}', function ($scope, $action) {
return json_encode([
'permission' => $scope . '.' . $action,
'authorized' => \Auth::user()->hasAccess($scope . '.' . $action),
]);
});
Route::get('/permissions', function () {
return json_encode([
'permissions' => Auth::user()->permissions
]);
});
});
});

View file

@ -24,23 +24,23 @@ class ApiTester extends \Codeception\Actor
* Define custom actions here
*/
public function getToken(\App\Models\User $user)
{
$client_repository = new \Laravel\Passport\ClientRepository();
$client = $client_repository->createPersonalAccessClient($user->id, 'Codeception API Test Client',
public function getToken(\App\Models\User $user)
{
$client_repository = new \Laravel\Passport\ClientRepository();
$client = $client_repository->createPersonalAccessClient($user->id, 'Codeception API Test Client',
'http://localhost/');
\Illuminate\Support\Facades\DB::table('oauth_personal_access_clients')->insert([
\Illuminate\Support\Facades\DB::table('oauth_personal_access_clients')->insert([
'client_id' => $client->id,
'created_at' => new DateTime,
'updated_at' => new DateTime,
]);
]);
$user->permissions = json_encode(['superuser' => true]);
$user->save();
$user->permissions = json_encode(['superuser' => true]);
$user->save();
$token = $user->createToken('CodeceptionAPItestToken')->accessToken;
$token = $user->createToken('CodeceptionAPItestToken')->accessToken;
return $token;
}
return $token;
}
}

210
tests/api/ApiAssetsCest.php Normal file
View file

@ -0,0 +1,210 @@
<?php
use Illuminate\Support\Facades\Auth;
class ApiAssetsCest
{
protected $faker;
protected $user;
public function _before(ApiTester $I)
{
$this->faker = \Faker\Factory::create();
$this->user = \App\Models\User::find(1);
$I->amBearerAuthenticated($I->getToken($this->user));
}
/** @test */
public function indexAssets(ApiTester $I)
{
$I->wantTo('Get a list of assets');
// setup
$assets = factory(\App\Models\Asset::class, 'asset', 10)->create([
'user_id' => $this->user->id,
]);
// call
$I->sendGET('/hardware');
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$response = json_decode($I->grabResponse());
// sample verify
$asset = $assets->random();
$I->seeResponseContainsJson([
'id' => $asset->id,
'name' => e($asset->name),
'asset_tag' => $asset->asset_tag,
'serial' => $asset->serial,
'model' => [
'id' => $asset->model_id,
'name' => e($asset->model->name),
],
// TODO: model_label
'last_checkout' => $asset->last_checkout,
// TODO: category [id, name]
// TODO: manufacturer [id, name]
'notes' => $asset->notes,
'expected_checkin' => $asset->expected_checkin,
'order_number' => $asset->order_number,
'company' => [
'id' => $asset->company->id,
'name' => $asset->company->name,
],
// TODO: location [id, name]
// TODO: rtd_location [id, name]
'image' => $asset->image,
'assigned_to' => $asset->assigned_to,
'warranty' => $asset->warranty,
'warranty_expires' => $asset->warranty_expires,
// TODO: created_at
'purchase_date' => $asset->purchase_date->format('Y-m-d'),
'purchase_cost' => \App\Helpers\Helper::formatCurrencyOutput($asset->purchase_cost),
// TODO: can_checkout
// TODO: available actions
]);
}
/** @test */
public function createAsset(ApiTester $I, $scenario)
{
$I->wantTo('Create a new asset');
$temp_asset = factory(\App\Models\Asset::class, 'asset')->make();
// setup
$data = [
'asset_tag' => $temp_asset->tag,
'assigned_to' => $temp_asset->assigned_to,
'company_id' => $temp_asset->company->id,
'image' => $temp_asset->image,
'model_id' => $temp_asset->model_id,
'name' => $temp_asset->name,
'notes' => $temp_asset->notes,
'purchase_cost' => $temp_asset->purchase_cost,
'purchase_date' => $temp_asset->purchase_date,
'rtd_location_id' => $temp_asset->rtd_location_id,
'serial' => $temp_asset->serial,
'status_id' => $temp_asset->status_id,
'supplier_id' => $temp_asset->supplier_id,
'warranty_months' => $temp_asset->warranty_months,
];
$scenario->incomplete('When I POST to /hardware i am redirected to html login page 😰');
// create
$I->sendPOST('/hardware', $data);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
}
/** @test */
public function updateAssetWithPatch(ApiTester $I, $scenario)
{
$I->wantTo('Update an asset with PATCH');
// create and store an asset
$asset = factory(\App\Models\Asset::class, 'asset')->create();
$I->assertInstanceOf(\App\Models\Asset::class, $asset);
// create a temporary asset to grab new data
$temp_asset = factory(\App\Models\Asset::class, 'asset')->make();
$data = [
'asset_tag' => $temp_asset->tag,
'assigned_to' => $temp_asset->assigned_to,
'company_id' => $temp_asset->company->id,
'image' => $temp_asset->image,
'model_id' => $temp_asset->model_id,
'name' => $temp_asset->name,
'notes' => $temp_asset->notes,
'purchase_cost' => $temp_asset->purchase_cost,
'purchase_date' => $temp_asset->purchase_date->format('Y-m-d'),
'rtd_location_id' => $temp_asset->rtd_location_id,
'serial' => $temp_asset->serial,
'status_id' => $temp_asset->status_id,
'supplier_id' => $temp_asset->supplier_id,
'warranty_months' => $temp_asset->warranty_months,
];
// the asset name should be different
$I->assertNotEquals($asset->name, $data['name']);
// update
$I->sendPATCH('/hardware/' . $asset->id, $data);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$response = json_decode($I->grabResponse());
$I->assertEquals('success', $response->status);
// verify
$scenario->incomplete('[BadMethodCallException] Call to undefined method Illuminate\Database\Query\Builder::detail() 🤔');
$I->sendGET('/hardware/' . $asset->id);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$I->seeResponseContainsJson([
'name' => $data['name'],
'id' => $asset->id,
]);
}
/** @test */
/* public function updateAssetWithPut(ApiTester $I)
{
$I->wantTo('Update a asset with PUT');
// create
$asset = factory(\App\Models\Asset::class, 'asset')->create();
$I->assertInstanceOf(\App\Models\Asset::class, $asset);
$data = [
'name' => $this->faker->sentence(3),
];
$I->assertNotEquals($asset->name, $data['name']);
// update
$I->sendPUT('/hardware/' . $asset->id, $data);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$response = json_decode($I->grabResponse());
$I->assertEquals('success', $response->status);
// verify
$I->sendGET('/hardware/' . $asset->id);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$I->seeResponseContainsJson([
'name' => e($data['name']),
'id' => e($asset->id),
'qty' => e($asset->qty),
]);
}
/** @test */
/* public function deleteAssetTest(ApiTester $I, $scenario)
{
$I->wantTo('Delete an asset');
// create
$asset = factory(\App\Models\Asset::class, 'asset')->create();
$I->assertInstanceOf(\App\Models\Asset::class, $asset);
// delete
$I->sendDELETE('/hardware/' . $asset->id);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
// verify, expect a 404
$I->sendGET('/hardware/' . $asset->id);
$I->seeResponseCodeIs(404);
// $I->seeResponseIsJson(); // @todo: response is not JSON
$scenario->incomplete('404 response should be JSON, receiving HTML instead');
} // */
}

View file

@ -18,10 +18,11 @@ class ApiComponentsAssetsCest
{
$I->wantTo('Get a list of assets related to a component');
// generate
// generate component
$component = factory(\App\Models\Component::class, 'component')
->create(['user_id' => $this->user->id, 'qty' => 20]);
// generate assets and associate component
$assets = factory(\App\Models\Asset::class, 'asset', 2)
->create(['user_id' => $this->user->id])
->each(function ($asset) use ($component) {
@ -34,12 +35,14 @@ class ApiComponentsAssetsCest
]);
});
// verify
$I->sendGET('/components/' . $component->id . '/assets/');
$I->seeResponseIsJson();
$I->seeResponseCodeIs(200);
$response = json_decode($I->grabResponse());
$response = json_decode($I->grabResponse());
$I->assertEquals(2, $response->total);
$I->assertInstanceOf(\Illuminate\Database\Eloquent\Collection::class, $assets);
$I->seeResponseContainsJson(['rows' => [
@ -56,4 +59,22 @@ class ApiComponentsAssetsCest
]
]);
}
/** @test */
public function expectEmptyResponseWithoutAssociatedAssets(ApiTester $I, $scenario)
{
$I->wantTo('See an empty response when there are no associated assets to a component');
$component = factory(\App\Models\Component::class, 'component')
->create(['user_id' => $this->user->id, 'qty' => 20]);
$I->sendGET('/components/' . $component->id . '/assets');
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$response = json_decode($I->grabResponse());
$I->assertEquals(0, $response->total);
$I->assertEquals([], $response->rows);
$I->seeResponseContainsJson(['total' => 0, 'rows' => []]);
}
}

View file

@ -21,7 +21,7 @@ class ApiComponentsCest
$I->wantTo('Get a list of components');
// setup
$components = factory(\App\Models\Component::class, 'component', 10)->create(['user_id' => $this->user->id]);
$components = factory(\App\Models\Component::class, 'component', 10)->create();
// call
$I->sendGET('/components');
@ -34,6 +34,10 @@ class ApiComponentsCest
'name' => $component->name,
'qty' => $component->qty,
]);
$I->seeResponseContainsJson([
'total' => \App\Models\Component::count(),
]);
}
/** @test */
@ -74,19 +78,19 @@ class ApiComponentsCest
'id' => $id,
'category' => [
'id' => $data['category_id'],
'name' => $category->name,
'name' => e($category->name),
],
'company' => [
'id' => $data['company_id'],
'name' => $company->name,
'name' => e($company->name),
],
'location' => [
'id' => $data['location_id'],
'name' => $location->name,
'name' => e($location->name),
],
'name' => $data['name'],
'qty' => $data['qty'],
'purchase_cost' => $data['purchase_cost'],
'purchase_cost' => \App\Helpers\Helper::formatCurrencyOutput($data['purchase_cost']),
'purchase_date' => $data['purchase_date'],
]);
}
@ -161,7 +165,7 @@ class ApiComponentsCest
}
/** @test */
public function deleteComponentTest(ApiTester $I)
public function deleteComponentTest(ApiTester $I, $scenario)
{
$I->wantTo('Delete a component');
@ -178,5 +182,6 @@ class ApiComponentsCest
$I->sendGET('/components/' . $component->id);
$I->seeResponseCodeIs(404);
// $I->seeResponseIsJson(); // @todo: response is not JSON
$scenario->incomplete('404 response should be JSON, receiving HTML instead');
}
}