From d54dda40d38b6b6d868d2e5e3a43dd65d307f9f2 Mon Sep 17 00:00:00 2001 From: Daniel Meltzer Date: Sat, 24 Feb 2018 15:03:33 -0500 Subject: [PATCH 1/6] Functional Tests Improvements (#5095) * Rely on laravel transactions instead of refreshing the database dump between functional test runs. Cuts functional test runtime by 75%. Also use mysql to seed directly. * Split functional tests into two groups on travis to reduce overall memory usage. Any new tests will need to be added to one of these two files before they are run on travis. running all functional tests simultaneously still works locally. * Fix name of test in group. --- .travis.yml | 4 ++-- tests/_envs/functional-travis.yml | 3 ++- tests/functional.suite.yml | 6 +++++- tests/functional/CategoriesCest.php | 2 +- tests/functional/ManufacturersCest.php | 2 +- tests/functional/func-part-1.txt | 8 ++++++++ tests/functional/func-part-2.txt | 7 +++++++ 7 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 tests/functional/func-part-1.txt create mode 100644 tests/functional/func-part-2.txt diff --git a/.travis.yml b/.travis.yml index de5c044794..6bb7ca309f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,8 +51,8 @@ before_script: script: - ./vendor/bin/codecept run unit # - ./vendor/bin/codecept run acceptance --env=testing-ci - - ./vendor/bin/codecept run functional --env=functional-travis -#script: ./vendor/bin/codecept run + - ./vendor/bin/codecept run functional --env=functional-travis -g func1 + - ./vendor/bin/codecept run functional --env=functional-travis -g func2 - ./vendor/bin/codecept run api --env=functional-travis after_script: diff --git a/tests/_envs/functional-travis.yml b/tests/_envs/functional-travis.yml index 937a3cb7b1..209a222a43 100644 --- a/tests/_envs/functional-travis.yml +++ b/tests/_envs/functional-travis.yml @@ -8,4 +8,5 @@ modules: Db: dsn: 'mysql:host=localhost;dbname=snipeit_unit' user: 'travis' - password: '' \ No newline at end of file + password: '' + populator: 'mysql -u travis snipeit_unit < tests/_data/dump.sql' diff --git a/tests/functional.suite.yml b/tests/functional.suite.yml index fbbb9d42e9..82387f50b9 100644 --- a/tests/functional.suite.yml +++ b/tests/functional.suite.yml @@ -18,6 +18,10 @@ modules: password: '' dump: tests/_data/dump.sql populate: true - cleanup: true + populator: 'mysql -u snipeit_laravel snipeittests < tests/_data/dump.sql' + cleanup: false - REST: depends: Laravel5 +groups: + func1: tests/functional/func-part-1.txt + func2: tests/functional/func-part-2.txt diff --git a/tests/functional/CategoriesCest.php b/tests/functional/CategoriesCest.php index 002fea21d6..6f054b5dd9 100644 --- a/tests/functional/CategoriesCest.php +++ b/tests/functional/CategoriesCest.php @@ -58,7 +58,7 @@ class CategoriesCest { $I->wantTo('Ensure I can delete a category'); $category = factory(App\Models\Category::class)->states('asset-laptop-category')->create([ - 'name'=>"Test Category" + 'name'=>"Deletable Test Category" ]); $I->sendDelete(route('categories.destroy', $category->id), ['_token' => csrf_token()]); $I->seeResponseCodeIs(200); diff --git a/tests/functional/ManufacturersCest.php b/tests/functional/ManufacturersCest.php index 322dc93f58..8c34badd1a 100644 --- a/tests/functional/ManufacturersCest.php +++ b/tests/functional/ManufacturersCest.php @@ -59,7 +59,7 @@ class ManufacturersCest public function allowsDelete(FunctionalTester $I) { $I->wantTo('Ensure I can delete a manufacturer'); - $manufacturerId = factory(App\Models\Manufacturer::class)->states('microsoft')->create(['name' => "Test Manufacturer"])->id; + $manufacturerId = factory(App\Models\Manufacturer::class)->states('microsoft')->create(['name' => "Deletable Test Manufacturer"])->id; $I->sendDelete(route('manufacturers.destroy', $manufacturerId), ['_token' => csrf_token()]); $I->seeResponseCodeIs(200); } diff --git a/tests/functional/func-part-1.txt b/tests/functional/func-part-1.txt new file mode 100644 index 0000000000..7409642049 --- /dev/null +++ b/tests/functional/func-part-1.txt @@ -0,0 +1,8 @@ +tests/functional/AccessoriesCest.php +tests/functional/AssetModelsCest.php +tests/functional/AssetsCest.php +tests/functional/CategoriesCest.php +tests/functional/CompaniesCest.php +tests/functional/ComponentsCest.php +tests/functional/ConsumablesCest.php +tests/functional/DepreciationsCest.php diff --git a/tests/functional/func-part-2.txt b/tests/functional/func-part-2.txt new file mode 100644 index 0000000000..3aa2d054c8 --- /dev/null +++ b/tests/functional/func-part-2.txt @@ -0,0 +1,7 @@ +tests/functional/GroupsCest.php +tests/functional/LicensesCest.php +tests/functional/LocationsCest.php +tests/functional/ManufacturersCest.php +tests/functional/StatusLabelsCest.php +tests/functional/SuppliersCest.php +tests/functional/UsersCest.php From b6a75093b74d540f443195e5786d3f86ddf9eac0 Mon Sep 17 00:00:00 2001 From: snipe Date: Sat, 24 Feb 2018 14:06:02 -0800 Subject: [PATCH 2/6] Removed duplicate location_id assignment --- app/Http/Controllers/Api/AssetsController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index 6d8c52d728..85019ad903 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -636,8 +636,6 @@ class AssetsController extends Controller $asset->location_id = $request->input('location_id'); } - $asset->location_id = $asset->rtd_location_id; - if (Input::has('status_id')) { $asset->status_id = e(Input::get('status_id')); } From 7de8f71f58d3538c8240a83593cec8186a5720e6 Mon Sep 17 00:00:00 2001 From: Daniel Meltzer Date: Sat, 24 Feb 2018 22:01:34 -0500 Subject: [PATCH 3/6] Api tests (#5096) * Use the formated date helper to clean up verifications. * Add Checkin/Checkout api tests. * Accessories api test * Add Companies API Test. * Return ModelNotFound as a 404. * Cleanups/simplficiations/updates. * Locations api test. * currency and image should be fillable on location. * Update components api test. * Use findOrFail so we return a 404 instead of a 200. Matches other item types. * order_number should be fillable in component. * Add updated_at and permissions to information returned from api for a user. * Add users test and flesh out factory and fillable fields. * Add test for assets method * API status label test. * Disable php7.2 for now on travis until the count(null) issues are remedied * Add serial to update. * API model not found should return a 200 --- .travis.yml | 2 +- .../Controllers/Api/ComponentsController.php | 4 +- .../Api/StatuslabelsController.php | 2 +- app/Http/Controllers/Api/UsersController.php | 42 +-- .../Transformers/AccessoriesTransformer.php | 2 +- app/Models/Component.php | 3 +- app/Models/Consumable.php | 2 + app/Models/Location.php | 14 +- app/Models/Statuslabel.php | 8 +- app/Models/User.php | 29 +- database/factories/UserFactory.php | 25 +- tests/api/ApiAccessoriesCest.php | 154 +++++++++ tests/api/ApiAssetsCest.php | 326 +----------------- tests/api/ApiCheckoutAssetsCest.php | 141 ++++++++ tests/api/ApiCompaniesCest.php | 133 +++++++ tests/api/ApiComponentsCest.php | 184 ++++------ tests/api/ApiConsumablesCest.php | 154 +++++++++ tests/api/ApiLocationsCest.php | 145 ++++++++ tests/api/ApiStatusLabelsCest.php | 143 ++++++++ tests/api/ApiUsersCest.php | 190 ++++++++++ 20 files changed, 1226 insertions(+), 477 deletions(-) create mode 100644 tests/api/ApiAccessoriesCest.php create mode 100644 tests/api/ApiCheckoutAssetsCest.php create mode 100644 tests/api/ApiCompaniesCest.php create mode 100644 tests/api/ApiConsumablesCest.php create mode 100644 tests/api/ApiLocationsCest.php create mode 100644 tests/api/ApiStatusLabelsCest.php create mode 100644 tests/api/ApiUsersCest.php diff --git a/.travis.yml b/.travis.yml index 6bb7ca309f..dbbc2f502d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ services: php: - 5.6 - 7.0 - - 7.2 + # - 7.2 DISABLE Temporarily until we fix the count(null) bugs - 7.1.4 # execute any number of scripts before the test run, custom env's are available as variables diff --git a/app/Http/Controllers/Api/ComponentsController.php b/app/Http/Controllers/Api/ComponentsController.php index 8e557df6a1..4b18a259e2 100644 --- a/app/Http/Controllers/Api/ComponentsController.php +++ b/app/Http/Controllers/Api/ComponentsController.php @@ -93,13 +93,11 @@ class ComponentsController extends Controller public function show($id) { $this->authorize('view', Component::class); - $component = Component::find($id); + $component = Component::findOrFail($id); if ($component) { return (new ComponentsTransformer)->transformComponent($component); } - - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.does_not_exist'))); } diff --git a/app/Http/Controllers/Api/StatuslabelsController.php b/app/Http/Controllers/Api/StatuslabelsController.php index 29748b9c22..a3c698fa7c 100644 --- a/app/Http/Controllers/Api/StatuslabelsController.php +++ b/app/Http/Controllers/Api/StatuslabelsController.php @@ -56,7 +56,7 @@ class StatuslabelsController extends Controller $request->except('deployable', 'pending','archived'); if (!$request->has('type')) { - return response()->json(Helper::formatStandardApiResponse('error', null, ["type" => ["Status label type is required."]])); + return response()->json(Helper::formatStandardApiResponse('error', null, ["type" => ["Status label type is required."]]),500); } $statuslabel = new Statuslabel; diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index f5b7ce08e4..d4d0339f09 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -28,30 +28,32 @@ class UsersController extends Controller $this->authorize('view', User::class); $users = User::select([ - 'users.id', - 'users.employee_num', - 'users.two_factor_enrolled', - 'users.jobtitle', - 'users.email', - 'users.phone', + 'users.activated', 'users.address', + 'users.avatar', 'users.city', - 'users.state', - 'users.country', - 'users.zip', - 'users.username', - 'users.location_id', - 'users.manager_id', - 'users.first_name', - 'users.last_name', - 'users.created_at', - 'users.notes', 'users.company_id', - 'users.last_login', + 'users.country', + 'users.created_at', 'users.deleted_at', 'users.department_id', - 'users.activated', - 'users.avatar', + 'users.email', + 'users.employee_num', + 'users.first_name', + 'users.id', + 'users.jobtitle', + 'users.last_login', + 'users.last_name', + 'users.location_id', + 'users.manager_id', + 'users.notes', + 'users.permissions', + 'users.phone', + 'users.state', + 'users.two_factor_enrolled', + 'users.updated_at', + 'users.username', + 'users.zip', ])->with('manager', 'groups', 'userloc', 'company', 'department','assets','licenses','accessories','consumables') ->withCount('assets','licenses','accessories','consumables'); @@ -69,7 +71,7 @@ class UsersController extends Controller if ($request->has('location_id')) { $users = $users->where('users.location_id', '=', $request->input('location_id')); } - + if ($request->has('group_id')) { $users = $users->ByGroup($request->get('group_id')); } diff --git a/app/Http/Transformers/AccessoriesTransformer.php b/app/Http/Transformers/AccessoriesTransformer.php index b4143ec584..fd6832e00c 100644 --- a/app/Http/Transformers/AccessoriesTransformer.php +++ b/app/Http/Transformers/AccessoriesTransformer.php @@ -32,7 +32,7 @@ class AccessoriesTransformer 'notes' => ($accessory->notes) ? e($accessory->notes) : null, 'qty' => ($accessory->qty) ? (int) $accessory->qty : null, 'purchase_date' => ($accessory->purchase_date) ? Helper::getFormattedDateObject($accessory->purchase_date, 'date') : null, - 'purchase_cost' => ($accessory->purchase_cost) ? e($accessory->purchase_cost) : null, + 'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost), 'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null, 'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null, 'remaining_qty' => $accessory->numRemaining(), diff --git a/app/Models/Component.php b/app/Models/Component.php index 21b794c71b..38610277bb 100644 --- a/app/Models/Component.php +++ b/app/Models/Component.php @@ -54,8 +54,9 @@ class Component extends SnipeModel 'purchase_cost', 'purchase_date', 'min_amt', + 'order_number', 'qty', - 'serial' + 'serial', ]; public function location() diff --git a/app/Models/Consumable.php b/app/Models/Consumable.php index 564d9d80bc..26c8b615fa 100644 --- a/app/Models/Consumable.php +++ b/app/Models/Consumable.php @@ -50,10 +50,12 @@ class Consumable extends SnipeModel protected $fillable = [ 'category_id', 'company_id', + 'item_no', 'location_id', 'manufacturer_id', 'name', 'order_number', + 'model_number', 'purchase_cost', 'purchase_date', 'qty', diff --git a/app/Models/Location.php b/app/Models/Location.php index 75b1b86289..ce6d06906e 100755 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -45,7 +45,19 @@ class Location extends SnipeModel * * @var array */ - protected $fillable = ['name','parent_id','address','address2','city','state', 'country','zip','ldap_ou']; + protected $fillable = [ + 'name', + 'parent_id', + 'address', + 'address2', + 'city', + 'state', + 'country', + 'zip', + 'ldap_ou', + 'currency', + 'image', + ]; protected $hidden = ['user_id']; public function users() diff --git a/app/Models/Statuslabel.php b/app/Models/Statuslabel.php index bfaba26c49..6a700b973e 100755 --- a/app/Models/Statuslabel.php +++ b/app/Models/Statuslabel.php @@ -27,7 +27,13 @@ class Statuslabel extends SnipeModel 'archived' => 'required', ); - protected $fillable = ['name', 'deployable', 'pending', 'archived']; + protected $fillable = [ + 'archived', + 'deployable', + 'name', + 'notes', + 'pending', + ]; /** diff --git a/app/Models/User.php b/app/Models/User.php index 9a31beb437..92f3caf9a5 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -28,24 +28,25 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo protected $table = 'users'; protected $injectUniqueIdentifier = true; protected $fillable = [ - 'email', - 'last_name', - 'company_id', - 'department_id', - 'employee_num', - 'jobtitle', - 'location_id', - 'password', - 'phone', - 'username', - 'first_name', + 'activated', 'address', 'city', - 'state', + 'company_id', 'country', - 'zip', - 'activated', + 'department_id', + 'email', + 'employee_num', + 'first_name', + 'jobtitle', + 'last_name', + 'locale', + 'location_id', 'manager_id', + 'password', + 'phone', + 'state', + 'username', + 'zip', ]; protected $casts = [ diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index eb79b53eba..a26897817c 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -6,21 +6,26 @@ $password = bcrypt('password'); $factory->define(App\Models\User::class, function (Faker\Generator $faker) use ($password) { return [ + 'activated' => 1, + 'address' => $faker->address, + 'city' => $faker->city, + 'company_id' => rand(1,4), + 'country' => $faker->country, + 'department_id' => rand(1,6), + 'email' => $faker->safeEmail, + 'employee_num' => $faker->numberBetween(3500, 35050), 'first_name' => $faker->firstName, + 'jobtitle' => $faker->jobTitle, 'last_name' => $faker->lastName, - 'username' => $faker->username, + 'locale' => $faker->locale, + 'location_id' => rand(1,5), + 'notes' => 'Created by DB seeder', 'password' => $password, 'permissions' => '{"user":"0"}', - 'email' => $faker->safeEmail, - 'company_id' => rand(1,4), - 'locale' => $faker->locale, - 'employee_num' => $faker->numberBetween(3500, 35050), - 'jobtitle' => $faker->jobTitle, - 'department_id' => rand(1,6), 'phone' => $faker->phoneNumber, - 'notes' => 'Created by DB seeder', - 'location_id' => rand(1,5), - 'activated' => 1, + 'state' => $faker->stateAbbr, + 'username' => $faker->username, + 'zip' => $faker->postcode ]; }); diff --git a/tests/api/ApiAccessoriesCest.php b/tests/api/ApiAccessoriesCest.php new file mode 100644 index 0000000000..23b7c4296b --- /dev/null +++ b/tests/api/ApiAccessoriesCest.php @@ -0,0 +1,154 @@ +user = \App\Models\User::find(1); + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexAccessories(ApiTester $I) + { + + $I->wantTo('Get a list of accessories'); + + // call + $I->sendGET('/accessories?limit=10'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse(), true); + // sample verify + $accessory = App\Models\Accessory::orderByDesc('created_at')->take(10)->get()->shuffle()->first(); + $I->seeResponseContainsJson((new AccessoriesTransformer)->transformAccessory($accessory)); + } + + /** @test */ + public function createAccessory(ApiTester $I, $scenario) + { + $I->wantTo('Create a new accessory'); + + $temp_accessory = factory(\App\Models\Accessory::class)->states('apple-bt-keyboard')->make([ + 'name' => "Test Accessory Name", + 'company_id' => 2 + ]); + + // setup + $data = [ + 'category_id' => $temp_accessory->category_id, + 'company_id' => $temp_accessory->company->id, + 'location_id' => $temp_accessory->location_id, + 'name' => $temp_accessory->name, + 'order_number' => $temp_accessory->order_number, + 'purchase_cost' => $temp_accessory->purchase_cost, + 'purchase_date' => $temp_accessory->purchase_date, + 'model_number' => $temp_accessory->model_number, + 'manufacturer_id' => $temp_accessory->manufacturer_id, + 'supplier_id' => $temp_accessory->supplier_id, + 'qty' => $temp_accessory->qty, + ]; + + // create + $I->sendPOST('/accessories', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + // Put is routed to the same method in the controller + // DO we actually need to test both? + /** @test */ + public function updateAccessoryWithPatch(ApiTester $I, $scenario) + { + $I->wantTo('Update an accessory with PATCH'); + + // create + $accessory = factory(\App\Models\Accessory::class)->states('apple-bt-keyboard')->create([ + 'name' => 'Original Accessory Name', + 'company_id' => 2, + 'location_id' => 3 + ]); + $I->assertInstanceOf(\App\Models\Accessory::class, $accessory); + + $temp_accessory = factory(\App\Models\Accessory::class)->states('microsoft-mouse')->make([ + 'company_id' => 3, + 'name' => "updated accessory name", + 'location_id' => 1, + ]); + + $data = [ + 'category_id' => $temp_accessory->category_id, + 'company_id' => $temp_accessory->company->id, + 'location_id' => $temp_accessory->location_id, + 'name' => $temp_accessory->name, + 'order_number' => $temp_accessory->order_number, + 'purchase_cost' => $temp_accessory->purchase_cost, + 'purchase_date' => $temp_accessory->purchase_date, + 'model_number' => $temp_accessory->model_number, + 'manufacturer_id' => $temp_accessory->manufacturer_id, + 'supplier_id' => $temp_accessory->supplier_id, + 'qty' => $temp_accessory->qty, + ]; + + $I->assertNotEquals($accessory->name, $data['name']); + + // update + $I->sendPATCH('/accessories/' . $accessory->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/accessories/message.update.success'), $response->messages); + $I->assertEquals($accessory->id, $response->payload->id); // accessory id does not change + $I->assertEquals($temp_accessory->company_id, $response->payload->company_id); // company_id updated + $I->assertEquals($temp_accessory->name, $response->payload->name); // accessory name updated + $I->assertEquals($temp_accessory->location_id, $response->payload->location_id); // accessory location_id updated + $temp_accessory->created_at = Carbon::parse($response->payload->created_at); + $temp_accessory->updated_at = Carbon::parse($response->payload->updated_at); + $temp_accessory->id = $accessory->id; + // verify + $I->sendGET('/accessories/' . $accessory->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson((new AccessoriesTransformer)->transformAccessory($temp_accessory)); + } + + /** @test */ + public function deleteAccessoryTest(ApiTester $I, $scenario) + { + $I->wantTo('Delete an accessory'); + + // create + $accessory = factory(\App\Models\Accessory::class)->states('apple-bt-keyboard')->create([ + 'name' => "Soon to be deleted" + ]); + $I->assertInstanceOf(\App\Models\Accessory::class, $accessory); + + // delete + $I->sendDELETE('/accessories/' . $accessory->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/accessories/message.delete.success'), $response->messages); + + // verify, expect a 200 + $I->sendGET('/accessories/' . $accessory->id); + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + } +} diff --git a/tests/api/ApiAssetsCest.php b/tests/api/ApiAssetsCest.php index ea41b99f72..5a14120ff4 100644 --- a/tests/api/ApiAssetsCest.php +++ b/tests/api/ApiAssetsCest.php @@ -1,5 +1,8 @@ setupDatabase(); $this->faker = \Faker\Factory::create(); $this->user = \App\Models\User::find(1); - $this->timeFormat = Setting::getSettings()->date_display_format .' '. Setting::getSettings()->time_display_format; + Setting::getSettings()->time_display_format = "H:i"; $I->amBearerAuthenticated($I->getToken($this->user)); } @@ -24,105 +26,15 @@ class ApiAssetsCest $I->wantTo('Get a list of assets'); - // We rely on the seeded database for this. No need to create new assets. - // $assets = factory(\App\Models\Asset::class, 10)->create(); - // call - $I->sendGET('/hardware?limit=10'); + $I->sendGET('/hardware?limit=20&sort=id&order=desc'); $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); $response = json_decode($I->grabResponse(), true); // sample verify - $asset = App\Models\Asset::orderByDesc('created_at')->first(); - - $I->seeResponseContainsJson([ - - 'id' => (int) $asset->id, - 'name' => e($asset->name), - 'asset_tag' => e($asset->asset_tag), - 'serial' => e($asset->serial), - 'model' => ($asset->model) ? [ - 'id' => (int) $asset->model->id, - 'name'=> e($asset->model->name) - ] : null, - 'model_number' => ($asset->model) ? e($asset->model->model_number) : null, - 'status_label' => ($asset->assetstatus) ? [ - 'id' => (int) $asset->assetstatus->id, - 'name'=> e($asset->assetstatus->name) - ] : null, - 'category' => ($asset->model->category) ? [ - 'id' => (int) $asset->model->category->id, - 'name'=> e($asset->model->category->name) - ] : null, - 'manufacturer' => ($asset->model->manufacturer) ? [ - 'id' => (int) $asset->model->manufacturer->id, - 'name'=> e($asset->model->manufacturer->name) - ] : null, - 'supplier' => ($asset->supplier) ? [ - 'id' => (int) $asset->supplier->id, - 'name'=> e($asset->supplier->name) - ] : null, - 'notes' => e($asset->notes), - 'order_number' => e($asset->order_number), - 'company' => ($asset->company) ? [ - 'id' => (int) $asset->company->id, - 'name'=> e($asset->company->name) - ] : null, - 'location' => ($asset->location) ? [ - 'id' => (int) $asset->location->id, - 'name'=> e($asset->location->name) - ] : null, - 'rtd_location' => ($asset->defaultLoc) ? [ - 'id' => (int) $asset->defaultLoc->id, - 'name'=> e($asset->defaultLoc->name) - ] : null, - 'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null, - 'assigned_to' => ($asset->assigneduser) ? [ - 'id' => (int) $asset->assigneduser->id, - 'name' => e($asset->assigneduser->getFullNameAttribute()), - 'first_name'=> e($asset->assigneduser->first_name), - 'last_name'=> e($asset->assigneduser->last_name) - ] : null, - 'warranty_months' => ($asset->warranty_months > 0) ? e($asset->warranty_months . ' ' . trans('admin/hardware/form.months')) : null, - 'warranty_expires' => ($asset->warranty_months > 0) ? [ - 'datetime' => $asset->created_at->format('Y-m-d'), - 'formatted' => $asset->created_at->format('Y-m-d'), - ] : null, - - // I have no idea why these cause the test to fail. I think it's something about nested json. - // 'created_at' => ($asset->created_at) ? [ - // 'datetime' => $asset->created_at->format('Y-m-d H:i:s'), - // 'formatted' => $asset->created_at->format('Y-m-d H:i a'), - // ] : null, - // 'updated_at' => ($asset->updated_at) ? [ - // 'datetime' => $asset->updated_at->format('Y-m-d H:i:s'), - // 'formatted' => $asset->updated_at->format('Y-m-d H:i a'), - // ] : null, - // // TODO: Implement last_audit_date and next_audit_date - // 'purchase_date' => ($asset->purchase_date) ? [ - // 'datetime' => $asset->purchase_date->format('Y-m-d'), - // 'formatted' => $asset->purchase_date->format('Y-m-d'), - // ] : null, - // 'last_checkout' => ($asset->last_checkout) ? [ - // 'datetime' => $asset->last_checkout->format('Y-m-d'), - // 'formatted' => $asset->last_checkout->format('Y-m-d'), - // ] : null, - // 'expected_checkin' => ($asset->created_at) ? [ - // 'date' => $asset->created_at->format('Y-m-d'), - // 'formatted' => $asset->created_at->format('Y-m-d'), - // ] : null, - 'purchase_cost' => (float) $asset->purchase_cost, - 'user_can_checkout' => (bool) $asset->availableForCheckout(), - 'available_actions' => [ - 'checkout' => (bool) Gate::allows('checkout', Asset::class), - 'checkin' => (bool) Gate::allows('checkin', Asset::class), - 'clone' => (bool) Gate::allows('create', Asset::class), - 'restore' => (bool) false, // FIXME: when this gets implemented in assetstransformer it should be updated here - 'update' => (bool) Gate::allows('update', Asset::class), - 'delete' => (bool) Gate::allows('delete', Asset::class), - ], - ]); + $asset = Asset::orderByDesc('id')->take(20)->get()->first(); + $I->seeResponseContainsJson((new AssetsTransformer)->transformAsset($asset)); } /** @test */ @@ -185,6 +97,7 @@ class ApiAssetsCest 'model_id' => $temp_asset->model_id, 'name' => $temp_asset->name, 'notes' => $temp_asset->notes, + 'order_number' => $temp_asset->order_number, 'purchase_cost' => $temp_asset->purchase_cost, 'purchase_date' => $temp_asset->purchase_date->format('Y-m-d'), 'rtd_location_id' => $temp_asset->rtd_location_id, @@ -202,227 +115,23 @@ class ApiAssetsCest $I->seeResponseCodeIs(200); $response = json_decode($I->grabResponse()); + // dd($response); $I->assertEquals('success', $response->status); $I->assertEquals(trans('admin/hardware/message.update.success'), $response->messages); $I->assertEquals($asset->id, $response->payload->id); // asset id does not change $I->assertEquals($temp_asset->asset_tag, $response->payload->asset_tag); // asset tag updated $I->assertEquals($temp_asset->name, $response->payload->name); // asset name updated $I->assertEquals($temp_asset->rtd_location_id, $response->payload->rtd_location_id); // asset rtd_location_id updated - - // verify - $I->sendGET('/hardware/' . $asset->id); - // dd($I->grabResponse()); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson([ - 'id' => (int) $asset->id, - 'name' => e($temp_asset->name), - 'asset_tag' => e($temp_asset->asset_tag), - 'serial' => e($temp_asset->serial), - 'model' => ($temp_asset->model) ? [ - 'id' => (int) $temp_asset->model->id, - 'name'=> e($temp_asset->model->name) - ] : null, - 'model_number' => ($temp_asset->model) ? e($temp_asset->model->model_number) : null, - 'status_label' => ($temp_asset->assetstatus) ? [ - 'id' => (int) $temp_asset->assetstatus->id, - 'name'=> e($temp_asset->assetstatus->name), - 'status_type' => $temp_asset->assetstatus->getStatuslabelType(), - 'status_meta' => $temp_asset->present()->statusMeta - ] : null, - 'category' => ($temp_asset->model->category) ? [ - 'id' => (int) $temp_asset->model->category->id, - 'name'=> e($temp_asset->model->category->name) - ] : null, - 'manufacturer' => ($temp_asset->model->manufacturer) ? [ - 'id' => (int) $temp_asset->model->manufacturer->id, - 'name'=> e($temp_asset->model->manufacturer->name) - ] : null, - 'notes' => e($temp_asset->notes), - 'order_number' => e($asset->order_number), - 'company' => ($asset->company) ? [ - 'id' => (int) $temp_asset->company->id, - 'name'=> e($temp_asset->company->name) - ] : null, - 'rtd_location' => ($temp_asset->defaultLoc) ? [ - 'id' => (int) $temp_asset->defaultLoc->id, - 'name'=> e($temp_asset->defaultLoc->name) - ] : null, - 'image' => ($temp_asset->getImageUrl()) ? $temp_asset->getImageUrl() : null, - 'assigned_to' => ($temp_asset->assigneduser) ? [ - 'id' => (int) $temp_asset->assigneduser->id, - 'name' => e($temp_asset->assigneduser->getFullNameAttribute()), - 'first_name'=> e($temp_asset->assigneduser->first_name), - 'last_name'=> e($temp_asset->assigneduser->last_name) - ] : null, - 'warranty_months' => ($asset->warranty_months > 0) ? e($asset->warranty_months . ' ' . trans('admin/hardware/form.months')) : null, - 'warranty_expires' => ($asset->warranty_months > 0) ? [ - 'datetime' => $asset->created_at->format('Y-m-d'), - 'formatted' => $asset->created_at->format('Y-m-d'), - ] : null, - // 'created_at' => ($asset->created_at) ? [ - // 'datetime' => $asset->created_at->format('Y-m-d H:i:s'), - // 'formatted' => $asset->created_at->format($this->timeFormat), - // ] : null, - // 'updated_at' => ($asset->updated_at) ? [ - // 'datetime' => $asset->updated_at->format('Y-m-d H:i:s'), - // 'formatted' => $asset->updated_at->format($this->timeFormat), - // ] : null, - 'purchase_date' => ($asset->purchase_date) ? [ - 'date' => $temp_asset->purchase_date->format('Y-m-d'), - 'formatted' => $temp_asset->purchase_date->format('Y-m-d'), - ] : null, - // 'last_checkout' => ($asset->last_checkout) ? [ - // 'datetime' => $asset->last_checkout->format('Y-m-d'), - // 'formatted' => $asset->last_checkout->format('Y-m-d'), - // ] : null, - // 'expected_checkin' => ($asset->created_at) ? [ - // 'date' => $asset->created_at->format('Y-m-d'), - // 'formatted' => $asset->created_at->format('Y-m-d'), - // ] : null, - // 'purchase_cost' => (float) $asset->purchase_cost, - 'user_can_checkout' => (bool) $temp_asset->availableForCheckout(), - 'available_actions' => [ - 'checkout' => (bool) Gate::allows('checkout', Asset::class), - 'checkin' => (bool) Gate::allows('checkin', Asset::class), - 'update' => (bool) Gate::allows('update', Asset::class), - 'delete' => (bool) Gate::allows('delete', Asset::class), - ], - ]); - } - - /** @test */ - public function updateAssetWithPut(ApiTester $I) - { - $I->wantTo('Update a asset with PUT'); - - // create - $asset = factory(\App\Models\Asset::class)->states('laptop-mbp')->create([ - 'company_id' => 2, - 'name' => "Original name" - ]); - $I->assertInstanceOf(\App\Models\Asset::class, $asset); - - $temp_asset = factory(\App\Models\Asset::class)->states('laptop-air')->make([ - 'company_id' => 1, - 'name' => "Updated Name" - ]); - - $I->assertNotNull($temp_asset->asset_tag); - - $data = [ - 'asset_tag' => $temp_asset->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, - ]; - - $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); - $I->assertEquals(trans('admin/hardware/message.update.success'), $response->messages); - $I->assertEquals($asset->id, $response->payload->id); // asset id does not change - $I->assertEquals($temp_asset->asset_tag, $response->payload->asset_tag); // asset tag updated - $I->assertEquals($temp_asset->name, $response->payload->name); // asset name updated - $I->assertEquals($temp_asset->rtd_location_id, $response->payload->rtd_location_id); // asset rtd_location_id updated + $temp_asset->created_at = Carbon::parse($response->payload->created_at); + $temp_asset->updated_at = Carbon::parse($response->payload->updated_at); + $temp_asset->id = $asset->id; + $temp_asset->location_id = $response->payload->rtd_location_id; // verify $I->sendGET('/hardware/' . $asset->id); $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson([ - 'id' => (int) $asset->id, - 'name' => e($temp_asset->name), - 'asset_tag' => e($temp_asset->asset_tag), - 'serial' => e($temp_asset->serial), - 'model' => ($temp_asset->model) ? [ - 'id' => (int) $temp_asset->model->id, - 'name'=> e($temp_asset->model->name) - ] : null, - 'model_number' => ($temp_asset->model) ? e($temp_asset->model->model_number) : null, - 'status_label' => ($temp_asset->assetstatus) ? [ - 'id' => (int) $temp_asset->assetstatus->id, - 'name'=> e($temp_asset->assetstatus->name) - ] : null, - 'category' => ($temp_asset->model->category) ? [ - 'id' => (int) $temp_asset->model->category->id, - 'name'=> e($temp_asset->model->category->name) - ] : null, - 'manufacturer' => ($temp_asset->model->manufacturer) ? [ - 'id' => (int) $temp_asset->model->manufacturer->id, - 'name'=> e($temp_asset->model->manufacturer->name) - ] : null, - 'notes' => e($temp_asset->notes), - 'order_number' => e($asset->order_number), - 'company' => ($asset->company) ? [ - 'id' => (int) $temp_asset->company->id, - 'name'=> e($temp_asset->company->name) - ] : null, - // 'location' => ($temp_asset->location) ? [ - // 'id' => (int) $temp_asset->location->id, - // 'name'=> e($temp_asset->location->name) - // ] : null, - 'rtd_location' => ($temp_asset->defaultLoc) ? [ - 'id' => (int) $temp_asset->defaultLoc->id, - 'name'=> e($temp_asset->defaultLoc->name) - ] : null, - 'image' => ($temp_asset->getImageUrl()) ? $temp_asset->getImageUrl() : null, - 'assigned_to' => ($temp_asset->assigneduser) ? [ - 'id' => (int) $temp_asset->assigneduser->id, - 'name' => e($temp_asset->assigneduser->getFullNameAttribute()), - 'first_name'=> e($temp_asset->assigneduser->first_name), - 'last_name'=> e($temp_asset->assigneduser->last_name) - ] : null, - 'warranty_months' => ($asset->warranty_months > 0) ? e($asset->warranty_months . ' ' . trans('admin/hardware/form.months')) : null, - 'warranty_expires' => ($asset->warranty_months > 0) ? [ - 'datetime' => $asset->created_at->format('Y-m-d'), - 'formatted' => $asset->created_at->format('Y-m-d'), - ] : null, - // 'created_at' => ($asset->created_at) ? [ - // 'datetime' => $asset->created_at->format('Y-m-d H:i:s'), - // 'formatted' => $asset->created_at->format('Y-m-d H:i a'), - // ] : null, - // 'updated_at' => ($asset->updated_at) ? [ - // 'datetime' => $asset->updated_at->format('Y-m-d H:i:s'), - // 'formatted' => $asset->updated_at->format('Y-m-d H:i a'), - // ] : null, - 'purchase_date' => ($asset->purchase_date) ? [ - 'date' => $temp_asset->purchase_date->format('Y-m-d'), - 'formatted' => $temp_asset->purchase_date->format('Y-m-d'), - ] : null, - // 'last_checkout' => ($asset->last_checkout) ? [ - // 'datetime' => $asset->last_checkout->format('Y-m-d'), - // 'formatted' => $asset->last_checkout->format('Y-m-d'), - // ] : null, - // 'expected_checkin' => ($asset->created_at) ? [ - // 'date' => $asset->created_at->format('Y-m-d'), - // 'formatted' => $asset->created_at->format('Y-m-d'), - // ] : null, - // 'purchase_cost' => (float) $asset->purchase_cost, - 'user_can_checkout' => (bool) $temp_asset->availableForCheckout(), - 'available_actions' => [ - 'checkout' => (bool) Gate::allows('checkout', Asset::class), - 'checkin' => (bool) Gate::allows('checkin', Asset::class), - 'update' => (bool) Gate::allows('update', Asset::class), - 'delete' => (bool) Gate::allows('delete', Asset::class), - ], - ]); + $I->seeResponseContainsJson((new AssetsTransformer)->transformAsset($temp_asset)); } /** @test */ @@ -446,9 +155,10 @@ class ApiAssetsCest // verify, expect a 200 $I->sendGET('/hardware/' . $asset->id); $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); // @todo: response is not JSON + $I->seeResponseIsJson(); - - // $scenario->incomplete('not found response should be JSON, receiving HTML instead'); + // Make sure we're soft deleted. + $response = json_decode($I->grabResponse()); + $I->assertNotNull($response->deleted_at); } } diff --git a/tests/api/ApiCheckoutAssetsCest.php b/tests/api/ApiCheckoutAssetsCest.php new file mode 100644 index 0000000000..daf56fc5d5 --- /dev/null +++ b/tests/api/ApiCheckoutAssetsCest.php @@ -0,0 +1,141 @@ +user = \App\Models\User::find(1); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function checkoutAssetToUser(ApiTester $I) { + $I->wantTo('Check out an asset to a user'); + //Grab an asset from the database that isn't checked out. + $asset = Asset::whereNull('assigned_to')->first(); + $targetUser = factory('App\Models\User')->create(); + $data = [ + 'assigned_user' => $targetUser->id, + 'note' => "This is a test checkout note", + 'expected_checkin' => "2018-05-24", + 'name' => "Updated Asset Name", + 'checkout_to_type' => 'user' + ]; + $I->sendPOST("/hardware/{$asset->id}/checkout", $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/hardware/message.checkout.success'), $response->messages); + + // Confirm results. + $I->sendGET("/hardware/{$asset->id}"); + $I->seeResponseContainsJson([ + 'assigned_to' => [ + 'id' => $targetUser->id, + 'type' => 'user' + ], + 'name' => "Updated Asset Name", + 'expected_checkin' => Helper::getFormattedDateObject('2018-05-24', 'date') + ]); + + } + + /** @test */ + public function checkoutAssetToAsset(ApiTester $I) { + $I->wantTo('Check out an asset to an asset'); + //Grab an asset from the database that isn't checked out. + $asset = Asset::whereNull('assigned_to')->where('model_id',8)->first(); // We need to make sure that this is an asset/model that doesn't require acceptance + $targetAsset = factory('App\Models\Asset')->states('desktop-macpro')->create([ + 'name' => "Test Asset For Checkout to" + ]); + // dd($targetAsset->model->category); + $data = [ + 'assigned_asset' => $targetAsset->id, + 'checkout_to_type' => 'asset' + ]; + $I->sendPOST("/hardware/{$asset->id}/checkout", $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/hardware/message.checkout.success'), $response->messages); + + // Confirm results. + $I->sendGET("/hardware/{$asset->id}"); + $I->seeResponseContainsJson([ + 'assigned_to' => [ + 'id' => $targetAsset->id, + 'type' => 'asset' + ] + ]); + + } + + /** @test */ + public function checkoutAssetToLocation(ApiTester $I) { + $I->wantTo('Check out an asset to an asset'); + //Grab an asset from the database that isn't checked out. + $asset = Asset::whereNull('assigned_to')->where('model_id',8)->first(); + $targetLocation = factory('App\Models\Location')->create([ + 'name' => "Test Location for Checkout" + ]); + // dd($targetAsset->model->category); + $data = [ + 'assigned_location' => $targetLocation->id, + 'checkout_to_type' => 'location' + ]; + $I->sendPOST("/hardware/{$asset->id}/checkout", $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/hardware/message.checkout.success'), $response->messages); + + // Confirm results. + $I->sendGET("/hardware/{$asset->id}"); + $I->seeResponseContainsJson([ + 'assigned_to' => [ + 'id' => $targetLocation->id, + 'type' => 'location' + ] + ]); + + } + + /** @test */ + public function checkinAsset(ApiTester $I) { + $I->wantTo('Checkin an asset that is currently checked out'); + + $asset = Asset::whereNotNull('assigned_to')->first(); + + $I->sendPOST("/hardware/{$asset->id}/checkin", [ + "note" => "Checkin Note" + ]); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/hardware/message.checkin.success'), $response->messages); + + // Verify + $I->sendGET("/hardware/{$asset->id}"); + $I->seeResponseContainsJson([ + 'assigned_to' => null + ]); + } +} diff --git a/tests/api/ApiCompaniesCest.php b/tests/api/ApiCompaniesCest.php new file mode 100644 index 0000000000..35653b4e9e --- /dev/null +++ b/tests/api/ApiCompaniesCest.php @@ -0,0 +1,133 @@ +user = \App\Models\User::find(1); + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexCompanys(ApiTester $I) + { + + $I->wantTo('Get a list of companies'); + + // call + $I->sendGET('/companies?order_by=id&limit=10'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse(), true); + // dd($response); + // sample verify + $company = App\Models\Company::withCount('assets','licenses','accessories','consumables','components','users') + ->orderByDesc('created_at')->take(10)->get()->shuffle()->first(); + + $I->seeResponseContainsJson((new CompaniesTransformer)->transformCompany($company)); + } + + /** @test */ + public function createCompany(ApiTester $I, $scenario) + { + $I->wantTo('Create a new company'); + + $temp_company = factory(\App\Models\Company::class)->make([ + 'name' => "Test Company Tag", + ]); + + // setup + $data = [ + 'name' => $temp_company->name, + ]; + + // create + $I->sendPOST('/companies', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + // Put is routed to the same method in the controller + // DO we actually need to test both? + /** @test */ + public function updateCompanyWithPatch(ApiTester $I, $scenario) + { + $I->wantTo('Update an company with PATCH'); + + // create + $company = factory(\App\Models\Company::class)->create([ + 'name' => 'Original Company Name', + ]); + $I->assertInstanceOf(\App\Models\Company::class, $company); + + $temp_company = factory(\App\Models\Company::class)->make([ + 'name' => "updated company name", + ]); + + $data = [ + 'name' => $temp_company->name, + ]; + + $I->assertNotEquals($company->name, $data['name']); + + // update + $I->sendPATCH('/companies/' . $company->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/companies/message.update.success'), $response->messages); + $I->assertEquals($company->id, $response->payload->id); // company id does not change + $I->assertEquals($temp_company->name, $response->payload->name); // company name updated + // Some manual copying to compare against + $temp_company->created_at = Carbon::parse($response->payload->created_at->datetime); + $temp_company->updated_at = Carbon::parse($response->payload->updated_at->datetime); + $temp_company->id = $company->id; + + // verify + $I->sendGET('/companies/' . $company->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson((new CompaniesTransformer)->transformCompany($temp_company)); + + } + + /** @test */ + public function deleteCompanyTest(ApiTester $I, $scenario) + { + $I->wantTo('Delete an company'); + + // create + $company = factory(\App\Models\Company::class)->create([ + 'name' => "Soon to be deleted" + ]); + $I->assertInstanceOf(\App\Models\Company::class, $company); + + // delete + $I->sendDELETE('/companies/' . $company->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/companies/message.delete.success'), $response->messages); + + // verify, expect a 200 + $I->sendGET('/companies/' . $company->id); + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + } +} diff --git a/tests/api/ApiComponentsCest.php b/tests/api/ApiComponentsCest.php index d7e05f4a7d..33120be95d 100644 --- a/tests/api/ApiComponentsCest.php +++ b/tests/api/ApiComponentsCest.php @@ -1,18 +1,20 @@ faker = \Faker\Factory::create(); $this->user = \App\Models\User::find(1); - + $I->haveHttpHeader('Accept', 'application/json'); $I->amBearerAuthenticated($I->getToken($this->user)); } @@ -21,104 +23,81 @@ class ApiComponentsCest { $I->wantTo('Get a list of components'); - // setup - // $components = factory(\App\Models\Component::class, 10)->create(); - // call - $I->sendGET('/components?limit=10&order=desc'); + $I->sendGET('/components?limit=10'); $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); + $response = json_decode($I->grabResponse(), true); // sample verify - $component = Component::orderByDesc('created_at')->first(); - $I->seeResponseContainsJson([ - 'name' => $component->name, - 'qty' => $component->qty, - ]); + $component = App\Models\Component::orderByDesc('created_at')->take(10)->get()->shuffle()->first(); - $I->seeResponseContainsJson([ - 'total' => \App\Models\Component::count(), - ]); + $I->seeResponseContainsJson((new ComponentsTransformer)->transformComponent($component)); } /** @test */ - public function createComponent(ApiTester $I) + public function createComponent(ApiTester $I, $scenario) { $I->wantTo('Create a new component'); - // setup - $category = factory(\App\Models\Category::class)->states('asset-laptop-category')->create([ - 'name' => "Test Category Name", - 'user_id' => $this->user->id + $temp_component = factory(\App\Models\Component::class)->states('ram-crucial4')->make([ + 'name' => "Test Component Name", + 'company_id' => 2 ]); - $location = factory(\App\Models\Location::class)->create(['user_id' => $this->user->id]); - $company = factory(\App\Models\Company::class)->create(); + // setup $data = [ - 'category_id' => $category->id, - 'company_id' => $company->id, - 'location_id' => $location->id, - 'name' => $this->faker->sentence(3), - 'purchase_cost' => $this->faker->randomFloat(2, 0), - 'purchase_date' => $this->faker->dateTime->format('Y-m-d'), - 'qty' => rand(1, 10), + 'category_id' => $temp_component->category_id, + 'company_id' => $temp_component->company->id, + 'location_id' => $temp_component->location_id, + 'manufacturer_id' => $temp_component->manufacturer_id, + 'model_number' => $temp_component->model_number, + 'name' => $temp_component->name, + 'order_number' => $temp_component->order_number, + 'purchase_cost' => $temp_component->purchase_cost, + 'purchase_date' => $temp_component->purchase_date, + 'qty' => $temp_component->qty, + 'serial' => $temp_component->serial ]; // create $I->sendPOST('/components', $data); $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $id = $response->payload->id; - - $I->assertEquals('success', $response->status); - - // verify - $I->sendGET('/components/' . $id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson([ - 'id' => (int) $id, - 'name' => e($data['name']), - // 'serial_number' => e($component->serial), - 'location' => [ - 'id' => (int) $data['location_id'], - 'name' => e($location->name), - ], - 'qty' => number_format($data['qty']), - // 'min_amt' => e($component->min_amt), - 'category' => [ - 'id' => (int) $data['category_id'], - 'name' => e($category->name), - ], - // 'order_number' => e($component->order_number), - 'purchase_date' => \App\Helpers\Helper::getFormattedDateObject($data['purchase_date'], 'date'), - 'purchase_cost' => \App\Helpers\Helper::formatCurrencyOutput($data['purchase_cost']), - // 'remaining' => (int) $component->numRemaining(), - 'company' => [ - 'id' => (int) $data['company_id'], - 'name' => e($company->name), - ], - // 'created_at' => Helper::getFormattedDateObject($component->created_at, 'datetime'), - // 'updated_at' => Helper::getFormattedDateObject($component->updated_at, 'datetime'), - ]); } + // Put is routed to the same method in the controller + // DO we actually need to test both? /** @test */ - public function updateComponentWithPatch(ApiTester $I) + public function updateComponentWithPatch(ApiTester $I, $scenario) { - $I->wantTo('Update a component with PATCH'); + $I->wantTo('Update an component with PATCH'); // create $component = factory(\App\Models\Component::class)->states('ram-crucial4')->create([ - 'name' => "Test Component" + 'name' => 'Original Component Name', + 'company_id' => 2, + 'location_id' => 3 ]); $I->assertInstanceOf(\App\Models\Component::class, $component); + $temp_component = factory(\App\Models\Component::class)->states('ssd-crucial240')->make([ + 'company_id' => 3, + 'name' => "updated component name", + 'location_id' => 1, + ]); + $data = [ - 'name' => $this->faker->sentence(3), - 'qty' => $this->faker->randomDigit + 1, + 'category_id' => $temp_component->category_id, + 'company_id' => $temp_component->company->id, + 'location_id' => $temp_component->location_id, + 'min_amt' => $temp_component->min_amt, + 'name' => $temp_component->name, + 'order_number' => $temp_component->order_number, + 'purchase_cost' => $temp_component->purchase_cost, + 'purchase_date' => $temp_component->purchase_date, + 'qty' => $temp_component->qty, + 'serial' => $temp_component->serial, ]; $I->assertNotEquals($component->name, $data['name']); @@ -129,63 +108,31 @@ class ApiComponentsCest $I->seeResponseCodeIs(200); $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/components/message.update.success'), $response->messages); + $I->assertEquals($component->id, $response->payload->id); // component id does not change + $I->assertEquals($temp_component->company_id, $response->payload->company_id); // company_id updated + $I->assertEquals($temp_component->name, $response->payload->name); // component name updated + $I->assertEquals($temp_component->location_id, $response->payload->location_id); // component location_id updated + $temp_component->created_at = Carbon::parse($response->payload->created_at); + $temp_component->updated_at = Carbon::parse($response->payload->updated_at); + $temp_component->id = $component->id; // verify $I->sendGET('/components/' . $component->id); $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson([ - 'name' => $data['name'], - 'id' => $component->id, - 'qty' => $data['qty'], - ]); - } - - /** @test */ - public function updateComponentWithPut(ApiTester $I) - { - $I->wantTo('Update a component with PUT'); - - // create - $component = factory(\App\Models\Component::class)->states('ram-crucial4')->create([ - 'name' => "Test Component" - ]); - $I->assertInstanceOf(\App\Models\Component::class, $component); - - $data = [ - 'name' => $this->faker->sentence(3), - ]; - - $I->assertNotEquals($component->name, $data['name']); - - // update - $I->sendPUT('/components/' . $component->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - - // verify - $I->sendGET('/components/' . $component->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson([ - 'name' => e($data['name']), - 'id' => e($component->id), - 'qty' => e($component->qty), - ]); + $I->seeResponseContainsJson((new ComponentsTransformer)->transformComponent($temp_component)); } /** @test */ public function deleteComponentTest(ApiTester $I, $scenario) { - $I->wantTo('Delete a component'); + $I->wantTo('Delete an component'); // create $component = factory(\App\Models\Component::class)->states('ram-crucial4')->create([ - 'name' => "Test Component" + 'name' => "Soon to be deleted" ]); $I->assertInstanceOf(\App\Models\Component::class, $component); @@ -194,10 +141,15 @@ class ApiComponentsCest $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); - // verify, expect a 200 with an error message + $response = json_decode($I->grabResponse()); + // dd($response); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/components/message.delete.success'), $response->messages); + + // verify, expect a 200 $I->sendGET('/components/' . $component->id); + $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); // @todo: response is not JSON - // $scenario->incomplete('Resource not found response should be JSON, receiving HTML instead'); + $I->seeResponseIsJson(); } } diff --git a/tests/api/ApiConsumablesCest.php b/tests/api/ApiConsumablesCest.php new file mode 100644 index 0000000000..d7938195b5 --- /dev/null +++ b/tests/api/ApiConsumablesCest.php @@ -0,0 +1,154 @@ +user = \App\Models\User::find(1); + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexConsumables(ApiTester $I) + { + $I->wantTo('Get a list of consumables'); + + // call + $I->sendGET('/consumables?limit=10'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse(), true); + // sample verify + $consumable = App\Models\Consumable::orderByDesc('created_at')->take(10)->get()->shuffle()->first(); + + $I->seeResponseContainsJson((new ConsumablesTransformer)->transformConsumable($consumable)); + } + + /** @test */ + public function createConsumable(ApiTester $I, $scenario) + { + $I->wantTo('Create a new consumable'); + + $temp_consumable = factory(\App\Models\Consumable::class)->states('ink')->make([ + 'name' => "Test Consumable Name", + 'company_id' => 2 + ]); + + // setup + $data = [ + 'category_id' => $temp_consumable->category_id, + 'company_id' => $temp_consumable->company->id, + 'location_id' => $temp_consumable->location_id, + 'manufacturer_id' => $temp_consumable->manufacturer_id, + 'name' => $temp_consumable->name, + 'order_number' => $temp_consumable->order_number, + 'purchase_cost' => $temp_consumable->purchase_cost, + 'purchase_date' => $temp_consumable->purchase_date, + 'qty' => $temp_consumable->qty, + 'model_number' => $temp_consumable->model_number, + ]; + + // create + $I->sendPOST('/consumables', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + // Put is routed to the same method in the controller + // DO we actually need to test both? + /** @test */ + public function updateConsumableWithPatch(ApiTester $I, $scenario) + { + $I->wantTo('Update an consumable with PATCH'); + + // create + $consumable = factory(\App\Models\Consumable::class)->states('ink')->create([ + 'name' => 'Original Consumable Name', + 'company_id' => 2, + 'location_id' => 3 + ]); + $I->assertInstanceOf(\App\Models\Consumable::class, $consumable); + + $temp_consumable = factory(\App\Models\Consumable::class)->states('cardstock')->make([ + 'company_id' => 3, + 'name' => "updated consumable name", + 'location_id' => 1, + ]); + + $data = [ + 'category_id' => $temp_consumable->category_id, + 'company_id' => $temp_consumable->company->id, + 'item_no' => $temp_consumable->item_no, + 'location_id' => $temp_consumable->location_id, + 'name' => $temp_consumable->name, + 'order_number' => $temp_consumable->order_number, + 'purchase_cost' => $temp_consumable->purchase_cost, + 'purchase_date' => $temp_consumable->purchase_date, + 'model_number' => $temp_consumable->model_number, + 'manufacturer_id' => $temp_consumable->manufacturer_id, + 'supplier_id' => $temp_consumable->supplier_id, + 'qty' => $temp_consumable->qty, + ]; + + $I->assertNotEquals($consumable->name, $data['name']); + + // update + $I->sendPATCH('/consumables/' . $consumable->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/consumables/message.update.success'), $response->messages); + $I->assertEquals($consumable->id, $response->payload->id); // consumable id does not change + $I->assertEquals($temp_consumable->company_id, $response->payload->company_id); // company_id updated + $I->assertEquals($temp_consumable->name, $response->payload->name); // consumable name updated + $I->assertEquals($temp_consumable->location_id, $response->payload->location_id); // consumable location_id updated + $temp_consumable->created_at = Carbon::parse($response->payload->created_at); + $temp_consumable->updated_at = Carbon::parse($response->payload->updated_at); + $temp_consumable->id = $consumable->id; + // verify + $I->sendGET('/consumables/' . $consumable->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson((new ConsumablesTransformer)->transformConsumable($temp_consumable)); + } + + /** @test */ + public function deleteConsumableTest(ApiTester $I, $scenario) + { + $I->wantTo('Delete an consumable'); + + // create + $consumable = factory(\App\Models\Consumable::class)->states('ink')->create([ + 'name' => "Soon to be deleted" + ]); + $I->assertInstanceOf(\App\Models\Consumable::class, $consumable); + + // delete + $I->sendDELETE('/consumables/' . $consumable->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/consumables/message.delete.success'), $response->messages); + + // verify, expect a 200 + $I->sendGET('/consumables/' . $consumable->id); + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + } +} diff --git a/tests/api/ApiLocationsCest.php b/tests/api/ApiLocationsCest.php new file mode 100644 index 0000000000..b30bb317e5 --- /dev/null +++ b/tests/api/ApiLocationsCest.php @@ -0,0 +1,145 @@ +user = \App\Models\User::find(1); + $this->timeFormat = Setting::getSettings()->date_display_format .' '. Setting::getSettings()->time_display_format; + $this->dateFormat = Setting::getSettings()->date_display_format; + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexLocations(ApiTester $I) + { + + $I->wantTo('Get a list of locations'); + + // call + $I->sendGET('/locations?limit=1'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse(), true); + // sample verify + $location = App\Models\Location::orderByDesc('created_at') + ->withCount('assignedAssets', 'assets', 'users') + ->take(1)->get()->shuffle()->first(); + $I->seeResponseContainsJson((new LocationsTransformer)->transformLocation($location)); + } + + /** @test */ + public function createLocation(ApiTester $I, $scenario) + { + $I->wantTo('Create a new location'); + + $temp_location = factory(\App\Models\Location::class)->make([ + 'name' => "Test Location Tag", + ]); + + // setup + $data = [ + 'name' => $temp_location->name, + ]; + + // create + $I->sendPOST('/locations', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + // Put is routed to the same method in the controller + // DO we actually need to test both? + /** @test */ + public function updateLocationWithPatch(ApiTester $I, $scenario) + { + $I->wantTo('Update an location with PATCH'); + + // create + $location = factory(\App\Models\Location::class)->create([ + 'name' => 'Original Location Name', + ]); + $I->assertInstanceOf(\App\Models\Location::class, $location); + + $temp_location = factory(\App\Models\Location::class)->make([ + 'name' => "updated location name", + ]); + + $data = [ + 'name' => $temp_location->name, + 'image' => $temp_location->image, + 'address' => $temp_location->address, + 'address2' => $temp_location->address2, + 'city' => $temp_location->city, + 'state' => $temp_location->state, + 'country' => $temp_location->country, + 'zip' => $temp_location->zip, + 'parent_id' => $temp_location->parent_id, + 'parent_id' => $temp_location->parent_id, + 'manager_id' => $temp_location->manager_id, + 'currency' => $temp_location->currency + ]; + + $I->assertNotEquals($location->name, $data['name']); + + // update + $I->sendPATCH('/locations/' . $location->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/locations/message.update.success'), $response->messages); + $I->assertEquals($location->id, $response->payload->id); // location id does not change + $I->assertEquals($temp_location->name, $response->payload->name); // location name updated + + // Some necessary manual copying + $temp_location->created_at = Carbon::parse($response->payload->created_at->datetime); + $temp_location->updated_at = Carbon::parse($response->payload->updated_at->datetime); + $temp_location->id = $location->id; + // verify + $I->sendGET('/locations/' . $location->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson((new LocationsTransformer)->transformLocation($temp_location)); + // $I->seeResponseContainsJson($this->generateJsonResponse($temp_location, $location)); + } + + /** @test */ + public function deleteLocationTest(ApiTester $I, $scenario) + { + $I->wantTo('Delete an location'); + + // create + $location = factory(\App\Models\Location::class)->create([ + 'name' => "Soon to be deleted" + ]); + $I->assertInstanceOf(\App\Models\Location::class, $location); + + // delete + $I->sendDELETE('/locations/' . $location->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/locations/message.delete.success'), $response->messages); + + // verify, expect a 200 + $I->sendGET('/locations/' . $location->id); + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + } +} diff --git a/tests/api/ApiStatusLabelsCest.php b/tests/api/ApiStatusLabelsCest.php new file mode 100644 index 0000000000..d3f02e843c --- /dev/null +++ b/tests/api/ApiStatusLabelsCest.php @@ -0,0 +1,143 @@ +user = \App\Models\User::find(1); + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexStatuslabels(ApiTester $I) + { + + $I->wantTo('Get a list of statuslabels'); + + // call + $I->sendGET('/statuslabels?limit=10'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse(), true); + // sample verify + $statuslabel = App\Models\Statuslabel::orderByDesc('created_at') + ->withCount('assets') + ->take(10)->get()->shuffle()->first(); + $I->seeResponseContainsJson((new StatuslabelsTransformer)->transformStatuslabel($statuslabel)); + } + + /** @test */ + public function createStatuslabel(ApiTester $I, $scenario) + { + $I->wantTo('Create a new statuslabel'); + + $temp_statuslabel = factory(\App\Models\Statuslabel::class)->make([ + 'name' => "Test Statuslabel Tag", + ]); + + // setup + $data = [ + 'name' => $temp_statuslabel->name, + 'archived' => $temp_statuslabel->archived, + 'deployable' => $temp_statuslabel->deployable, + 'notes' => $temp_statuslabel->notes, + 'pending' => $temp_statuslabel->pending, + 'type' => 'deployable' + ]; + + // create + $I->sendPOST('/statuslabels', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + // Put is routed to the same method in the controller + // DO we actually need to test both? + /** @test */ + public function updateStatuslabelWithPatch(ApiTester $I, $scenario) + { + $I->wantTo('Update an statuslabel with PATCH'); + + // create + $statuslabel = factory(\App\Models\Statuslabel::class)->states('rtd')->create([ + 'name' => 'Original Statuslabel Name', + ]); + $I->assertInstanceOf(\App\Models\Statuslabel::class, $statuslabel); + + $temp_statuslabel = factory(\App\Models\Statuslabel::class)->states('pending')->make([ + 'name' => "updated statuslabel name", + 'type' => 'pending' + ]); + + $data = [ + 'name' => $temp_statuslabel->name, + 'archived' => $temp_statuslabel->archived, + 'deployable' => $temp_statuslabel->deployable, + 'notes' => $temp_statuslabel->notes, + 'pending' => $temp_statuslabel->pending, + 'type' => $temp_statuslabel->type + ]; + + $I->assertNotEquals($statuslabel->name, $data['name']); + + // update + $I->sendPATCH('/statuslabels/' . $statuslabel->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); +// dd($response); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/statuslabels/message.update.success'), $response->messages); + $I->assertEquals($statuslabel->id, $response->payload->id); // statuslabel id does not change + $I->assertEquals($temp_statuslabel->name, $response->payload->name); // statuslabel name updated + // Some manual copying to compare against + $temp_statuslabel->created_at = Carbon::parse($response->payload->created_at); + $temp_statuslabel->updated_at = Carbon::parse($response->payload->updated_at); + $temp_statuslabel->id = $statuslabel->id; + + // verify + $I->sendGET('/statuslabels/' . $statuslabel->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson((new StatuslabelsTransformer)->transformStatuslabel($temp_statuslabel)); + + } + + /** @test */ + public function deleteStatuslabelTest(ApiTester $I, $scenario) + { + $I->wantTo('Delete an statuslabel'); + + // create + $statuslabel = factory(\App\Models\Statuslabel::class)->create([ + 'name' => "Soon to be deleted" + ]); + $I->assertInstanceOf(\App\Models\Statuslabel::class, $statuslabel); + + // delete + $I->sendDELETE('/statuslabels/' . $statuslabel->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/statuslabels/message.delete.success'), $response->messages); + + // verify, expect a 200 + $I->sendGET('/statuslabels/' . $statuslabel->id); + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + } +} diff --git a/tests/api/ApiUsersCest.php b/tests/api/ApiUsersCest.php new file mode 100644 index 0000000000..0744035a97 --- /dev/null +++ b/tests/api/ApiUsersCest.php @@ -0,0 +1,190 @@ +user = \App\Models\User::find(1); + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexUsers(ApiTester $I) + { + $I->wantTo('Get a list of users'); + + // call + $I->sendGET('/users?limit=10&sort=created_at'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse(), true); + // sample verify + $user = App\Models\User::orderByDesc('created_at') + ->withCount('assets', 'licenses', 'accessories', 'consumables') + ->take(10)->get()->shuffle()->first(); + $I->seeResponseContainsJson((new UsersTransformer)->transformUser($user)); + } + + /** @test */ + public function createUser(ApiTester $I, $scenario) + { + $I->wantTo('Create a new user'); + + $temp_user = factory(\App\Models\User::class)->make([ + 'name' => "Test User Name", + ]); + + // setup + $data = [ + 'activated' => $temp_user->activated, + 'address' => $temp_user->address, + 'city' => $temp_user->city, + 'company_id' => $temp_user->company_id, + 'country' => $temp_user->country, + 'department_id' => $temp_user->department_id, + 'email' => $temp_user->email, + 'employee_num' => $temp_user->employee_num, + 'first_name' => $temp_user->first_name, + 'jobtitle' => $temp_user->jobtitle, + 'last_name' => $temp_user->last_name, + 'locale' => $temp_user->locale, + 'location_id' => $temp_user->location_id, + 'notes' => $temp_user->notes, + 'manager_id' => $temp_user->manager_id, + 'password' => $temp_user->password, + 'phone' => $temp_user->phone, + 'state' => $temp_user->state, + 'username' => $temp_user->username, + 'zip' => $temp_user->zip, + ]; + + // create + $I->sendPOST('/users', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + // Put is routed to the same method in the controller + // DO we actually need to test both? + /** @test */ + public function updateUserWithPatch(ApiTester $I, $scenario) + { + $I->wantTo('Update an user with PATCH'); + + // create + $user = factory(\App\Models\User::class)->create([ + 'first_name' => 'Original User Name', + 'company_id' => 2, + 'location_id' => 3 + ]); + $I->assertInstanceOf(\App\Models\User::class, $user); + + $temp_user = factory(\App\Models\User::class)->make([ + 'company_id' => 3, + 'first_name' => "updated user name", + 'location_id' => 1, + ]); + + $data = [ + 'activated' => $temp_user->activated, + 'address' => $temp_user->address, + 'city' => $temp_user->city, + 'company_id' => $temp_user->company_id, + 'country' => $temp_user->country, + 'department_id' => $temp_user->department_id, + 'email' => $temp_user->email, + 'employee_num' => $temp_user->employee_num, + 'first_name' => $temp_user->first_name, + 'jobtitle' => $temp_user->jobtitle, + 'last_name' => $temp_user->last_name, + 'locale' => $temp_user->locale, + 'location_id' => $temp_user->location_id, + 'notes' => $temp_user->notes, + 'manager_id' => $temp_user->manager_id, + 'password' => $temp_user->password, + 'phone' => $temp_user->phone, + 'state' => $temp_user->state, + 'username' => $temp_user->username, + 'zip' => $temp_user->zip, + ]; + + $I->assertNotEquals($user->first_name, $data['first_name']); + + // update + $I->sendPATCH('/users/' . $user->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/users/message.success.update'), $response->messages); + $I->assertEquals($user->id, $response->payload->id); // user id does not change + $I->assertEquals($temp_user->company_id, $response->payload->company->id); // company_id updated + $I->assertEquals($temp_user->first_name, $response->payload->first_name); // user name updated + $I->assertEquals($temp_user->location_id, $response->payload->location->id); // user location_id updated + $temp_user->created_at = Carbon::parse($response->payload->created_at->datetime); + $temp_user->updated_at = Carbon::parse($response->payload->updated_at->datetime); + $temp_user->id = $user->id; + // verify + $I->sendGET('/users/' . $user->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson((new UsersTransformer)->transformUser($temp_user)); + } + + /** @test */ + public function deleteUserTest(ApiTester $I, $scenario) + { + $I->wantTo('Delete an user'); + + // create + $user = factory(\App\Models\User::class)->create([ + 'first_name' => "Soon to be deleted" + ]); + $I->assertInstanceOf(\App\Models\User::class, $user); + + // delete + $I->sendDELETE('/users/' . $user->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + // dd($response); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/users/message.success.delete'), $response->messages); + + // verify, expect a 200 + $I->sendGET('/users/' . $user->id); + + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + } + + /** @test */ + public function fetchUserAssetsTest(ApiTester $I, $scenario) { + $I->wantTo("Fetch assets for a user"); + + $user = User::has('assets')->first(); + $asset = $user->assets->shuffle()->first(); + $I->sendGET("/users/{$user->id}/assets"); + $response = json_decode($I->grabResponse()); + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + + // Just test a random one. + $I->seeResponseContainsJson([ + 'asset_tag' => $asset->asset_tag, + ]); + } +} From 9ee2c6be572c9852331fe45b58900599802a7961 Mon Sep 17 00:00:00 2001 From: Daniel Meltzer Date: Sun, 25 Feb 2018 15:10:02 -0500 Subject: [PATCH 4/6] Api tests2 (#5098) * Cleanup * API tests for asset models and related cleanup/improvements * Api license test. Tests incomplete because create/update/destroy are not implemented yet in the controller * API Category tests. * Manufacturers API Test. * Implement License Create/Update/Delete Methods for API and enable test. * Add missing gate for api. Fixes only superadmins being able to generate Personal Access Toekns --- .../Controllers/Api/AssetModelsController.php | 20 +- .../Controllers/Api/LicensesController.php | 54 ++++- .../Transformers/AssetModelsTransformer.php | 2 +- app/Models/AssetModel.php | 13 +- app/Models/Category.php | 10 +- app/Models/License.php | 37 ++-- app/Models/Manufacturer.php | 9 +- app/Providers/AuthServiceProvider.php | 4 + database/factories/CategoryFactory.php | 4 +- database/factories/LicenseFactory.php | 4 + tests/api/ApiCategoriesCest.php | 145 +++++++++++++ tests/api/ApiLicensesCest.php | 192 ++++++++++++++++++ tests/api/ApiLocationsCest.php | 19 +- tests/api/ApiManufacturersCest.php | 143 +++++++++++++ 14 files changed, 613 insertions(+), 43 deletions(-) create mode 100644 tests/api/ApiCategoriesCest.php create mode 100644 tests/api/ApiLicensesCest.php create mode 100644 tests/api/ApiManufacturersCest.php diff --git a/app/Http/Controllers/Api/AssetModelsController.php b/app/Http/Controllers/Api/AssetModelsController.php index a8916af45e..6a9e0ab166 100644 --- a/app/Http/Controllers/Api/AssetModelsController.php +++ b/app/Http/Controllers/Api/AssetModelsController.php @@ -32,7 +32,21 @@ class AssetModelsController extends Controller $this->authorize('view', AssetModel::class); $allowed_columns = ['id','image','name','model_number','eol','notes','created_at','manufacturer','assets_count']; - $assetmodels = AssetModel::select(['models.id','models.image','models.name','model_number','eol','models.notes','models.created_at','category_id','manufacturer_id','depreciation_id','fieldset_id', 'models.deleted_at']) + $assetmodels = AssetModel::select([ + 'models.id', + 'models.image', + 'models.name', + 'model_number', + 'eol', + 'models.notes', + 'models.created_at', + 'category_id', + 'manufacturer_id', + 'depreciation_id', + 'fieldset_id', + 'models.deleted_at', + 'models.updated_at', + ]) ->with('category','depreciation', 'manufacturer','fieldset') ->withCount('assets'); @@ -137,7 +151,7 @@ class AssetModelsController extends Controller $assetmodel->fieldset_id = $request->get("custom_fieldset_id"); if ($assetmodel->save()) { - return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/assetmodels/message.update.success'))); + return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success'))); } return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors())); @@ -170,7 +184,7 @@ class AssetModelsController extends Controller } $assetmodel->delete(); - return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/assetmodels/message.delete.success'))); + return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.delete.success'))); } diff --git a/app/Http/Controllers/Api/LicensesController.php b/app/Http/Controllers/Api/LicensesController.php index 3d9614caa3..2fb16a8538 100644 --- a/app/Http/Controllers/Api/LicensesController.php +++ b/app/Http/Controllers/Api/LicensesController.php @@ -2,13 +2,15 @@ namespace App\Http\Controllers\Api; -use Illuminate\Http\Request; +use App\Helpers\Helper; use App\Http\Controllers\Controller; -use App\Http\Transformers\LicensesTransformer; use App\Http\Transformers\LicenseSeatsTransformer; +use App\Http\Transformers\LicensesTransformer; +use App\Models\Company; use App\Models\License; use App\Models\LicenseSeat; -use App\Models\Company; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; class LicensesController extends Controller { @@ -121,6 +123,14 @@ class LicensesController extends Controller public function store(Request $request) { // + $this->authorize('create', License::class); + $license = new License; + $license->fill($request->all()); + + if($license->save()) { + return response()->json(Helper::formatStandardApiResponse('success', $license, trans('admin/licenses/message.create.success'))); + } + return response()->json(Helper::formatStandardApiResponse('error', null, $license->getErrors())); } /** @@ -132,13 +142,10 @@ class LicensesController extends Controller */ public function show($id) { - $license = License::find($id); - if (isset($license->id)) { - $license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset'); - $this->authorize('view', $license); - return (new LicensesTransformer)->transformLicense($license); - } - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200); + $this->authorize('view', License::class); + $license = License::findOrFail($id); + $license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset'); + return (new LicensesTransformer)->transformLicense($license); } @@ -154,6 +161,16 @@ class LicensesController extends Controller public function update(Request $request, $id) { // + $this->authorize('edit', License::class); + + $license = License::findOrFail($id); + $license->fill($request->all()); + + if ($license->save()) { + return response()->json(Helper::formatStandardApiResponse('success', $license, trans('admin/licenses/message.update.success'))); + } + + return Helper::formatStandardApiResponse('error', null, $license->getErrors()); } /** @@ -167,6 +184,23 @@ class LicensesController extends Controller public function destroy($id) { // + $license = License::findOrFail($id); + $this->authorize('delete', $license); + + if($license->assigned_seats_count == 0) { + // Delete the license and the associated license seats + DB::table('license_seats') + ->where('id', $license->id) + ->update(array('assigned_to' => null,'asset_id' => null)); + + $licenseSeats = $license->licenseseats(); + $licenseSeats->delete(); + $license->delete(); + + // Redirect to the licenses management page + return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/licenses/message.delete.success'))); + } + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.assoc_users'))); } /** diff --git a/app/Http/Transformers/AssetModelsTransformer.php b/app/Http/Transformers/AssetModelsTransformer.php index f9ba38160e..2bb0a9e1a9 100644 --- a/app/Http/Transformers/AssetModelsTransformer.php +++ b/app/Http/Transformers/AssetModelsTransformer.php @@ -34,7 +34,7 @@ class AssetModelsTransformer 'id' => (int) $assetmodel->depreciation->id, 'name'=> e($assetmodel->depreciation->name) ] : null, - 'assets_count' => $assetmodel->assets_count, + 'assets_count' => (int) $assetmodel->assets_count, 'category' => ($assetmodel->category) ? [ 'id' => (int) $assetmodel->category->id, 'name'=> e($assetmodel->category->name) diff --git a/app/Models/AssetModel.php b/app/Models/AssetModel.php index 71af09422e..1dec88a5b0 100755 --- a/app/Models/AssetModel.php +++ b/app/Models/AssetModel.php @@ -55,7 +55,18 @@ class AssetModel extends SnipeModel * * @var array */ - protected $fillable = ['name','manufacturer_id','category_id','eol', 'user_id', 'fieldset_id', 'model_number', 'notes']; + protected $fillable = [ + 'category_id', + 'depreciation_id', + 'eol', + 'fieldset_id', + 'image', + 'manufacturer_id', + 'model_number', + 'name', + 'notes', + 'user_id', + ]; public function assets() { diff --git a/app/Models/Category.php b/app/Models/Category.php index 6e61be4cd8..63a3104484 100755 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -53,7 +53,15 @@ class Category extends SnipeModel * * @var array */ - protected $fillable = ['name','category_type', 'user_id', 'use_default_eula','checkin_email','require_acceptance']; + protected $fillable = [ + 'category_type', + 'checkin_email', + 'eula_text', + 'name', + 'require_acceptance', + 'use_default_eula', + 'user_id', + ]; public function has_models() diff --git a/app/Models/License.php b/app/Models/License.php index 7ccae0799a..6b22903532 100755 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -40,7 +40,6 @@ class License extends Depreciable 'seats' => 'required|min:1|max:1000000|integer', 'license_email' => 'email|nullable|max:120', 'license_name' => 'string|nullable|max:100', - 'note' => 'string|nullable', 'notes' => 'string|nullable', 'company_id' => 'integer|nullable', ); @@ -51,25 +50,25 @@ class License extends Depreciable * @var array */ protected $fillable = [ - 'name', - 'serial', - 'purchase_date', - 'purchase_cost', - 'order_number', - 'seats', - 'notes', - 'user_id', - 'depreciation_id', - 'license_name', //actually licensed_to - 'license_email', - 'supplier_id', - 'expiration_date', - 'purchase_order', - 'termination_date', - 'maintained', - 'reassignable', 'company_id', - 'manufacturer_id' + 'depreciation_id', + 'expiration_date', + 'license_email', + 'license_name', //actually licensed_to + 'maintained', + 'manufacturer_id', + 'name', + 'notes', + 'order_number', + 'purchase_cost', + 'purchase_date', + 'purchase_order', + 'reassignable', + 'seats', + 'serial', + 'supplier_id', + 'termination_date', + 'user_id', ]; public static function boot() diff --git a/app/Models/Manufacturer.php b/app/Models/Manufacturer.php index 189dae51b5..358cccc48d 100755 --- a/app/Models/Manufacturer.php +++ b/app/Models/Manufacturer.php @@ -40,7 +40,14 @@ class Manufacturer extends SnipeModel * * @var array */ - protected $fillable = ['name','url','support_url','support_phone','support_email']; + protected $fillable = [ + 'name', + 'image', + 'support_email', + 'support_phone', + 'support_url', + 'url', + ]; diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 66666f5603..6783864230 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -125,6 +125,10 @@ class AuthServiceProvider extends ServiceProvider } }); + Gate::define('self.api', function($user) { + return $user->hasAccess('self.api'); + }); + Gate::define('backend.interact', function ($user) { return $user->can('view', \App\Models\Statuslabel::class) || $user->can('view', \App\Models\AssetModel::class) diff --git a/database/factories/CategoryFactory.php b/database/factories/CategoryFactory.php index 0452925435..b7fe01baa7 100644 --- a/database/factories/CategoryFactory.php +++ b/database/factories/CategoryFactory.php @@ -12,11 +12,11 @@ $factory->define(App\Models\Category::class, function (Faker\Generator $faker) { return [ - 'user_id' => 1, + 'checkin_email' => $faker->boolean(), 'eula_text' => $faker->paragraph(), 'require_acceptance' => false, 'use_default_eula' => $faker->boolean(), - 'checkin_email' => $faker->boolean() + 'user_id' => 1, ]; }); diff --git a/database/factories/LicenseFactory.php b/database/factories/LicenseFactory.php index 1cebafef1f..32155fdce1 100644 --- a/database/factories/LicenseFactory.php +++ b/database/factories/LicenseFactory.php @@ -19,6 +19,10 @@ $factory->define(App\Models\License::class, function (Faker\Generator $faker) { 'notes' => 'Created by DB seeder', 'purchase_date' => $faker->dateTimeBetween('-1 years','now', date_default_timezone_get()), 'order_number' => $faker->numberBetween(1000000, 50000000), + 'expiration_date' => $faker->dateTimeBetween('now', '+3 years', date_default_timezone_get())->format('Y-m-d H:i:s'), + 'reassignable' => $faker->boolean(), + 'termination_date' => $faker->dateTimeBetween('-1 years','now', date_default_timezone_get())->format('Y-m-d H:i:s'), + 'supplier_id' => $faker->numberBetween(1,5), ]; }); diff --git a/tests/api/ApiCategoriesCest.php b/tests/api/ApiCategoriesCest.php new file mode 100644 index 0000000000..c7642973bc --- /dev/null +++ b/tests/api/ApiCategoriesCest.php @@ -0,0 +1,145 @@ +user = \App\Models\User::find(1); + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexCategorys(ApiTester $I) + { + + $I->wantTo('Get a list of categories'); + + // call + $I->sendGET('/categories?order_by=id&limit=10'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse(), true); + // dd($response); + // sample verify + $category = App\Models\Category::withCount('assets','accessories','consumables','components') + ->orderByDesc('created_at')->take(10)->get()->shuffle()->first(); + + $I->seeResponseContainsJson((new CategoriesTransformer)->transformCategory($category)); + } + + /** @test */ + public function createCategory(ApiTester $I, $scenario) + { + $I->wantTo('Create a new category'); + + $temp_category = factory(\App\Models\Category::class)->states('asset-laptop-category')->make([ + 'name' => "Test Category Tag", + ]); + + // setup + $data = [ + 'category_type' => $temp_category->category_type, + 'checkin_email' => $temp_category->checkin_email, + 'eula_text' => $temp_category->eula_text, + 'name' => $temp_category->name, + 'require_acceptance' => $temp_category->require_acceptance, + 'use_default_eula' => $temp_category->use_default_eula, + ]; + + // create + $I->sendPOST('/categories', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + // Put is routed to the same method in the controller + // DO we actually need to test both? + /** @test */ + public function updateCategoryWithPatch(ApiTester $I, $scenario) + { + $I->wantTo('Update an category with PATCH'); + + // create + $category = factory(\App\Models\Category::class)->states('asset-laptop-category') + ->create([ + 'name' => 'Original Category Name', + ]); + $I->assertInstanceOf(\App\Models\Category::class, $category); + + $temp_category = factory(\App\Models\Category::class) + ->states('accessory-mouse-category')->make([ + 'name' => "updated category name", + ]); + + $data = [ + 'category_type' => $temp_category->category_type, + 'checkin_email' => $temp_category->checkin_email, + 'eula_text' => $temp_category->eula_text, + 'name' => $temp_category->name, + 'require_acceptance' => $temp_category->require_acceptance, + 'use_default_eula' => $temp_category->use_default_eula, + ]; + + $I->assertNotEquals($category->name, $data['name']); + + // update + $I->sendPATCH('/categories/' . $category->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/categories/message.update.success'), $response->messages); + $I->assertEquals($category->id, $response->payload->id); // category id does not change + $I->assertEquals($temp_category->name, $response->payload->name); // category name updated + // Some manual copying to compare against + $temp_category->created_at = Carbon::parse($response->payload->created_at); + $temp_category->updated_at = Carbon::parse($response->payload->updated_at); + $temp_category->id = $category->id; + + // verify + $I->sendGET('/categories/' . $category->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson((new CategoriesTransformer)->transformCategory($temp_category)); + + } + + /** @test */ + public function deleteCategoryTest(ApiTester $I, $scenario) + { + $I->wantTo('Delete an category'); + + // create + $category = factory(\App\Models\Category::class)->states('asset-laptop-category')->create([ + 'name' => "Soon to be deleted" + ]); + $I->assertInstanceOf(\App\Models\Category::class, $category); + + // delete + $I->sendDELETE('/categories/' . $category->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/categories/message.delete.success'), $response->messages); + + // verify, expect a 200 + $I->sendGET('/categories/' . $category->id); + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + } +} diff --git a/tests/api/ApiLicensesCest.php b/tests/api/ApiLicensesCest.php new file mode 100644 index 0000000000..c38536ec10 --- /dev/null +++ b/tests/api/ApiLicensesCest.php @@ -0,0 +1,192 @@ +user = \App\Models\User::find(1); + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexLicenses(ApiTester $I) + { + $I->wantTo('Get a list of licenses'); + + // call + $I->sendGET('/licenses?limit=10&sort=created_at'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse(), true); + // sample verify + $license = App\Models\License::orderByDesc('created_at') + ->withCount('freeSeats') + ->take(10)->get()->shuffle()->first(); + $I->seeResponseContainsJson((new LicensesTransformer)->transformLicense($license)); + } + + /** @test */ + public function createLicense(ApiTester $I, $scenario) + { + $I->wantTo('Create a new license'); + + $temp_license = factory(\App\Models\License::class)->states('acrobat')->make([ + 'name' => "Test License Name", + 'depreciation_id' => 3, + 'company_id' => 2 + ]); + + // setup + $data = [ + 'company_id' => $temp_license->company_id, + 'depreciation_id' => $temp_license->depreciation_id, + 'expiration_date' => $temp_license->expiration_date, + 'license_email' => $temp_license->license_email, + 'license_name' => $temp_license->license_name, + 'maintained' => $temp_license->maintained, + 'manufacturer_id' => $temp_license->manufacturer_id, + 'name' => $temp_license->name, + 'notes' => $temp_license->notes, + 'order_number' => $temp_license->order_number, + 'purchase_cost' => $temp_license->purchase_cost, + 'purchase_date' => $temp_license->purchase_date, + 'purchase_order' => $temp_license->purchase_order, + 'reassignable' => $temp_license->reassignable, + 'seats' => $temp_license->seats, + 'serial' => $temp_license->serial, + 'supplier_id' => $temp_license->supplier_id, + 'termination_date' => $temp_license->termination_date, + ]; + + // create + $I->sendPOST('/licenses', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + // Put is routed to the same method in the controller + // DO we actually need to test both? + /** @test */ + public function updateLicenseWithPatch(ApiTester $I, $scenario) + { + $I->wantTo('Update an license with PATCH'); + + // create + $license = factory(\App\Models\License::class)->states('acrobat')->create([ + 'name' => 'Original License Name', + 'depreciation_id' => 3, + 'company_id' => 2 + ]); + $I->assertInstanceOf(\App\Models\License::class, $license); + + $temp_license = factory(\App\Models\License::class)->states('office')->make([ + 'company_id' => 3, + 'depreciation_id' => 2, + 'company_id' => 4 + ]); + + $data = [ + 'company_id' => $temp_license->company_id, + 'depreciation_id' => $temp_license->depreciation_id, + 'expiration_date' => $temp_license->expiration_date, + 'license_email' => $temp_license->license_email, + 'license_name' => $temp_license->license_name, + 'maintained' => $temp_license->maintained, + 'manufacturer_id' => $temp_license->manufacturer_id, + 'name' => $temp_license->name, + 'notes' => $temp_license->notes, + 'order_number' => $temp_license->order_number, + 'purchase_cost' => $temp_license->purchase_cost, + 'purchase_date' => $temp_license->purchase_date, + 'purchase_order' => $temp_license->purchase_order, + 'reassignable' => $temp_license->reassignable, + 'seats' => $temp_license->seats, + 'serial' => $temp_license->serial, + 'supplier_id' => $temp_license->supplier_id, + 'termination_date' => $temp_license->termination_date, + ]; + + $I->assertNotEquals($license->name, $data['name']); + + // update + $I->sendPATCH('/licenses/' . $license->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages); + $I->assertEquals($license->id, $response->payload->id); // license id does not change + $I->assertEquals($temp_license->name, $response->payload->name); // license name + $temp_license->created_at = Carbon::parse($response->payload->created_at); + $temp_license->updated_at = Carbon::parse($response->payload->updated_at); + $temp_license->id = $license->id; + // verify + $I->sendGET('/licenses/' . $license->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson((new LicensesTransformer)->transformLicense($temp_license)); + } + + /** @test */ + public function deleteLicenseWithUsersTest(ApiTester $I, $scenario) + { + $I->wantTo('Ensure a license with seats checked out cannot be deleted'); + + // create + $license = factory(\App\Models\License::class)->states('acrobat')->create([ + 'name' => "Soon to be deleted" + ]); + $licenseSeat = $license->freeSeat(); + $licenseSeat->assigned_to = $this->user->id; + $licenseSeat->save(); + $I->assertInstanceOf(\App\Models\License::class, $license); + + // delete + $I->sendDELETE('/licenses/' . $license->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('error', $response->status); + $I->assertEquals(trans('admin/licenses/message.assoc_users'), $response->messages); + } + + /** @test */ + public function deleteLicenseTest(ApiTester $I, $scenario) + { + $I->wantTo('Delete an license'); + + // create + $license = factory(\App\Models\License::class)->states('acrobat')->create([ + 'name' => "Soon to be deleted" + ]); + $I->assertInstanceOf(\App\Models\License::class, $license); + + // delete + $I->sendDELETE('/licenses/' . $license->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/licenses/message.delete.success'), $response->messages); + + // verify, expect a 200 + $I->sendGET('/licenses/' . $license->id); + + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + } +} diff --git a/tests/api/ApiLocationsCest.php b/tests/api/ApiLocationsCest.php index b30bb317e5..4ab3978e92 100644 --- a/tests/api/ApiLocationsCest.php +++ b/tests/api/ApiLocationsCest.php @@ -14,8 +14,6 @@ class ApiLocationsCest public function _before(ApiTester $I) { $this->user = \App\Models\User::find(1); - $this->timeFormat = Setting::getSettings()->date_display_format .' '. Setting::getSettings()->time_display_format; - $this->dateFormat = Setting::getSettings()->date_display_format; $I->haveHttpHeader('Accept', 'application/json'); $I->amBearerAuthenticated($I->getToken($this->user)); } @@ -27,7 +25,7 @@ class ApiLocationsCest $I->wantTo('Get a list of locations'); // call - $I->sendGET('/locations?limit=1'); + $I->sendGET('/locations?limit=10'); $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); @@ -35,7 +33,7 @@ class ApiLocationsCest // sample verify $location = App\Models\Location::orderByDesc('created_at') ->withCount('assignedAssets', 'assets', 'users') - ->take(1)->get()->shuffle()->first(); + ->take(10)->get()->shuffle()->first(); $I->seeResponseContainsJson((new LocationsTransformer)->transformLocation($location)); } @@ -51,6 +49,17 @@ class ApiLocationsCest // setup $data = [ 'name' => $temp_location->name, + 'image' => $temp_location->image, + 'address' => $temp_location->address, + 'address2' => $temp_location->address2, + 'city' => $temp_location->city, + 'state' => $temp_location->state, + 'country' => $temp_location->country, + 'zip' => $temp_location->zip, + 'parent_id' => $temp_location->parent_id, + 'parent_id' => $temp_location->parent_id, + 'manager_id' => $temp_location->manager_id, + 'currency' => $temp_location->currency ]; // create @@ -109,12 +118,12 @@ class ApiLocationsCest $temp_location->created_at = Carbon::parse($response->payload->created_at->datetime); $temp_location->updated_at = Carbon::parse($response->payload->updated_at->datetime); $temp_location->id = $location->id; + // verify $I->sendGET('/locations/' . $location->id); $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); $I->seeResponseContainsJson((new LocationsTransformer)->transformLocation($temp_location)); - // $I->seeResponseContainsJson($this->generateJsonResponse($temp_location, $location)); } /** @test */ diff --git a/tests/api/ApiManufacturersCest.php b/tests/api/ApiManufacturersCest.php new file mode 100644 index 0000000000..4338f09ef2 --- /dev/null +++ b/tests/api/ApiManufacturersCest.php @@ -0,0 +1,143 @@ +user = \App\Models\User::find(1); + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexManufacturers(ApiTester $I) + { + + $I->wantTo('Get a list of manufacturers'); + + // call + $I->sendGET('/manufacturers?order_by=id&limit=10'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse(), true); + // sample verify + $manufacturer = App\Models\Manufacturer::withCount('assets','accessories','consumables','licenses') + ->orderByDesc('created_at')->take(10)->get()->shuffle()->first(); + + $I->seeResponseContainsJson((new ManufacturersTransformer)->transformManufacturer($manufacturer)); + } + + /** @test */ + public function createManufacturer(ApiTester $I, $scenario) + { + $I->wantTo('Create a new manufacturer'); + + $temp_manufacturer = factory(\App\Models\Manufacturer::class)->states('apple')->make([ + 'name' => "Test Manufacturer Tag", + ]); + + // setup + $data = [ + 'image' => $temp_manufacturer->image, + 'name' => $temp_manufacturer->name, + 'support_email' => $temp_manufacturer->support_email, + 'support_phone' => $temp_manufacturer->support_phone, + 'support_url' => $temp_manufacturer->support_url, + 'url' => $temp_manufacturer->url, + ]; + + // create + $I->sendPOST('/manufacturers', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + // Put is routed to the same method in the controller + // DO we actually need to test both? + /** @test */ + public function updateManufacturerWithPatch(ApiTester $I, $scenario) + { + $I->wantTo('Update an manufacturer with PATCH'); + + // create + $manufacturer = factory(\App\Models\Manufacturer::class)->states('apple') + ->create([ + 'name' => 'Original Manufacturer Name', + ]); + $I->assertInstanceOf(\App\Models\Manufacturer::class, $manufacturer); + + $temp_manufacturer = factory(\App\Models\Manufacturer::class)->states('dell')->make([ + 'name' => "updated manufacturer name", + ]); + + $data = [ + 'image' => $temp_manufacturer->image, + 'name' => $temp_manufacturer->name, + 'support_email' => $temp_manufacturer->support_email, + 'support_phone' => $temp_manufacturer->support_phone, + 'support_url' => $temp_manufacturer->support_url, + 'url' => $temp_manufacturer->url, + ]; + + $I->assertNotEquals($manufacturer->name, $data['name']); + + // update + $I->sendPATCH('/manufacturers/' . $manufacturer->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/manufacturers/message.update.success'), $response->messages); + $I->assertEquals($manufacturer->id, $response->payload->id); // manufacturer id does not change + $I->assertEquals($temp_manufacturer->name, $response->payload->name); // manufacturer name updated + // Some manual copying to compare against + $temp_manufacturer->created_at = Carbon::parse($response->payload->created_at); + $temp_manufacturer->updated_at = Carbon::parse($response->payload->updated_at); + $temp_manufacturer->id = $manufacturer->id; + + // verify + $I->sendGET('/manufacturers/' . $manufacturer->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson((new ManufacturersTransformer)->transformManufacturer($temp_manufacturer)); + + } + + /** @test */ + public function deleteManufacturerTest(ApiTester $I, $scenario) + { + $I->wantTo('Delete an manufacturer'); + + // create + $manufacturer = factory(\App\Models\Manufacturer::class)->states('apple')->create([ + 'name' => "Soon to be deleted" + ]); + $I->assertInstanceOf(\App\Models\Manufacturer::class, $manufacturer); + + // delete + $I->sendDELETE('/manufacturers/' . $manufacturer->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/manufacturers/message.delete.success'), $response->messages); + + // verify, expect a 200 + $I->sendGET('/manufacturers/' . $manufacturer->id); + $I->seeResponseCodeIs(200); + $I->seeResponseIsJson(); + } +} From b4f704d7f175ebefeca3d499e1a60aab3eaacbbc Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 26 Feb 2018 15:43:49 -0800 Subject: [PATCH 5/6] Fixed 2FA reset button --- app/Http/Controllers/Api/UsersController.php | 28 ++++++++++++++++++++ app/Http/Controllers/UsersController.php | 17 ------------ resources/views/users/edit.blade.php | 4 +++ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index d4d0339f09..5d075b32d2 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -290,4 +290,32 @@ class UsersController extends Controller $assets = Asset::where('assigned_to', '=', $id)->with('model')->get(); return (new AssetsTransformer)->transformAssets($assets, $assets->count()); } + + /** + * Reset the user's two-factor status + * + * @author [A. Gianotto] [] + * @since [v3.0] + * @param $userId + * @return string JSON + */ + public function postTwoFactorReset(Request $request) + { + + $this->authorize('edit', User::class); + + if ($request->has('id')) { + try { + $user = User::find($request->get('id')); + $user->two_factor_secret = null; + $user->two_factor_enrolled = 0; + $user->save(); + return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200); + } catch (\Exception $e) { + return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500); + } + } + return response()->json(['message' => 'No ID provided'], 500); + + } } diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php index 27f18adf88..2b6f0c008a 100755 --- a/app/Http/Controllers/UsersController.php +++ b/app/Http/Controllers/UsersController.php @@ -1138,23 +1138,6 @@ class UsersController extends Controller } - public function postTwoFactorReset(Request $request) - { - if (Gate::denies('users.edit')) { - return response()->json(['message' => trans('general.insufficient_permissions')], 500); - } - - try { - $user = User::find($request->get('id')); - $user->two_factor_secret = null; - $user->two_factor_enrolled = 0; - $user->save(); - return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200); - } catch (\Exception $e) { - return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500); - } - } - /** * LDAP form processing. * diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php index e84d5c175b..420ab124ab 100755 --- a/resources/views/users/edit.blade.php +++ b/resources/views/users/edit.blade.php @@ -627,6 +627,10 @@ $(document).ready(function() { url: '{{ route('api.users.two_factor_reset', ['id'=> $user->id]) }}', type: 'POST', data: {}, + headers: { + "X-Requested-With": 'XMLHttpRequest', + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + }, dataType: 'json', success: function (data) { From e3fb4f8799d9a342ef6d59bf8d0f8faa2f9bf50f Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 26 Feb 2018 15:44:36 -0800 Subject: [PATCH 6/6] Bumped hash --- config/version.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/version.php b/config/version.php index 4e6f60c8af..8547150aa6 100644 --- a/config/version.php +++ b/config/version.php @@ -1,10 +1,10 @@ 'v4.1.14', - 'full_app_version' => 'v4.1.14 - build 3391-g0f23462', - 'build_version' => '3391', + 'full_app_version' => 'v4.1.14 - build 3408-ga705c71', + 'build_version' => '3408', 'prerelease_version' => '', - 'hash_version' => 'g0f23462', - 'full_hash' => 'v4.1.13-5-g0f23462', + 'hash_version' => 'ga705c71', + 'full_hash' => 'v4.1.14-17-ga705c71', 'branch' => 'develop', );