File: /home/xedaptot/be.naniguide.com/tests/Feature/FileManagerApiTest.php
<?php
/**
* FileManagerApi feature test — exercises /api/v1/filemanager/* endpoints
* consumed by the scalable-filemanager frontend library.
*
* DB strategy: DatabaseTransactions so rows auto-rollback. Customers and
* Users are inserted via forceCreate() because CustomerFactory nests
* User::factory() which doesn't resolve for App\Model\User.
*/
use App\Model\Asset;
use App\Model\Customer;
use App\Model\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
uses(Tests\TestCase::class);
uses(DatabaseTransactions::class);
function seedApiCustomerAndUser(): array
{
$customer = Customer::forceCreate([
'uid' => 'fmcus_' . Str::random(10),
'timezone' => 'UTC',
'status' => 'active',
'created_at' => now(),
'updated_at' => now(),
]);
$user = User::forceCreate([
'uid' => 'fmusr_' . Str::random(10),
'email' => 'fm_' . Str::random(6) . '@example.test',
'password' => bcrypt('x'),
'activated' => 1,
'api_token' => Str::random(60),
'customer_id' => $customer->id,
]);
return [$customer, $user];
}
function seedApiAsset(Customer $customer, array $overrides = []): Asset
{
return Asset::forceCreate(array_merge([
'uid' => 'fmast_' . Str::random(10),
'customer_id' => $customer->id,
'type' => Asset::TYPE_MEDIA,
'visibility' => Asset::VISIBILITY_PUBLIC,
'path' => "customers/{$customer->uid}/home/media/",
'original_name' => 'photo.png',
'filename' => 'photo.png',
'size' => 1024,
'mime_type' => 'image/png',
], $overrides));
}
it('requires authentication for list endpoint', function () {
$this->getJson('/api/v1/filemanager/list')->assertStatus(401);
});
it('returns the authenticated customers assets only', function () {
[$customerA, $userA] = seedApiCustomerAndUser();
[$customerB] = seedApiCustomerAndUser();
seedApiAsset($customerA, ['original_name' => 'a1.png']);
seedApiAsset($customerA, ['original_name' => 'a2.pdf', 'mime_type' => 'application/pdf']);
seedApiAsset($customerB, ['original_name' => 'b1.png']);
// seedApiAsset defaults assets into the `media/` subfolder, so list there
// (no `path` param would query root and surface only the `media/` folder entry).
$response = $this->actingAs($userA, 'api')
->getJson('/api/v1/filemanager/list?scope_type=customer&scope_id=' . $customerA->id . '&path=media');
$response->assertOk()
->assertJsonPath('success', true)
->assertJsonPath('data.total', 2)
->assertJsonCount(2, 'data.items');
$names = collect($response->json('data.items'))->pluck('name')->sort()->values()->all();
expect($names)->toBe(['a1.png', 'a2.pdf']);
});
it('rejects mismatched scope_id', function () {
[$customer, $user] = seedApiCustomerAndUser();
$this->actingAs($user, 'api')
->getJson('/api/v1/filemanager/list?scope_type=customer&scope_id=' . ($customer->id + 9999))
->assertStatus(403);
});
it('uploads a file and returns the entry', function () {
Storage::fake('local');
[$customer, $user] = seedApiCustomerAndUser();
$response = $this->actingAs($user, 'api')
->post('/api/v1/filemanager/upload', [
'scope_type' => 'customer',
'scope_id' => $customer->id,
'file' => UploadedFile::fake()->image('hero.png', 200, 200),
]);
$response->assertOk()
->assertJsonPath('success', true)
->assertJsonPath('data.name', 'hero.png')
->assertJsonPath('data.kind', 'image');
expect(Asset::where('customer_id', $customer->id)->count())->toBe(1);
});
it('deletes an asset by uid', function () {
Storage::fake('local');
[$customer, $user] = seedApiCustomerAndUser();
$asset = seedApiAsset($customer);
$this->actingAs($user, 'api')
->deleteJson('/api/v1/filemanager/delete?scope_type=customer&scope_id=' . $customer->id . '&id=' . $asset->uid)
->assertOk()
->assertJsonPath('success', true);
expect(Asset::where('uid', $asset->uid)->exists())->toBeFalse();
});
it('renames an asset', function () {
[$customer, $user] = seedApiCustomerAndUser();
$asset = seedApiAsset($customer);
$response = $this->actingAs($user, 'api')
->patchJson('/api/v1/filemanager/rename', [
'scope_type' => 'customer',
'scope_id' => $customer->id,
'id' => $asset->uid,
'name' => 'renamed.png',
]);
$response->assertOk()->assertJsonPath('data.name', 'renamed.png');
expect($asset->fresh()->original_name)->toBe('renamed.png');
});
it('view endpoint redirects to the asset url', function () {
[$customer, $user] = seedApiCustomerAndUser();
$asset = seedApiAsset($customer);
$this->actingAs($user, 'api')
->get('/api/v1/filemanager/view?scope_type=customer&scope_id=' . $customer->id . '&id=' . $asset->uid)
->assertRedirect($asset->getUrl());
});
it('rejects unsupported scope_type', function () {
[$customer, $user] = seedApiCustomerAndUser();
$this->actingAs($user, 'api')
->getJson('/api/v1/filemanager/list?scope_type=system')
->assertStatus(400);
});