mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-25 05:34:06 -08:00
Merge remote-tracking branch 'origin/develop'
This commit is contained in:
commit
b2a69efc9d
|
@ -87,6 +87,7 @@ SESSION_LIFETIME=12000
|
|||
EXPIRE_ON_CLOSE=false
|
||||
ENCRYPT=false
|
||||
COOKIE_NAME=snipeit_session
|
||||
PASSPORT_COOKIE_NAME='snipeit_passport_token'
|
||||
COOKIE_DOMAIN=null
|
||||
SECURE_COOKIES=false
|
||||
API_TOKEN_EXPIRATION_YEARS=15
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
|||
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use App\Http\Requests\StoreAssetRequest;
|
||||
use App\Http\Requests\UpdateAssetRequest;
|
||||
use App\Http\Traits\MigratesLegacyAssetLocations;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\LicenseSeat;
|
||||
|
@ -651,36 +652,35 @@ class AssetsController extends Controller
|
|||
* Accepts a POST request to update an asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $id) : JsonResponse
|
||||
public function update(UpdateAssetRequest $request, Asset $asset): JsonResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
$asset->fill($request->validated());
|
||||
|
||||
if ($asset = Asset::find($id)) {
|
||||
$asset->fill($request->all());
|
||||
if ($request->has('model_id')) {
|
||||
$asset->model()->associate(AssetModel::find($request->validated()['model_id']));
|
||||
}
|
||||
if ($request->has('company_id')) {
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->validated()['company_id']);
|
||||
}
|
||||
if ($request->has('rtd_location_id') && !$request->has('location_id')) {
|
||||
$asset->location_id = $request->validated()['rtd_location_id'];
|
||||
}
|
||||
if ($request->input('last_audit_date')) {
|
||||
$asset->last_audit_date = Carbon::parse($request->input('last_audit_date'))->startOfDay()->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
($request->filled('model_id')) ?
|
||||
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : null;
|
||||
($request->filled('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : '';
|
||||
($request->filled('company_id')) ?
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : '';
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
if ($request->has('image_source')) {
|
||||
$request->offsetSet('image', $request->offsetGet('image_source'));
|
||||
}
|
||||
|
||||
($request->filled('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : null;
|
||||
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
if ($request->has('image_source')) {
|
||||
$request->offsetSet('image', $request->offsetGet('image_source'));
|
||||
}
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
$model = AssetModel::find($asset->model_id);
|
||||
$asset = $request->handleImages($asset);
|
||||
$model = $asset->model;
|
||||
|
||||
// Update custom fields
|
||||
$problems_updating_encrypted_custom_fields = false;
|
||||
|
@ -706,15 +706,13 @@ class AssetsController extends Controller
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($asset->save()) {
|
||||
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
|
||||
$location = $target->location_id;
|
||||
|
||||
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $id)
|
||||
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
|
||||
->update(['location_id' => $target->location_id]);
|
||||
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
|
||||
$location = $target->id;
|
||||
|
@ -728,17 +726,13 @@ class AssetsController extends Controller
|
|||
$asset->image = $asset->getImageUrl();
|
||||
}
|
||||
|
||||
if ($problems_updating_encrypted_custom_fields) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
|
||||
} else {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
|
||||
}
|
||||
if ($problems_updating_encrypted_custom_fields) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
|
||||
} else {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ use App\Models\Asset;
|
|||
use App\Models\User;
|
||||
use App\Notifications\FirstAdminNotification;
|
||||
use App\Notifications\MailTest;
|
||||
use Illuminate\Http\Client\HttpClientException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
@ -129,11 +128,11 @@ class SettingsController extends Controller
|
|||
protected function dotEnvFileIsExposed() : bool
|
||||
{
|
||||
try {
|
||||
return Http::timeout(10)
|
||||
return Http::withoutVerifying()->timeout(10)
|
||||
->accept('*/*')
|
||||
->get(URL::to('.env'))
|
||||
->successful();
|
||||
} catch (HttpClientException $e) {
|
||||
} catch (\Exception $e) {
|
||||
Log::debug($e->getMessage());
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -20,5 +20,5 @@ class EncryptCookies extends BaseEncrypter
|
|||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $serialize = true;
|
||||
protected static $serialize = false;
|
||||
}
|
||||
|
|
44
app/Http/Requests/UpdateAssetRequest.php
Normal file
44
app/Http/Requests/UpdateAssetRequest.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateAssetRequest extends ImageUploadRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return Gate::allows('update', $this->asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = array_merge(
|
||||
parent::rules(),
|
||||
(new Asset)->getRules(),
|
||||
// this is to overwrite rulesets that include required, and rewrite unique_undeleted
|
||||
[
|
||||
'model_id' => ['integer', 'exists:models,id,deleted_at,NULL', 'not_array'],
|
||||
'status_id' => ['integer', 'exists:status_labels,id'],
|
||||
'asset_tag' => [
|
||||
'min:1', 'max:255', 'not_array',
|
||||
Rule::unique('assets', 'asset_tag')->ignore($this->asset)->withoutTrashed()
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
|
@ -97,35 +97,38 @@ class Asset extends Depreciable
|
|||
];
|
||||
|
||||
protected $rules = [
|
||||
'model_id' => 'required|integer|exists:models,id,deleted_at,NULL|not_array',
|
||||
'status_id' => 'required|integer|exists:status_labels,id',
|
||||
'asset_tag' => 'required|min:1|max:255|unique_undeleted:assets,asset_tag|not_array',
|
||||
'name' => 'nullable|max:255',
|
||||
'company_id' => 'nullable|integer|exists:companies,id',
|
||||
'warranty_months' => 'nullable|numeric|digits_between:0,240',
|
||||
'last_checkout' => 'nullable|date_format:Y-m-d H:i:s',
|
||||
'last_checkin' => 'nullable|date_format:Y-m-d H:i:s',
|
||||
'expected_checkin' => 'nullable|date',
|
||||
'last_audit_date' => 'nullable|date_format:Y-m-d H:i:s',
|
||||
// 'next_audit_date' => 'nullable|date|after:last_audit_date',
|
||||
'next_audit_date' => 'nullable|date',
|
||||
'location_id' => 'nullable|exists:locations,id',
|
||||
'rtd_location_id' => 'nullable|exists:locations,id',
|
||||
'purchase_date' => 'nullable|date|date_format:Y-m-d',
|
||||
'serial' => 'nullable|unique_undeleted:assets,serial',
|
||||
'purchase_cost' => 'nullable|numeric|gte:0',
|
||||
'supplier_id' => 'nullable|exists:suppliers,id',
|
||||
'asset_eol_date' => 'nullable|date',
|
||||
'eol_explicit' => 'nullable|boolean',
|
||||
'byod' => 'nullable|boolean',
|
||||
'order_number' => 'nullable|string|max:191',
|
||||
'notes' => 'nullable|string|max:65535',
|
||||
'assigned_to' => 'nullable|integer',
|
||||
'requestable' => 'nullable|boolean',
|
||||
'model_id' => ['required', 'integer', 'exists:models,id,deleted_at,NULL', 'not_array'],
|
||||
'status_id' => ['required', 'integer', 'exists:status_labels,id'],
|
||||
'asset_tag' => ['required', 'min:1', 'max:255', 'unique_undeleted:assets,asset_tag', 'not_array'],
|
||||
'name' => ['nullable', 'max:255'],
|
||||
'company_id' => ['nullable', 'integer', 'exists:companies,id'],
|
||||
'warranty_months' => ['nullable', 'numeric', 'digits_between:0,240'],
|
||||
'last_checkout' => ['nullable', 'date_format:Y-m-d H:i:s'],
|
||||
'last_checkin' => ['nullable', 'date_format:Y-m-d H:i:s'],
|
||||
'expected_checkin' => ['nullable', 'date'],
|
||||
'last_audit_date' => ['nullable', 'date_format:Y-m-d H:i:s'],
|
||||
'next_audit_date' => ['nullable', 'date'],
|
||||
//'after:last_audit_date'],
|
||||
'location_id' => ['nullable', 'exists:locations,id'],
|
||||
'rtd_location_id' => ['nullable', 'exists:locations,id'],
|
||||
'purchase_date' => ['nullable', 'date', 'date_format:Y-m-d'],
|
||||
'serial' => ['nullable', 'unique_undeleted:assets,serial'],
|
||||
'purchase_cost' => ['nullable', 'numeric', 'gte:0'],
|
||||
'supplier_id' => ['nullable', 'exists:suppliers,id'],
|
||||
'asset_eol_date' => ['nullable', 'date'],
|
||||
'eol_explicit' => ['nullable', 'boolean'],
|
||||
'byod' => ['nullable', 'boolean'],
|
||||
'order_number' => ['nullable', 'string', 'max:191'],
|
||||
'notes' => ['nullable', 'string', 'max:65535'],
|
||||
'assigned_to' => ['nullable', 'integer'],
|
||||
'requestable' => ['nullable', 'boolean'],
|
||||
'assigned_user' => ['nullable', 'exists:users,id,deleted_at,NULL'],
|
||||
'assigned_location' => ['nullable', 'exists:locations,id,deleted_at,NULL'],
|
||||
'assigned_asset' => ['nullable', 'exists:assets,id,deleted_at,NULL']
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
|
|
|
@ -87,11 +87,11 @@ class AuthServiceProvider extends ServiceProvider
|
|||
]);
|
||||
|
||||
$this->registerPolicies();
|
||||
//Passport::routes(); //this is no longer required in newer passport versions
|
||||
Passport::tokensExpireIn(Carbon::now()->addYears(config('passport.expiration_years')));
|
||||
Passport::refreshTokensExpireIn(Carbon::now()->addYears(config('passport.expiration_years')));
|
||||
Passport::personalAccessTokensExpireIn(Carbon::now()->addYears(config('passport.expiration_years')));
|
||||
Passport::withCookieSerialization();
|
||||
|
||||
Passport::cookie(config('passport.cookie_name'));
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -66,7 +66,6 @@ class ValidationServiceProvider extends ServiceProvider
|
|||
* `unique_undeleted:table,fieldname` in your rules out of the box
|
||||
*/
|
||||
Validator::extend('unique_undeleted', function ($attribute, $value, $parameters, $validator) {
|
||||
|
||||
if (count($parameters)) {
|
||||
|
||||
// This is a bit of a shim, but serial doesn't have any other rules around it other than that it's nullable
|
||||
|
|
|
@ -14,4 +14,5 @@ return [
|
|||
'private_key' => env('PASSPORT_PRIVATE_KEY'),
|
||||
'public_key' => env('PASSPORT_PUBLIC_KEY'),
|
||||
'expiration_years' => env('API_TOKEN_EXPIRATION_YEARS', 20),
|
||||
'cookie_name' => env('PASSPORT_COOKIE_NAME', 'snipeit_passport_token'),
|
||||
];
|
||||
|
|
|
@ -48,6 +48,7 @@ class AssetFactory extends Factory
|
|||
'assigned_type' => null,
|
||||
'next_audit_date' => null,
|
||||
'last_checkout' => null,
|
||||
'asset_eol_date' => null
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -354,6 +355,17 @@ class AssetFactory extends Factory
|
|||
return $this->state(['requestable' => false]);
|
||||
}
|
||||
|
||||
public function noPurchaseOrEolDate()
|
||||
{
|
||||
return $this->afterCreating(function (Asset $asset) {
|
||||
$asset->update([
|
||||
'purchase_date' => null,
|
||||
'asset_eol_date' => null
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function hasEncryptedCustomField(CustomField $field = null)
|
||||
{
|
||||
return $this->state(function () use ($field) {
|
||||
|
@ -372,7 +384,6 @@ class AssetFactory extends Factory
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This allows bypassing model level validation if you want to purposefully
|
||||
* create an asset in an invalid state. Validation is turned back on
|
||||
|
|
|
@ -25,7 +25,13 @@ class LocationFactory extends Factory
|
|||
'image' => rand(1, 9).'.jpg',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// one of these can eventuall go away - left temporarily for conflict resolution
|
||||
public function deleted(): self
|
||||
{
|
||||
return $this->state(['deleted_at' => $this->faker->dateTime()]);
|
||||
}
|
||||
|
||||
public function deletedLocation()
|
||||
{
|
||||
return $this->state(function () {
|
||||
|
|
|
@ -309,4 +309,9 @@ class UserFactory extends Factory
|
|||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function deleted(): self
|
||||
{
|
||||
return $this->state(['deleted_at' => $this->faker->dateTime()]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ return [
|
|||
'clone' => 'Clone :item_type',
|
||||
'edit' => 'Edit :item_type',
|
||||
'delete' => 'Delete :item_type',
|
||||
'restore' => 'Delete :item_type',
|
||||
'restore' => 'Restore :item_type',
|
||||
'create' => 'Create New :item_type',
|
||||
'checkout' => 'Checkout :item_type',
|
||||
'checkin' => 'Checkin :item_type',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Api;
|
||||
// use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
||||
|
@ -571,19 +570,22 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
|
|||
'destroy'
|
||||
]
|
||||
)->name('api.assets.files.destroy');
|
||||
|
||||
});
|
||||
|
||||
Route::resource('hardware',
|
||||
// pulling this out of resource route group to begin normalizing for route-model binding.
|
||||
// this would probably keep working with the resource route group, but the general practice is for
|
||||
// the model name to be the parameter - and i think it's a good differentiation in the code while we convert the others.
|
||||
Route::patch('/hardware/{asset}', [Api\AssetsController::class, 'update'])->name('api.assets.update');
|
||||
|
||||
Route::resource('hardware',
|
||||
Api\AssetsController::class,
|
||||
['names' => [
|
||||
'index' => 'api.assets.index',
|
||||
'show' => 'api.assets.show',
|
||||
'update' => 'api.assets.update',
|
||||
'store' => 'api.assets.store',
|
||||
'destroy' => 'api.assets.destroy',
|
||||
],
|
||||
'except' => ['create', 'edit'],
|
||||
'except' => ['create', 'edit', 'update'],
|
||||
'parameters' => ['asset' => 'asset_id'],
|
||||
]
|
||||
); // end assets API routes
|
||||
|
|
|
@ -3,13 +3,268 @@
|
|||
namespace Tests\Feature\Assets\Api;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Company;
|
||||
use App\Models\Location;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use App\Models\CustomField;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UpdateAssetTest extends TestCase
|
||||
{
|
||||
public function testThatANonExistentAssetIdReturnsError()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->editAssets()->createAssets()->create())
|
||||
->patchJson(route('api.assets.update', 123456789))
|
||||
->assertStatusMessageIs('error');
|
||||
}
|
||||
|
||||
public function testRequiresPermissionToUpdateAsset()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testGivenPermissionUpdateAssetIsAllowed()
|
||||
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'name' => 'test'
|
||||
])
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function testAllAssetAttributesAreStored()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$user = User::factory()->editAssets()->create();
|
||||
$userAssigned = User::factory()->create();
|
||||
$company = Company::factory()->create();
|
||||
$location = Location::factory()->create();
|
||||
$model = AssetModel::factory()->create();
|
||||
$rtdLocation = Location::factory()->create();
|
||||
$status = Statuslabel::factory()->create();
|
||||
$supplier = Supplier::factory()->create();
|
||||
|
||||
$response = $this->actingAsForApi($user)
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'asset_eol_date' => '2024-06-02',
|
||||
'asset_tag' => 'random_string',
|
||||
'assigned_user' => $userAssigned->id,
|
||||
'company_id' => $company->id,
|
||||
'last_audit_date' => '2023-09-03 12:23:45',
|
||||
'location_id' => $location->id,
|
||||
'model_id' => $model->id,
|
||||
'name' => 'A New Asset',
|
||||
'notes' => 'Some notes',
|
||||
'order_number' => '5678',
|
||||
'purchase_cost' => '123.45',
|
||||
'purchase_date' => '2023-09-02',
|
||||
'requestable' => true,
|
||||
'rtd_location_id' => $rtdLocation->id,
|
||||
'serial' => '1234567890',
|
||||
'status_id' => $status->id,
|
||||
'supplier_id' => $supplier->id,
|
||||
'warranty_months' => 10,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success')
|
||||
->json();
|
||||
|
||||
$updatedAsset = Asset::find($response['payload']['id']);
|
||||
|
||||
$this->assertEquals('2024-06-02', $updatedAsset->asset_eol_date);
|
||||
$this->assertEquals('random_string', $updatedAsset->asset_tag);
|
||||
$this->assertEquals($userAssigned->id, $updatedAsset->assigned_to);
|
||||
$this->assertTrue($updatedAsset->company->is($company));
|
||||
$this->assertTrue($updatedAsset->location->is($location));
|
||||
$this->assertTrue($updatedAsset->model->is($model));
|
||||
$this->assertEquals('A New Asset', $updatedAsset->name);
|
||||
$this->assertEquals('Some notes', $updatedAsset->notes);
|
||||
$this->assertEquals('5678', $updatedAsset->order_number);
|
||||
$this->assertEquals('123.45', $updatedAsset->purchase_cost);
|
||||
$this->assertTrue($updatedAsset->purchase_date->is('2023-09-02'));
|
||||
$this->assertEquals('1', $updatedAsset->requestable);
|
||||
$this->assertTrue($updatedAsset->defaultLoc->is($rtdLocation));
|
||||
$this->assertEquals('1234567890', $updatedAsset->serial);
|
||||
$this->assertTrue($updatedAsset->assetstatus->is($status));
|
||||
$this->assertTrue($updatedAsset->supplier->is($supplier));
|
||||
$this->assertEquals(10, $updatedAsset->warranty_months);
|
||||
//$this->assertEquals('2023-09-03 00:00:00', $updatedAsset->last_audit_date->format('Y-m-d H:i:s'));
|
||||
$this->assertEquals('2023-09-03 00:00:00', $updatedAsset->last_audit_date);
|
||||
}
|
||||
|
||||
public function testAssetEolDateIsCalculatedIfPurchaseDateUpdated()
|
||||
{
|
||||
$asset = Asset::factory()->laptopMbp()->noPurchaseOrEolDate()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson((route('api.assets.update', $asset->id)), [
|
||||
'purchase_date' => '2021-01-01',
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success')
|
||||
->json();
|
||||
|
||||
$asset->refresh();
|
||||
|
||||
$this->assertEquals('2024-01-01', $asset->asset_eol_date);
|
||||
}
|
||||
|
||||
public function testAssetEolDateIsNotCalculatedIfPurchaseDateNotSet()
|
||||
{
|
||||
$asset = Asset::factory()->laptopMbp()->noPurchaseOrEolDate()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'name' => 'test asset',
|
||||
'asset_eol_date' => '2022-01-01'
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success')
|
||||
->json();
|
||||
|
||||
$asset->refresh();
|
||||
|
||||
$this->assertEquals('2022-01-01', $asset->asset_eol_date);
|
||||
}
|
||||
|
||||
public function testAssetEolExplicitIsSetIfAssetEolDateIsExplicitlySet()
|
||||
{
|
||||
$asset = Asset::factory()->laptopMbp()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'asset_eol_date' => '2025-01-01',
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success')
|
||||
->json();
|
||||
|
||||
$asset->refresh();
|
||||
|
||||
$this->assertEquals('2025-01-01', $asset->asset_eol_date);
|
||||
$this->assertTrue($asset->eol_explicit);
|
||||
}
|
||||
|
||||
public function testAssetTagCannotUpdateToNullValue()
|
||||
{
|
||||
$asset = Asset::factory()->laptopMbp()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'asset_tag' => null,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error');
|
||||
}
|
||||
|
||||
public function testAssetTagCannotUpdateToEmptyStringValue()
|
||||
{
|
||||
$asset = Asset::factory()->laptopMbp()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'asset_tag' => "",
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error');
|
||||
}
|
||||
|
||||
public function testModelIdCannotUpdateToNullValue()
|
||||
{
|
||||
$asset = Asset::factory()->laptopMbp()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'model_id' => null
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error');
|
||||
}
|
||||
|
||||
public function testModelIdCannotUpdateToEmptyStringValue()
|
||||
{
|
||||
$asset = Asset::factory()->laptopMbp()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'model_id' => ""
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error');
|
||||
}
|
||||
|
||||
public function testStatusIdCannotUpdateToNullValue()
|
||||
{
|
||||
$asset = Asset::factory()->laptopMbp()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'status_id' => null
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error');
|
||||
}
|
||||
|
||||
public function testStatusIdCannotUpdateToEmptyStringValue()
|
||||
{
|
||||
$asset = Asset::factory()->laptopMbp()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'status_id' => ""
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error');
|
||||
}
|
||||
|
||||
public function testIfRtdLocationIdIsSetWithoutLocationIdAssetReturnsToDefault()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
$asset = Asset::factory()->laptopMbp()->create([
|
||||
'location_id' => $location->id
|
||||
]);
|
||||
$rtdLocation = Location::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'rtd_location_id' => $rtdLocation->id
|
||||
]);
|
||||
|
||||
$asset->refresh();
|
||||
|
||||
$this->assertTrue($asset->defaultLoc->is($rtdLocation));
|
||||
$this->assertTrue($asset->location->is($rtdLocation));
|
||||
}
|
||||
|
||||
public function testIfLocationAndRtdLocationAreSetLocationIdIsLocation()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
$asset = Asset::factory()->laptopMbp()->create();
|
||||
$rtdLocation = Location::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAssets()->create())
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'rtd_location_id' => $rtdLocation->id,
|
||||
'location_id' => $location->id
|
||||
]);
|
||||
|
||||
$asset->refresh();
|
||||
|
||||
$this->assertTrue($asset->defaultLoc->is($rtdLocation));
|
||||
$this->assertTrue($asset->location->is($location));
|
||||
}
|
||||
|
||||
public function testEncryptedCustomFieldCanBeUpdated()
|
||||
{
|
||||
$this->markIncompleteIfMySQL('Custom Fields tests do not work on MySQL');
|
||||
|
@ -52,4 +307,151 @@ class UpdateAssetTest extends TestCase
|
|||
$asset->refresh();
|
||||
$this->assertEquals("encrypted value should not change", Crypt::decrypt($asset->{$field->db_column_name()}));
|
||||
}
|
||||
|
||||
public function testCheckoutToUserOnAssetUpdate()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$user = User::factory()->editAssets()->create();
|
||||
$assigned_user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAsForApi($user)
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'assigned_user' => $assigned_user->id,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success')
|
||||
->json();
|
||||
|
||||
$asset->refresh();
|
||||
$this->assertEquals($assigned_user->id, $asset->assigned_to);
|
||||
$this->assertEquals($asset->assigned_type, 'App\Models\User');
|
||||
}
|
||||
|
||||
public function testCheckoutToDeletedUserFailsOnAssetUpdate()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$user = User::factory()->editAssets()->create();
|
||||
$assigned_user = User::factory()->deleted()->create();
|
||||
|
||||
$this->actingAsForApi($user)
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'assigned_user' => $assigned_user->id,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
|
||||
$asset->refresh();
|
||||
$this->assertNull($asset->assigned_to);
|
||||
$this->assertNull($asset->assigned_type);
|
||||
}
|
||||
|
||||
public function testCheckoutToLocationOnAssetUpdate()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$user = User::factory()->editAssets()->create();
|
||||
$assigned_location = Location::factory()->create();
|
||||
|
||||
$this->actingAsForApi($user)
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'assigned_location' => $assigned_location->id,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success')
|
||||
->json();
|
||||
|
||||
$asset->refresh();
|
||||
$this->assertEquals($assigned_location->id, $asset->assigned_to);
|
||||
$this->assertEquals($asset->assigned_type, 'App\Models\Location');
|
||||
|
||||
}
|
||||
|
||||
public function testCheckoutToDeletedLocationFailsOnAssetUpdate()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$user = User::factory()->editAssets()->create();
|
||||
$assigned_location = Location::factory()->deleted()->create();
|
||||
|
||||
$this->actingAsForApi($user)
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'assigned_location' => $assigned_location->id,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
|
||||
$asset->refresh();
|
||||
$this->assertNull($asset->assigned_to);
|
||||
$this->assertNull($asset->assigned_type);
|
||||
}
|
||||
|
||||
public function testCheckoutAssetOnAssetUpdate()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$user = User::factory()->editAssets()->create();
|
||||
$assigned_asset = Asset::factory()->create();
|
||||
|
||||
$this->actingAsForApi($user)
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'assigned_asset' => $assigned_asset->id,
|
||||
'checkout_to_type' => 'user',
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success')
|
||||
->json();
|
||||
|
||||
$asset->refresh();
|
||||
$this->assertEquals($assigned_asset->id, $asset->assigned_to);
|
||||
$this->assertEquals($asset->assigned_type, 'App\Models\Asset');
|
||||
|
||||
}
|
||||
|
||||
public function testCheckoutToDeletedAssetFailsOnAssetUpdate()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$user = User::factory()->editAssets()->create();
|
||||
$assigned_asset = Asset::factory()->deleted()->create();
|
||||
|
||||
$this->actingAsForApi($user)
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'assigned_asset' => $assigned_asset->id,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
|
||||
$asset->refresh();
|
||||
$this->assertNull($asset->assigned_to);
|
||||
$this->assertNull($asset->assigned_type);
|
||||
}
|
||||
|
||||
public function testAssetCannotBeUpdatedByUserInSeparateCompany()
|
||||
{
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$companyA = Company::factory()->create();
|
||||
$companyB = Company::factory()->create();
|
||||
$userA = User::factory()->editAssets()->create([
|
||||
'company_id' => $companyA->id,
|
||||
]);
|
||||
$userB = User::factory()->editAssets()->create([
|
||||
'company_id' => $companyB->id,
|
||||
]);
|
||||
$asset = Asset::factory()->create([
|
||||
'user_id' => $userA->id,
|
||||
'company_id' => $companyA->id,
|
||||
]);
|
||||
|
||||
$this->actingAsForApi($userB)
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'name' => 'test name'
|
||||
])
|
||||
->assertStatusMessageIs('error');
|
||||
|
||||
$this->actingAsForApi($userA)
|
||||
->patchJson(route('api.assets.update', $asset->id), [
|
||||
'name' => 'test name'
|
||||
])
|
||||
->assertStatusMessageIs('success');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\Feature\Settings;
|
||||
|
||||
use App\Http\Controllers\SettingsController;
|
||||
use Illuminate\Database\Events\QueryExecuted;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Http\Client\Request;
|
||||
|
@ -301,4 +302,11 @@ class ShowSetUpPageTest extends TestCase
|
|||
|
||||
$this->assertSeeDirectoryPermissionError(false);
|
||||
}
|
||||
|
||||
public function testInvalidTLSCertsOkWhenCheckingForEnvFile()
|
||||
{
|
||||
//set the weird bad SSL cert place - https://self-signed.badssl.com
|
||||
$this->markTestIncomplete("Not yet sure how to write this test, it requires messing with .env ...");
|
||||
$this->assertTrue((new SettingsController())->dotEnvFileIsExposed());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue