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 de5c04479..6bb7ca309 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 937a3cb7b..209a222a4 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 fbbb9d42e..82387f50b 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 002fea21d..6f054b5dd 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 322dc93f5..8c34badd1 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 000000000..740964204 --- /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 000000000..3aa2d054c --- /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 6d8c52d72..85019ad90 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 6bb7ca309..dbbc2f502 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 8e557df6a..4b18a259e 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 29748b9c2..a3c698fa7 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 f5b7ce08e..d4d0339f0 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 b4143ec58..fd6832e00 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 21b794c71..38610277b 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 564d9d80b..26c8b615f 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 75b1b8628..ce6d06906 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 bfaba26c4..6a700b973 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 9a31beb43..92f3caf9a 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 eb79b53eb..a26897817 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 000000000..23b7c4296 --- /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 ea41b99f7..5a14120ff 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 000000000..daf56fc5d --- /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 000000000..35653b4e9 --- /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 d7e05f4a7..33120be95 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 000000000..d7938195b --- /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 000000000..b30bb317e --- /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 000000000..d3f02e843 --- /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 000000000..0744035a9 --- /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 a8916af45..6a9e0ab16 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 3d9614caa..2fb16a853 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 f9ba38160..2bb0a9e1a 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 71af09422..1dec88a5b 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 6e61be4cd..63a310448 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 7ccae0799..6b2290353 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 189dae51b..358cccc48 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 66666f560..678386423 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 045292543..b7fe01baa 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 1cebafef1..32155fdce 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 000000000..c7642973b --- /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 000000000..c38536ec1 --- /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 b30bb317e..4ab3978e9 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 000000000..4338f09ef --- /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 d4d0339f0..5d075b32d 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 27f18adf8..2b6f0c008 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 e84d5c175..420ab124a 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 4e6f60c8a..8547150aa 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', );