mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 00:01:21 +00:00
Update manticore search usage
This commit is contained in:
@@ -18,6 +18,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string}> */
|
||||
public static function negationQueriesProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -37,6 +38,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string}> */
|
||||
public static function phraseQueriesProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -55,6 +57,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string}> */
|
||||
public static function orQueriesProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -71,6 +74,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string}> */
|
||||
public static function wildcardQueriesProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -88,6 +92,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string}> */
|
||||
public static function groupingQueriesProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -104,6 +109,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string}> */
|
||||
public static function escapingQueriesProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -143,6 +149,24 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_adds_a_normalized_release_name_query_for_punctuation_separators(): void
|
||||
{
|
||||
$reflection = new ReflectionClass(ManticoreSearchDriver::class);
|
||||
$method = $reflection->getMethod('scopeReleaseSearchQuery');
|
||||
|
||||
$query = $method->invoke(
|
||||
null,
|
||||
'Love.Is.A.Dogs.Best.Friend.2025.1080p.WEB-DL.HEVC.x265-BONE',
|
||||
ManticoreSearchDriver::prepareUserSearchQuery('Love.Is.A.Dogs.Best.Friend.2025.1080p.WEB-DL.HEVC.x265-BONE'),
|
||||
'@searchname'
|
||||
);
|
||||
|
||||
$this->assertStringContainsString('@searchname (Love.Is.A.Dogs.Best.Friend.2025.1080p.WEB\\-DL.HEVC.x265\\-BONE)', $query);
|
||||
$this->assertStringContainsString('@searchname (Love Is A Dogs Best Friend 2025 1080p WEB DL HEVC x265 BONE)', $query);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string}> */
|
||||
public static function edgeCaseQueriesProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -163,6 +187,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
$this->assertStringContainsString('circus', $result);
|
||||
}
|
||||
|
||||
/** @param array<string, string>|string $input */
|
||||
#[Test]
|
||||
#[DataProvider('negationDetectionProvider')]
|
||||
public function it_detects_negation_operators(array|string $input, bool $expected): void
|
||||
@@ -171,6 +196,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/** @return array<string, array{array<string, string>|string, bool}> */
|
||||
public static function negationDetectionProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -244,7 +270,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
|
||||
$this->assertIsString($driverSource);
|
||||
$this->assertStringContainsString(
|
||||
"\$terms[] = '@@relaxed '.self::scopePreparedQueryToField(\$prepared, '@'.\$key);",
|
||||
"\$terms[] = '@@relaxed '.self::scopeReleaseSearchQuery((string) \$value, \$prepared, '@'.\$key);",
|
||||
$driverSource
|
||||
);
|
||||
$this->assertStringNotContainsString(
|
||||
@@ -260,7 +286,7 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
|
||||
$this->assertIsString($driverSource);
|
||||
$this->assertStringContainsString(
|
||||
"\$searchExpr = '@@relaxed '.self::scopePreparedQueryToField(\$preparedSearch, '@searchname');",
|
||||
"\$searchExpr = '@@relaxed '.self::scopeReleaseSearchQuery(\$searchTerm, \$preparedSearch, '@searchname');",
|
||||
$driverSource
|
||||
);
|
||||
$this->assertStringNotContainsString(
|
||||
|
||||
@@ -121,7 +121,7 @@ class NntmuxSearchReconcileFetchIndexedIdsTest extends TestCase
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function sql(...$params): mixed
|
||||
public function sql(mixed ...$params): mixed
|
||||
{
|
||||
$this->capturedSql = $params[0] ?? null;
|
||||
|
||||
@@ -143,6 +143,9 @@ class NntmuxSearchReconcileFetchIndexedIdsTest extends TestCase
|
||||
$this->assertNotNull($client->capturedSql);
|
||||
$this->assertStringContainsString('LIMIT 500', $client->capturedSql);
|
||||
$this->assertStringContainsString('OPTION max_matches=500', $client->capturedSql);
|
||||
$driverSource = file_get_contents(__DIR__.'/../../app/Services/Search/Drivers/ManticoreSearchDriver.php');
|
||||
self::assertIsString($driverSource);
|
||||
self::assertStringContainsString('ORDER BY id ASC', $driverSource);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,7 +162,7 @@ class NntmuxSearchReconcileFetchIndexedIdsTest extends TestCase
|
||||
// Bypass parent ctor: we never hit the network in this test.
|
||||
}
|
||||
|
||||
public function sql(...$params): mixed
|
||||
public function sql(mixed ...$params): mixed
|
||||
{
|
||||
return $this->stubResponse;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,61 @@ use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ReleaseSearchIndexDocumentTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function normalize_builds_the_complete_public_projection_without_sensitive_fields(): void
|
||||
{
|
||||
$document = ReleaseSearchIndexDocument::normalize([
|
||||
'id' => '42',
|
||||
'guid' => 'guid-42',
|
||||
'name' => 'Release.Name',
|
||||
'searchname' => 'Release.Name',
|
||||
'fromname' => 'poster',
|
||||
'filename' => 'file.nzb',
|
||||
'categories_id' => '1',
|
||||
'category_name' => 'TV',
|
||||
'parent_category' => 'Video',
|
||||
'sub_category' => 'TV',
|
||||
'groups_id' => '2',
|
||||
'group_name' => 'alt.example',
|
||||
'tmdbid' => '123',
|
||||
'tv_episodes_id' => '456',
|
||||
'passwordstatus' => '-1',
|
||||
'comments' => '3',
|
||||
'postdate' => '2026-01-02 03:04:05',
|
||||
'adddate' => '2026-01-03 04:05:06',
|
||||
'nzb_password' => 'must-not-be-indexed',
|
||||
]);
|
||||
|
||||
self::assertSame(42, $document['id']);
|
||||
self::assertSame('Release Name', $document['plainsearchname']);
|
||||
self::assertSame(123, $document['tmdbid']);
|
||||
self::assertSame(456, $document['tv_episodes_id']);
|
||||
self::assertSame(-1, $document['passwordstatus']);
|
||||
self::assertGreaterThan(0, $document['postdate_ts']);
|
||||
self::assertGreaterThan(0, $document['adddate_ts']);
|
||||
self::assertArrayNotHasKey('nzb_password', $document);
|
||||
$documentFields = array_keys($document);
|
||||
$declaredFields = ReleaseSearchIndexDocument::fields();
|
||||
sort($documentFields);
|
||||
sort($declaredFields);
|
||||
self::assertSame($declaredFields, $documentFields);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function indexed_documents_can_be_hydrated_for_legacy_release_consumers(): void
|
||||
{
|
||||
$row = ReleaseSearchIndexDocument::toReleaseRow([
|
||||
'id' => 42,
|
||||
'postdate_ts' => 1767323045,
|
||||
'adddate_ts' => 1767413106,
|
||||
'name' => 'Release',
|
||||
]);
|
||||
|
||||
self::assertSame('2026-01-02 03:04:05', $row['postdate']);
|
||||
self::assertSame('2026-01-03 04:05:06', $row['adddate']);
|
||||
self::assertSame(42, $row['id']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function normalize_for_bulk_preserves_timestamps_when_row_is_already_normalized(): void
|
||||
{
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Services\Search;
|
||||
|
||||
use App\Services\Search\Drivers\ManticoreSearchDriver;
|
||||
use Manticoresearch\Client;
|
||||
use Manticoresearch\Table;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
final class ManticoreDeleteReleasesTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function delete_releases_uses_document_api_not_sql(): void
|
||||
{
|
||||
$config = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 9308,
|
||||
'indexes' => [
|
||||
'releases' => 'releases_rt',
|
||||
'predb' => 'predb_rt',
|
||||
],
|
||||
];
|
||||
|
||||
$table = $this->createMock(Table::class);
|
||||
$table->expects($this->once())
|
||||
->method('deleteDocumentsByIds')
|
||||
->with([149331415, 42]);
|
||||
|
||||
$client = $this->createMock(Client::class);
|
||||
$client->expects($this->once())
|
||||
->method('table')
|
||||
->with('releases_rt')
|
||||
->willReturn($table);
|
||||
$client->expects($this->never())->method('sql');
|
||||
|
||||
$driver = new ManticoreSearchDriver($config);
|
||||
$prop = new \ReflectionProperty(ManticoreSearchDriver::class, 'manticoreSearch');
|
||||
$prop->setAccessible(true);
|
||||
$prop->setValue($driver, $client);
|
||||
|
||||
$driver->deleteReleases([149331415, 42, 0, 149331415]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function delete_releases_skips_empty_id_list(): void
|
||||
{
|
||||
$config = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 9308,
|
||||
'indexes' => [
|
||||
'releases' => 'releases_rt',
|
||||
'predb' => 'predb_rt',
|
||||
],
|
||||
];
|
||||
|
||||
$client = $this->createMock(Client::class);
|
||||
$client->expects($this->never())->method('table');
|
||||
$client->expects($this->never())->method('sql');
|
||||
|
||||
$driver = new ManticoreSearchDriver($config);
|
||||
$prop = new \ReflectionProperty(ManticoreSearchDriver::class, 'manticoreSearch');
|
||||
$prop->setAccessible(true);
|
||||
$prop->setValue($driver, $client);
|
||||
|
||||
$driver->deleteReleases([0, -1]);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Tests\Unit\Services\Search;
|
||||
|
||||
use App\Services\Search\Drivers\ManticoreSearchDriver;
|
||||
use App\Support\ReleaseSearchIndexDocument;
|
||||
use Manticoresearch\Client;
|
||||
use Manticoresearch\Exceptions\ResponseException;
|
||||
use Manticoresearch\Request;
|
||||
@@ -130,4 +131,40 @@ final class ManticoreInsertRetryTest extends TestCase
|
||||
$ok = $refP->invoke($driver, $this->releaseRow(7));
|
||||
$this->assertFalse($ok);
|
||||
}
|
||||
|
||||
public function test_replace_release_document_preserves_pre_normalized_timestamps(): void
|
||||
{
|
||||
$config = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 9308,
|
||||
'retry_attempts' => 1,
|
||||
'indexes' => ['releases' => 'releases_rt', 'predb' => 'predb_rt'],
|
||||
];
|
||||
$document = ReleaseSearchIndexDocument::normalize($this->releaseRow(42));
|
||||
|
||||
$table = $this->createMock(Table::class);
|
||||
$table->expects($this->once())
|
||||
->method('replaceDocument')
|
||||
->with(
|
||||
$this->callback(static fn (array $indexed): bool =>
|
||||
$indexed['postdate_ts'] === $document['postdate_ts']
|
||||
&& $indexed['adddate_ts'] === $document['adddate_ts']
|
||||
),
|
||||
42
|
||||
);
|
||||
|
||||
$client = $this->createMock(Client::class);
|
||||
$client->expects($this->once())
|
||||
->method('table')
|
||||
->with('releases_rt')
|
||||
->willReturn($table);
|
||||
|
||||
$driver = new ManticoreSearchDriver($config);
|
||||
$prop = new \ReflectionProperty(ManticoreSearchDriver::class, 'manticoreSearch');
|
||||
$prop->setValue($driver, $client);
|
||||
|
||||
$method = new ReflectionMethod(ManticoreSearchDriver::class, 'replaceReleaseDocumentWithRetry');
|
||||
|
||||
$this->assertTrue($method->invoke($driver, $document));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user