Skip to content

Commit fe36ef0

Browse files
committed
Update test
1 parent 8874a01 commit fe36ef0

15 files changed

+1619
-12
lines changed

src/Builder/CsvBuilder.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace NaimSolong\DataExtractor\Builder;
44

5+
use DateTime;
6+
57
class CsvBuilder extends BaseBuilder
68
{
79
/**
@@ -25,8 +27,19 @@ public function build(): string
2527
}
2628

2729
$value = $this->data[$column] ?? '';
28-
// Escape CSV values
29-
$csvRow[] = '"'.str_replace('"', '""', $value).'"';
30+
if (is_array($value)) {
31+
$csvRow[] = "'".json_encode($value, JSON_UNESCAPED_UNICODE)."'";
32+
} elseif (is_null($value)) {
33+
$csvRow[] = 'NULL';
34+
} elseif (is_numeric($value)) {
35+
$csvRow[] = $value;
36+
} elseif (is_bool($value)) {
37+
$csvRow[] = ($value ? "'1'" : "'0'");
38+
} elseif ($value instanceof DateTime) {
39+
$csvRow[] = "'".$value->format('Y-m-d H:i:s')."'";
40+
} else {
41+
$csvRow[] = '"'.str_replace('"', '""', $value).'"';
42+
}
3043
}
3144
$csv .= implode(',', $csvRow)."\n";
3245

src/Builder/ExtractBuilder.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ public function createBuilder(string $type): self
4242
return $this;
4343
}
4444

45+
public function getBuilder(): CsvBuilder|SqlBuilder
46+
{
47+
return $this->builder;
48+
}
49+
4550
public function asCsv(): self
4651
{
4752
return $this->createBuilder(self::FORMAT_CSV);
@@ -74,12 +79,12 @@ public function setModel(mixed $model): self
7479
return $this;
7580
}
7681

77-
protected function getTableName(): string
82+
public function getTableName(): string
7883
{
7984
return $this->model->getTable();
8085
}
8186

82-
protected function getTableColumns(): array
87+
public function getTableColumns(): array
8388
{
8489
return app(DatabaseManager::class)
8590
->connection($this->model->getConnectionName())

src/Dto/Source.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public function __construct(
1515

1616
public static function fromArray(array $data): self
1717
{
18-
if (! is_subclass_of($data['model'], Model::class)) {
18+
if (! is_subclass_of($data['model'] ?? null, Model::class)) {
1919
throw new Exception('The provided model, parent must be an instance of Illuminate\Database\Eloquent\Model');
2020
}
2121

src/OptionsResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ public function set(int|string $value): self
4545
}
4646

4747
if (is_string($value)) {
48-
$filteredOptions = array_filter($this->options, function ($option) use ($value) {
48+
$filteredOptions = array_values(array_filter($this->options, function ($option) use ($value) {
4949
return $option->name === $value;
50-
});
50+
}));
5151

5252
if (count($filteredOptions) > 0) {
5353
$this->option = $filteredOptions[0];

src/SourcesResolver.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ public function set(string $value): self
2929

3030
public function get(): Source
3131
{
32+
if (empty($this->source)) {
33+
throw new InvalidArgumentException('Source has not been set. Please call set() method with a valid source key.');
34+
}
35+
3236
return $this->source;
3337
}
3438
}

tests/Builder/CsvBuilderTest.php

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
use NaimSolong\DataExtractor\Builder\CsvBuilder;
4+
5+
it('can build CSV output with headers', function () {
6+
$builder = new CsvBuilder();
7+
$builder->setSchemaName('test_table');
8+
$builder->setColumns(['id', 'name', 'email']);
9+
$builder->setData([
10+
'id' => 1,
11+
'name' => 'John Doe',
12+
'email' => 'john@example.com',
13+
]);
14+
15+
$result = $builder->build();
16+
17+
expect($result)->toBeString();
18+
expect($result)->toBe("id,name,email\n1,\"John Doe\",\"john@example.com\"\n");
19+
});
20+
21+
it('can handle single row data', function () {
22+
$builder = new CsvBuilder();
23+
$builder->setSchemaName('users');
24+
$builder->setColumns(['id', 'name']);
25+
$builder->setData([
26+
'id' => 1,
27+
'name' => 'User 1',
28+
]);
29+
30+
$result = $builder->build();
31+
32+
expect($result)->toBeString();
33+
expect($result)->toBe("id,name\n1,\"User 1\"\n");
34+
});
35+
36+
it('can handle values with commas', function () {
37+
$builder = new CsvBuilder();
38+
$builder->setSchemaName('test_table');
39+
$builder->setColumns(['id', 'description']);
40+
$builder->setData([
41+
'id' => 1,
42+
'description' => 'This is a description, with commas',
43+
]);
44+
45+
$result = $builder->build();
46+
47+
expect($result)->toContain('"This is a description, with commas"');
48+
});
49+
50+
it('can handle values with quotes', function () {
51+
$builder = new CsvBuilder();
52+
$builder->setSchemaName('test_table');
53+
$builder->setColumns(['id', 'quote_test']);
54+
$builder->setData([
55+
'id' => 1,
56+
'quote_test' => 'He said "Hello World"',
57+
]);
58+
59+
$result = $builder->build();
60+
61+
expect($result)->toContain('"He said ""Hello World"""');
62+
});
63+
64+
it('can handle NULL values', function () {
65+
$builder = new CsvBuilder();
66+
$builder->setSchemaName('test_table');
67+
$builder->setColumns(['id', 'name', 'bio']);
68+
$builder->setData([
69+
'id' => 1,
70+
'name' => 'John Doe',
71+
'bio' => null,
72+
]);
73+
74+
$result = $builder->build();
75+
76+
expect($result)->toContain('"John Doe",""');
77+
});
78+
79+
it('can handle boolean values', function () {
80+
$builder = new CsvBuilder();
81+
$builder->setSchemaName('test_table');
82+
$builder->setColumns(['id', 'is_active', 'is_admin']);
83+
$builder->setData([
84+
'id' => 1,
85+
'is_active' => true,
86+
'is_admin' => false,
87+
]);
88+
89+
$result = $builder->build();
90+
91+
expect($result)->toContain('1,\'1\',\'0\'');
92+
});
93+
94+
it('can handle array values as JSON strings', function () {
95+
$builder = new CsvBuilder();
96+
$builder->setSchemaName('test_table');
97+
$builder->setColumns(['id', 'metadata']);
98+
$builder->setData([
99+
'id' => 1,
100+
'metadata' => ['key' => 'value', 'count' => 5],
101+
]);
102+
103+
$result = $builder->build();
104+
105+
expect($result)->toContain('{"key":"value","count":5}');
106+
});
107+
108+
it('can handle missing columns', function () {
109+
$builder = new CsvBuilder();
110+
$builder->setSchemaName('test_table');
111+
$builder->setColumns(['id', 'name', 'email']);
112+
$builder->setData([
113+
'id' => 1,
114+
'name' => 'John Doe',
115+
// email is missing
116+
]);
117+
118+
$result = $builder->build();
119+
120+
expect($result)->toContain("'*****'");
121+
});
122+
123+
it('can handle empty data array', function () {
124+
$builder = new CsvBuilder();
125+
$builder->setSchemaName('test_table');
126+
$builder->setColumns(['id', 'name', 'email']);
127+
$builder->setData([]);
128+
129+
$result = $builder->build();
130+
131+
expect($result)->toBe("id,name,email\n'*****','*****','*****'\n");
132+
});
133+
134+
it('can handle DateTime objects', function () {
135+
$builder = new CsvBuilder();
136+
$builder->setSchemaName('test_table');
137+
$builder->setColumns(['id', 'created_at']);
138+
$builder->setData([
139+
'id' => 1,
140+
'created_at' => new DateTime('2023-01-01 12:00:00'),
141+
]);
142+
143+
$result = $builder->build();
144+
145+
expect($result)->toContain('2023-01-01 12:00:00');
146+
});
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
use Illuminate\Database\Eloquent\Model;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
use NaimSolong\DataExtractor\Builder\CsvBuilder;
7+
use NaimSolong\DataExtractor\Builder\ExtractBuilder;
8+
use NaimSolong\DataExtractor\Builder\SqlBuilder;
9+
10+
beforeEach(function () {
11+
Schema::create('extract_test_users', function (Blueprint $table) {
12+
$table->id();
13+
$table->string('name');
14+
$table->string('email');
15+
$table->boolean('is_active')->default(true);
16+
$table->timestamps();
17+
});
18+
});
19+
20+
afterEach(function () {
21+
Schema::dropIfExists('extract_test_users');
22+
});
23+
24+
class ExtractTestUser extends Model
25+
{
26+
protected $table = 'extract_test_users';
27+
protected $guarded = [];
28+
}
29+
30+
it('can create SQL builder', function () {
31+
$builder = (new ExtractBuilder)->createBuilder(ExtractBuilder::FORMAT_SQL);
32+
33+
expect($builder->getBuilder())->toBeInstanceOf(SqlBuilder::class);
34+
});
35+
36+
it('can create CSV builder', function () {
37+
$builder = (new ExtractBuilder)->createBuilder(ExtractBuilder::FORMAT_CSV);
38+
39+
expect($builder->getBuilder())->toBeInstanceOf(CsvBuilder::class);
40+
});
41+
42+
it('throws exception for unsupported format', function () {
43+
expect(fn() => (new ExtractBuilder)->createBuilder('invalid_format'))
44+
->toThrow('Invalid builder type: invalid_format');
45+
});
46+
47+
it('can get table schema information', function () {
48+
$user = ExtractTestUser::create([
49+
'name' => 'Schema Test',
50+
'email' => 'schema@example.com',
51+
]);
52+
53+
$builder = (new ExtractBuilder)->createBuilder(ExtractBuilder::FORMAT_SQL);
54+
$columns = $builder->setModel($user)->getTableColumns();
55+
56+
expect($columns)->toBeArray();
57+
expect($columns)->toContain('id');
58+
expect($columns)->toContain('name');
59+
expect($columns)->toContain('email');
60+
expect($columns)->toContain('is_active');
61+
expect($columns)->toContain('created_at');
62+
expect($columns)->toContain('updated_at');
63+
});
64+
65+
it('can build data with models', function () {
66+
$user = ExtractTestUser::create(['name' => 'User 1', 'email' => 'user1@example.com']);
67+
68+
$builder = (new ExtractBuilder)->createBuilder(ExtractBuilder::FORMAT_SQL);
69+
$results = $builder->setModel($user)->build();
70+
71+
expect($results)->toBeString();
72+
expect($results)->toContain('INSERT INTO extract_test_users');
73+
expect($results)->toContain('User 1');
74+
});
75+
76+
it('can build CSV data with models', function () {
77+
$user = ExtractTestUser::create(['name' => 'CSV User', 'email' => 'csv@example.com']);
78+
79+
$builder = (new ExtractBuilder)->createBuilder(ExtractBuilder::FORMAT_CSV);
80+
$results = $builder->setModel($user)->build();
81+
82+
expect($results)->toBeString();
83+
expect($results)->toContain('id,name,email');
84+
expect($results)->toContain('CSV User');
85+
});
86+
87+
it('throws exception when building with invalid models', function () {
88+
$builder = (new ExtractBuilder)->createBuilder(ExtractBuilder::FORMAT_SQL);
89+
90+
expect(fn() => $builder->build(['not_a_model']))
91+
->toThrow('Model not set. Use setModel() to set the model before building.');
92+
});
93+
94+
it('groups models by table correctly', function () {
95+
// Create different model types
96+
$user = ExtractTestUser::create(['name' => 'User 2', 'email' => 'user2@example.com']);
97+
98+
$builder = (new ExtractBuilder)->createBuilder(ExtractBuilder::FORMAT_SQL);
99+
$results = $builder->setModel($user)->build();
100+
101+
// Both should be INSERT statements for the same table
102+
expect($results)->toContain('INSERT INTO extract_test_users');
103+
});

0 commit comments

Comments
 (0)