Add feature tests for CSKH Excel import and database backup CLI commands

This commit is contained in:
2026-05-20 10:34:18 +00:00
parent efa97b6c71
commit 44a7f53deb
3 changed files with 1796 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
<?php
use App\Models\User;
use App\Models\Product;
use Illuminate\Support\Facades\File;
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
test('export backup command exports database tables to json and writes manifest', function () {
// Create some data
User::factory()->count(3)->create();
Product::create(['name' => 'Sunrise Suite A1', 'status' => 'active']);
Product::create(['name' => 'Sunset Suite A2', 'status' => 'active']);
$backupPath = base_path('tests/Feature/backups_test');
// Clean backup directory if it exists
if (File::isDirectory($backupPath)) {
File::deleteDirectory($backupPath);
}
// Run backup command
$this->artisan('app:export-backup', [
'--path' => $backupPath,
'--tables' => 'users,products',
'--pretty' => true
])
->assertExitCode(0);
// Verify backup folder created
$directories = File::directories($backupPath);
expect($directories)->toHaveCount(1);
$backupDir = $directories[0];
// Verify files created
expect(File::exists("{$backupDir}/manifest.json"))->toBeTrue();
expect(File::exists("{$backupDir}/users.json"))->toBeTrue();
expect(File::exists("{$backupDir}/products.json"))->toBeTrue();
// Verify manifest contents
$manifest = json_decode(File::get("{$backupDir}/manifest.json"), true);
expect($manifest['tables']['users'])->toBe(3);
expect($manifest['tables']['products'])->toBe(2);
expect($manifest['total_rows'])->toBe(5);
// Verify products contents
$productsJson = json_decode(File::get("{$backupDir}/products.json"), true);
expect($productsJson)->toHaveCount(2);
expect($productsJson[0]['name'])->toBe('Sunrise Suite A1');
expect($productsJson[1]['name'])->toBe('Sunset Suite A2');
// Clean up backup directory
File::deleteDirectory($backupPath);
});

View File

@@ -0,0 +1,127 @@
<?php
use App\Models\Contract;
use App\Models\Customer;
use App\Models\Feedback;
use App\Models\FeedbackChannel;
use App\Models\FeedbackInteraction;
use App\Models\Handover;
use App\Models\Product;
use App\Models\ProductService;
use App\Models\User;
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
// Seed a default user since the importer associates interactions with the first user
User::factory()->create([
'name' => 'Default Agent',
'email' => 'agent@test.com',
]);
// Create a default FeedbackChannel (ID 1) which is required by the importer
FeedbackChannel::create([
'name' => 'Email',
'slug' => 'email',
'icon' => 'heroicon-o-envelope',
'color' => '#3b82f6'
]);
});
test('cskh import command processes rows correctly in dry run and active modes', function () {
// Create temporary CSV file inside the project directory so WSL has easy access
$csvPath = base_path('tests/Feature/cskh_test.csv');
// Headers: Mã căn hộ, Họ tên KH, BÀN GIÀO CĂN HỘ, TÌNH TRẠNG, HỢP ĐỒNG THUÊ, HĐ HỢP TÁC KINH DOANH, TỰ DOANH, Phí QL, Lịch Sử khách hàng
$headers = [
'Mã căn hộ',
'Họ tên KH',
'BÀN GIÀO CĂN HỘ',
'TÌNH TRẠNG',
'HỢP ĐỒNG THUÊ',
'HĐ HỢP TÁC KINH DOANH',
'TỰ DOANH',
'Phí QL',
'Lịch Sử khách hàng'
];
$row1 = [
'A1-101',
'Nguyen Van A',
'{"date":"2026-01-01 00:00:00.000000","timezone_type":3,"timezone":"UTC"}',
'Đã bàn giao căn hộ cho KH',
'Số HĐ: HĐ-001, Giá: 10M, Thời hạn: 1 năm',
'',
'',
'Đã thu phí QL tháng 1',
"KH báo hỏng bóng đèn\nKỹ thuật đã sửa xong"
];
$row2 = [
'A1-102',
'Tran Thi B',
'',
'',
'',
'HĐ hợp tác BCC-123',
'Doanh nghiệp B',
'',
'Khách hàng thắc mắc hóa đơn tiền nước'
];
$fp = fopen($csvPath, 'w');
// Write UTF-8 BOM for Excel support
fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
fputcsv($fp, $headers);
fputcsv($fp, $row1);
fputcsv($fp, $row2);
fclose($fp);
// 1. Dry run execution
$this->artisan('app:import-cskh', [
'file_path' => $csvPath,
'--dry-run' => true
])
->expectsOutputToContain('Total rows in file: 2')
->expectsOutputToContain('New Products')
->assertExitCode(0);
// Verify no records created in dry-run
expect(Product::count())->toBe(0);
expect(Customer::count())->toBe(0);
expect(Contract::count())->toBe(0);
expect(Handover::count())->toBe(0);
expect(ProductService::count())->toBe(0);
expect(Feedback::count())->toBe(0);
// 2. Active run execution
$this->artisan('app:import-cskh', [
'file_path' => $csvPath
])
->expectsOutputToContain('Total rows in file: 2')
->assertExitCode(0);
// Verify records created
expect(Product::count())->toBe(2);
expect(Customer::count())->toBe(2);
expect(Contract::count())->toBe(2); // 1 Lease (row1) and 1 BCC (row2)
expect(Handover::count())->toBe(1); // row1 has handover, row2 doesn't
expect(ProductService::count())->toBe(1); // row2 has Self-Business
expect(Feedback::count())->toBe(2); // both have feedback logs
expect(FeedbackInteraction::count())->toBe(3); // row1 has 2 lines, row2 has 1 line
// Verify relationships and fields
$productA = Product::where('name', 'A1-101')->first();
expect($productA)->not->toBeNull();
$handover = Handover::where('product_id', $productA->id)->first();
expect($handover)->not->toBeNull();
expect($handover->handover_date)->toBe('2026-01-01');
$contract = Contract::where('product_id', $productA->id)->first();
expect($contract)->not->toBeNull();
expect($contract->type)->toBe('lease'); // type is a string 'lease'
// Clean up temporary file
@unlink($csvPath);
});