Skip to content

Commit 16137d5

Browse files
authored
Added feature tests (#3)
* Added feature tests * Added filter to test configuration * Explicitly added configuration file
1 parent 8e0f9b5 commit 16137d5

File tree

11 files changed

+318
-8
lines changed

11 files changed

+318
-8
lines changed

.github/workflows/tests.yml

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ concurrency:
1111
cancel-in-progress: true
1212

1313
jobs:
14-
tests:
14+
unit_tests:
1515
name: Unit tests
1616
runs-on: ubuntu-latest
1717
strategy:
@@ -32,7 +32,7 @@ jobs:
3232
dependency-versions: highest
3333

3434
- name: Run unit tests
35-
run: vendor/bin/phpunit --testsuite Unit --coverage-clover build/logs/clover.xml --coverage-filter ./src
35+
run: vendor/bin/phpunit -c phpunit.xml.dist --testsuite Unit --coverage-clover build/logs/clover.xml --coverage-filter ./src
3636

3737
- name: Upload codecov coverage
3838
uses: codecov/codecov-action@v3
@@ -42,3 +42,39 @@ jobs:
4242
verbose: true
4343
env:
4444
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
45+
46+
feature_tests:
47+
name: Feature tests
48+
runs-on: ubuntu-latest
49+
50+
services:
51+
redis:
52+
image: redis/redis-stack-server:${{ matrix.redis_stack }}
53+
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
54+
ports:
55+
- 6379:6379
56+
57+
strategy:
58+
fail-fast: false
59+
matrix:
60+
redis_stack:
61+
- latest
62+
- edge
63+
64+
steps:
65+
- name: Checkout repository
66+
uses: actions/checkout@v3
67+
68+
- name: Setup PHP with Composer and extensions
69+
uses: shivammathur/setup-php@v2
70+
with:
71+
php-version: '8.1'
72+
coverage: xdebug
73+
74+
- name: Install Composer dependencies
75+
uses: ramsey/composer-install@v2
76+
with:
77+
dependency-versions: highest
78+
79+
- name: Run feature tests
80+
run: vendor/bin/phpunit -c phpunit.xml.dist --testsuite Feature

composer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
"Vladvildanov\\PredisVl\\": "src/"
1818
}
1919
},
20+
"autoload-dev": {
21+
"psr-4": {
22+
"Vladvildanov\\PredisVl\\": "tests/"
23+
}
24+
},
2025
"authors": [
2126
{
2227
"name": "vladvildanov",

phpunit.xml.dist

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,27 @@
1010
<testsuite name="Unit">
1111
<directory>tests/Unit/</directory>
1212
</testsuite>
13+
<testsuite name="Feature">
14+
<directory>tests/Feature/</directory>
15+
</testsuite>
1316
</testsuites>
1417

18+
<source>
19+
<include>
20+
<directory>./src</directory>
21+
</include>
22+
</source>
23+
1524
<coverage>
1625
<report>
1726
<clover outputFile="build/logs/clover.xml"/>
1827
</report>
1928
</coverage>
29+
30+
<php>
31+
<!-- Redis -->
32+
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
33+
<const name="REDIS_SERVER_PORT" value="6379" />
34+
<const name="REDIS_SERVER_DBNUM" value="0" />
35+
</php>
2036
</phpunit>

tests/Feature/FeatureTestCase.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Vladvildanov\PredisVl\Feature;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Predis\Client;
7+
8+
abstract class FeatureTestCase extends TestCase
9+
{
10+
/**
11+
* Returns a named array with default values for connection parameters.
12+
*
13+
* @return array Default connection parameters
14+
*/
15+
protected function getDefaultParametersArray(): array
16+
{
17+
return [
18+
'scheme' => 'tcp',
19+
'host' => constant('REDIS_SERVER_HOST'),
20+
'port' => constant('REDIS_SERVER_PORT'),
21+
'database' => constant('REDIS_SERVER_DBNUM'),
22+
];
23+
}
24+
25+
/**
26+
* Creates Redis client with default or given configuration.
27+
*
28+
* @param array|null $parameters
29+
* @param array|null $options
30+
* @param bool|null $flushDB
31+
* @return Client
32+
*/
33+
protected function getClient(?array $parameters = null, ?array $options = null, ?bool $flushDB = true): Client
34+
{
35+
$parameters = array_merge(
36+
$this->getDefaultParametersArray(),
37+
$parameters ?: []
38+
);
39+
40+
$client = new Client($parameters, $options);
41+
42+
if ($flushDB) {
43+
$client->flushdb();
44+
}
45+
46+
return $client;
47+
}
48+
}
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
<?php
2+
3+
namespace Vladvildanov\PredisVl\Feature\Index;
4+
5+
use Vladvildanov\PredisVl\Feature\FeatureTestCase;
6+
use Predis\Client;
7+
use Vladvildanov\PredisVl\Index\SearchIndex;
8+
use Vladvildanov\PredisVl\VectorHelper;
9+
10+
class SearchIndexTest extends FeatureTestCase
11+
{
12+
/**
13+
* @var Client
14+
*/
15+
private Client $client;
16+
17+
/**
18+
* @var array
19+
*/
20+
private array $hashSchema;
21+
22+
/**
23+
* @var array
24+
*/
25+
private array $jsonSchema;
26+
27+
protected function setUp(): void
28+
{
29+
$this->client = $this->getClient();
30+
$this->hashSchema = [
31+
'index' => [
32+
'name' => 'foobar',
33+
'prefix' => 'foo:',
34+
],
35+
'fields' => [
36+
'id' => [
37+
'type' => 'text',
38+
'alias' => 'foo',
39+
],
40+
'count' => [
41+
'type' => 'numeric',
42+
],
43+
'id_embeddings' => [
44+
'type' => 'vector',
45+
'algorithm' => 'flat',
46+
'dims' => 3,
47+
'datatype' => 'float32',
48+
'distance_metric' => 'cosine',
49+
]
50+
]
51+
];
52+
$this->jsonSchema = [
53+
'index' => [
54+
'name' => 'foobar',
55+
'prefix' => 'foo:',
56+
'storage_type' => 'json'
57+
],
58+
'fields' => [
59+
'$.id' => [
60+
'type' => 'text',
61+
'alias' => 'foo',
62+
],
63+
'$.count' => [
64+
'type' => 'numeric',
65+
],
66+
'$.id_embeddings' => [
67+
'type' => 'vector',
68+
'algorithm' => 'flat',
69+
'dims' => 3,
70+
'datatype' => 'float32',
71+
'distance_metric' => 'cosine',
72+
]
73+
]
74+
];
75+
}
76+
77+
/**
78+
* @return void
79+
*/
80+
public function testCreatesHashIndexWithMultipleFields(): void
81+
{
82+
$index = new SearchIndex($this->client, $this->hashSchema);
83+
$this->assertEquals('OK', $index->create());
84+
85+
$indexInfo = $this->client->ftinfo($this->hashSchema['index']['name']);
86+
87+
$this->assertEquals('foobar', $indexInfo[1]);
88+
$this->assertEquals('HASH', $indexInfo[5][1]);
89+
$this->assertEquals('foo:', $indexInfo[5][3][0]);
90+
$this->assertEquals('foo', $indexInfo[7][0][3]);
91+
$this->assertEquals('TEXT', $indexInfo[7][0][5]);
92+
$this->assertEquals('NUMERIC', $indexInfo[7][1][5]);
93+
$this->assertEquals('VECTOR', $indexInfo[7][2][5]);
94+
95+
$this->assertEquals(
96+
'OK',
97+
$index->load('foo:1', ['id' => '1', 'count' => 10, 'id_embeddings' => VectorHelper::toBytes([0.000001, 0.000002, 0.000003])])
98+
);
99+
100+
$searchResult = $this->client->ftsearch($this->hashSchema['index']['name'], '*');
101+
102+
$this->assertSame('foo:1', $searchResult[1]);
103+
$this->assertSame('1', $searchResult[2][1]);
104+
$this->assertSame('10', $searchResult[2][3]);
105+
$this->assertSame(VectorHelper::toBytes([0.000001, 0.000002, 0.000003]), $searchResult[2][5]);
106+
}
107+
108+
/**
109+
* @return void
110+
*/
111+
public function testCreatesJsonIndexWithMultipleFields(): void
112+
{
113+
$index = new SearchIndex($this->client, $this->jsonSchema);
114+
$this->assertEquals('OK', $index->create());
115+
116+
$indexInfo = $this->client->ftinfo($this->jsonSchema['index']['name']);
117+
118+
$this->assertEquals('foobar', $indexInfo[1]);
119+
$this->assertEquals('JSON', $indexInfo[5][1]);
120+
$this->assertEquals('foo:', $indexInfo[5][3][0]);
121+
$this->assertEquals('foo', $indexInfo[7][0][3]);
122+
$this->assertEquals('TEXT', $indexInfo[7][0][5]);
123+
$this->assertEquals('NUMERIC', $indexInfo[7][1][5]);
124+
$this->assertEquals('VECTOR', $indexInfo[7][2][5]);
125+
126+
$this->assertEquals(
127+
'OK',
128+
$index->load('foo:1', '{"id":"1","count":10,"id_embeddings":[0.000001, 0.000002, 0.000003]}')
129+
);
130+
131+
$searchResult = $this->client->ftsearch($this->jsonSchema['index']['name'], '*');
132+
133+
$this->assertSame('foo:1', $searchResult[1]);
134+
$this->assertSame('{"id":"1","count":10,"id_embeddings":[1e-6,2e-6,3e-6]}', $searchResult[2][1]);
135+
}
136+
137+
/**
138+
* @return void
139+
*/
140+
public function testCreatesHashIndexWithOverride(): void
141+
{
142+
$index = new SearchIndex($this->client, $this->hashSchema);
143+
$this->assertEquals('OK', $index->create());
144+
145+
$indexInfo = $this->client->ftinfo($this->hashSchema['index']['name']);
146+
147+
$this->assertEquals('foobar', $indexInfo[1]);
148+
$this->assertEquals('HASH', $indexInfo[5][1]);
149+
$this->assertEquals('foo:', $indexInfo[5][3][0]);
150+
$this->assertEquals('foo', $indexInfo[7][0][3]);
151+
$this->assertEquals('TEXT', $indexInfo[7][0][5]);
152+
$this->assertEquals('NUMERIC', $indexInfo[7][1][5]);
153+
$this->assertEquals('VECTOR', $indexInfo[7][2][5]);
154+
155+
$this->assertEquals('OK', $index->create(true));
156+
157+
$this->assertEquals(
158+
'OK',
159+
$index->load('foo:1', ['id' => '1', 'count' => 10, 'id_embeddings' => VectorHelper::toBytes([0.000001, 0.000002, 0.000003])])
160+
);
161+
162+
$searchResult = $this->client->ftsearch($this->hashSchema['index']['name'], '*');
163+
164+
$this->assertSame('foo:1', $searchResult[1]);
165+
$this->assertSame('1', $searchResult[2][1]);
166+
$this->assertSame('10', $searchResult[2][3]);
167+
$this->assertSame(VectorHelper::toBytes([0.000001, 0.000002, 0.000003]), $searchResult[2][5]);
168+
}
169+
170+
/**
171+
* @return void
172+
*/
173+
public function testFetchHashData(): void
174+
{
175+
$index = new SearchIndex($this->client, $this->hashSchema);
176+
$this->assertEquals('OK', $index->create());
177+
178+
$this->assertEquals(
179+
'OK',
180+
$index->load('foo:1', ['id' => '1', 'count' => 10, 'id_embeddings' => VectorHelper::toBytes([0.000001, 0.000002, 0.000003])])
181+
);
182+
183+
$this->assertEquals(
184+
['id' => '1', 'count' => 10, 'id_embeddings' => VectorHelper::toBytes([0.000001, 0.000002, 0.000003])],
185+
$index->fetch('1'));
186+
}
187+
188+
/**
189+
* @return void
190+
*/
191+
public function testFetchJsonData(): void
192+
{
193+
$index = new SearchIndex($this->client, $this->jsonSchema);
194+
$this->assertEquals('OK', $index->create());
195+
196+
$this->assertEquals(
197+
'OK',
198+
$index->load('foo:1', '{"id":"1","count":10,"id_embeddings":[0.000001, 0.000002, 0.000003]}')
199+
);
200+
201+
$this->assertEquals(
202+
'{"id":"1","count":10,"id_embeddings":[1e-6,2e-6,3e-6]}',
203+
$index->fetch('1'));
204+
}
205+
}

tests/Unit/Enum/SearchFieldTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Unit\Enum;
3+
namespace Vladvildanov\PredisVl\Unit\Enum;
44

55
use PHPUnit\Framework\TestCase;
66
use Predis\Command\Argument\Search\SchemaFields\NumericField;

tests/Unit/FactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Unit;
3+
namespace Vladvildanov\PredisVl\Unit;
44

55
use PHPUnit\Framework\TestCase;
66
use Predis\Command\Argument\Search\CreateArguments;

tests/Unit/Index/SearchIndexTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Unit\Index;
3+
namespace Vladvildanov\PredisVl\Unit\Index;
44

55
use Exception;
66
use Mockery;

tests/Unit/VectorHelperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Unit;
3+
namespace Vladvildanov\PredisVl\Unit;
44

55
use PHPUnit\Framework\TestCase;
66
use Vladvildanov\PredisVl\VectorHelper;

tests/Unit/Vectorizer/FactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Unit\Vectorizer;
3+
namespace Vladvildanov\PredisVl\Unit\Vectorizer;
44

55
use Exception;
66
use PHPUnit\Framework\TestCase;

0 commit comments

Comments
 (0)