194 lines
6.9 KiB
PHP
194 lines
6.9 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Importing;
|
|
|
|
use App\Models\Asset;
|
|
use App\Models\AssetModel;
|
|
use App\Models\Category;
|
|
use App\Models\Company;
|
|
use App\Models\Location;
|
|
use App\Models\Statuslabel;
|
|
use App\Models\User;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\TestCase;
|
|
|
|
class AssetImportCreatedAtTest extends TestCase
|
|
{
|
|
/**
|
|
* Test that importing assets doesn't modify created_at timestamps on existing assets
|
|
* This test addresses the reported bug where large imports caused random created_at changes
|
|
*/
|
|
#[Test]
|
|
public function existing_asset_created_at_not_modified_on_import_update()
|
|
{
|
|
// Create test data
|
|
$category = Category::factory()->create(['category_type' => 'asset']);
|
|
$location = Location::factory()->create();
|
|
$statusLabel = Statuslabel::factory()->create();
|
|
$company = Company::factory()->create();
|
|
$assetModel = AssetModel::factory()->for($category, 'category')->create();
|
|
|
|
// Create an existing asset with a known created_at date
|
|
$originalCreatedAt = now()->subDays(30)->toDateTimeString();
|
|
$asset = Asset::factory()
|
|
->for($assetModel, 'model')
|
|
->for($statusLabel, 'status')
|
|
->for($location, 'defaultLoc')
|
|
->for($company, 'company')
|
|
->create([
|
|
'asset_tag' => 'TEST-001',
|
|
'name' => 'Test Asset',
|
|
'created_at' => $originalCreatedAt,
|
|
]);
|
|
|
|
// Create CSV content that updates the existing asset
|
|
$csv = "asset tag,item name,category,status\n";
|
|
$csv .= "TEST-001,Test Asset Updated,{$category->name},{$statusLabel->name}\n";
|
|
|
|
// Perform import with update flag
|
|
$this->actingAsForApi(User::factory()->canImport()->create())
|
|
->postJson(route('api.imports.store'), [
|
|
'files' => [
|
|
$this->createFakeUploadedFile('test.csv', $csv),
|
|
],
|
|
])
|
|
->assertSuccessful();
|
|
|
|
// Import the file
|
|
$import = \App\Models\Import::latest()->first();
|
|
$this->actingAsForApi(User::factory()->canImport()->create())
|
|
->postJson(route('api.imports.importFile', $import->id), [
|
|
'import-type' => 'asset',
|
|
'import-update' => true,
|
|
'column-mappings' => [
|
|
'asset tag' => 'asset_tag',
|
|
'item name' => 'item_name',
|
|
'category' => 'category',
|
|
'status' => 'status',
|
|
],
|
|
])
|
|
->assertSuccessful();
|
|
|
|
// Verify the asset's created_at timestamp wasn't modified
|
|
$asset->refresh();
|
|
$this->assertEquals(
|
|
$originalCreatedAt,
|
|
$asset->created_at->toDateTimeString(),
|
|
'Asset created_at timestamp was modified during import update, which should not happen'
|
|
);
|
|
|
|
// Verify the asset was updated correctly
|
|
$this->assertEquals('Test Asset Updated', $asset->name);
|
|
}
|
|
|
|
/**
|
|
* Test that multiple successive imports don't cause timestamp drift
|
|
*/
|
|
#[Test]
|
|
public function successive_imports_maintain_created_at_consistency()
|
|
{
|
|
// Create test data
|
|
$category = Category::factory()->create(['category_type' => 'asset']);
|
|
$location = Location::factory()->create();
|
|
$statusLabel = Statuslabel::factory()->create();
|
|
$company = Company::factory()->create();
|
|
$assetModel = AssetModel::factory()->for($category, 'category')->create();
|
|
|
|
// Create multiple existing assets
|
|
$assets = collect();
|
|
for ($i = 1; $i <= 5; $i++) {
|
|
$assets->push(Asset::factory()
|
|
->for($assetModel, 'model')
|
|
->for($statusLabel, 'status')
|
|
->for($location, 'defaultLoc')
|
|
->for($company, 'company')
|
|
->create([
|
|
'asset_tag' => "TEST-{$i}",
|
|
'created_at' => now()->subDays(30 - $i),
|
|
]));
|
|
}
|
|
|
|
$originalCreatedAts = $assets->mapWithKeys(fn ($asset) => [$asset->id => $asset->created_at->toDateTimeString()])->toArray();
|
|
|
|
// Perform first import update
|
|
$csv = "asset tag,item name,category,status\n";
|
|
foreach ($assets as $asset) {
|
|
$csv .= "{$asset->asset_tag},{$asset->name},{$category->name},{$statusLabel->name}\n";
|
|
}
|
|
|
|
$this->actingAsForApi(User::factory()->canImport()->create())
|
|
->postJson(route('api.imports.store'), [
|
|
'files' => [
|
|
$this->createFakeUploadedFile('test1.csv', $csv),
|
|
],
|
|
])
|
|
->assertSuccessful();
|
|
|
|
$import1 = \App\Models\Import::latest()->first();
|
|
$this->actingAsForApi(User::factory()->canImport()->create())
|
|
->postJson(route('api.imports.importFile', $import1->id), [
|
|
'import-type' => 'asset',
|
|
'import-update' => true,
|
|
'column-mappings' => [
|
|
'asset tag' => 'asset_tag',
|
|
'item name' => 'item_name',
|
|
'category' => 'category',
|
|
'status' => 'status',
|
|
],
|
|
])
|
|
->assertSuccessful();
|
|
|
|
// Perform second import update
|
|
$this->actingAsForApi(User::factory()->canImport()->create())
|
|
->postJson(route('api.imports.store'), [
|
|
'files' => [
|
|
$this->createFakeUploadedFile('test2.csv', $csv),
|
|
],
|
|
])
|
|
->assertSuccessful();
|
|
|
|
$import2 = \App\Models\Import::latest()->first();
|
|
$this->actingAsForApi(User::factory()->canImport()->create())
|
|
->postJson(route('api.imports.importFile', $import2->id), [
|
|
'import-type' => 'asset',
|
|
'import-update' => true,
|
|
'column-mappings' => [
|
|
'asset tag' => 'asset_tag',
|
|
'item name' => 'item_name',
|
|
'category' => 'category',
|
|
'status' => 'status',
|
|
],
|
|
])
|
|
->assertSuccessful();
|
|
|
|
// Verify all assets' created_at timestamps remain unchanged
|
|
foreach ($assets as $asset) {
|
|
$asset->refresh();
|
|
$this->assertEquals(
|
|
$originalCreatedAts[$asset->id],
|
|
$asset->created_at->toDateTimeString(),
|
|
"Asset {$asset->asset_tag} created_at changed between imports"
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper method to create a fake uploaded file
|
|
*/
|
|
protected function createFakeUploadedFile(string $filename, string $content)
|
|
{
|
|
$path = tempnam(sys_get_temp_dir(), 'csv');
|
|
file_put_contents($path, $content);
|
|
|
|
return new \Illuminate\Http\UploadedFile(
|
|
$path,
|
|
$filename,
|
|
'text/csv',
|
|
null,
|
|
true
|
|
);
|
|
}
|
|
|
|
}
|
|
|