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->format('Y-m-d'))->toBe('2026-01-01'); $contract = Contract::where('product_id', $productA->id)->first(); expect($contract)->not->toBeNull(); expect($contract->type->value)->toBe('lease'); // type is ContractType enum // Clean up temporary file @unlink($csvPath); });