packages/ztd-query-sqlite/tests/Unit/Rewrite/FullText/MatchExpressionRewriterTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Rewrite\FullText;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Platform\Sqlite\Rewrite\FullText\MatchExpressionRewriter;
11use ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile;
12use ZtdQuery\Sql\SqlToken;
13use ZtdQuery\Sql\SqlTokenKind;
14use ZtdQuery\Sql\SqlTokenStream;
15
16#[CoversClass(MatchExpressionRewriter::class)]
17#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Expression\AssignmentColumnParser::class)]
18#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Expression\AssignmentParser::class)]
19#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Expression\ValueListParser::class)]
20#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Insert\InsertClauseParser::class)]
21#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\ExpressionSpan::class)]
22#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\IdentifierDecoder::class)]
23#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\LiteralMasker::class)]
24#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\OpaqueSqlSpan::class)]
25#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\QuotedSpan::class)]
26#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\TopLevelKeywordScanner::class)]
27#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Relation\RelationSourceParser::class)]
28#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Statement\StatementClassifier::class)]
29#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Statement\StatementStructure::class)]
30#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Statement\TargetTableParser::class)]
31#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Update\UpdateClauseParser::class)]
32#[UsesClass(\ZtdQuery\Platform\Sqlite\Rewrite\FullText\FullTextColumns::class)]
33#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteIdentifierQuoter::class)]
34#[UsesClass(SqliteLexerProfile::class)]
35#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteLexicalMasker::class)]
36#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteParser::class)]
37#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Relation\SqliteSelectRelationParser::class)]
38final class MatchExpressionRewriterTest extends TestCase
39{
40 public function testExpressionEditProjectsAllDocumentColumns(): void
41 {
42 $stream = SqlTokenStream::tokenize("docs MATCH 'hello'", SqliteLexerProfile::create());
43 $rewriter = new MatchExpressionRewriter(new \ZtdQuery\Platform\Sqlite\Sql\SqliteParser(), new \ZtdQuery\Platform\Sqlite\Sql\SqliteIdentifierQuoter());
44 $edit = $rewriter->expressionEdit($stream, $stream->significantTokens()[1], ['docs' => ['columns' => ['title', 'body']]]);
45 self::assertNotNull($edit);
46 self::assertSame(0, $edit['start']);
47 self::assertSame(18, $edit['end']);
48 self::assertStringContainsString('COALESCE(CAST("title" AS TEXT)', $edit['replacement']);
49 self::assertStringContainsString('COALESCE(CAST("body" AS TEXT)', $edit['replacement']);
50 self::assertStringContainsString("CAST(('hello') AS TEXT)", $edit['replacement']);
51 }
52
53 public function testExpressionEditRejectsAnUnknownDocument(): void
54 {
55 $stream = SqlTokenStream::tokenize("unknown MATCH 'hello'", SqliteLexerProfile::create());
56 $rewriter = new MatchExpressionRewriter(new \ZtdQuery\Platform\Sqlite\Sql\SqliteParser(), new \ZtdQuery\Platform\Sqlite\Sql\SqliteIdentifierQuoter());
57 self::assertNull($rewriter->expressionEdit($stream, $stream->significantTokens()[1], ['docs' => ['columns' => ['body']]]));
58 }
59
60 public function testIsIdentifierRejectsLiteralTokens(): void
61 {
62 self::assertTrue(MatchExpressionRewriter::isIdentifier(new SqlToken(SqlTokenKind::Word, 'body', 0, 0, 0)));
63 self::assertTrue(MatchExpressionRewriter::isIdentifier(new SqlToken(SqlTokenKind::QuotedIdentifier, '"body"', 0, 0, 0)));
64 self::assertFalse(MatchExpressionRewriter::isIdentifier(new SqlToken(SqlTokenKind::String, "'body'", 0, 0, 0)));
65 }
66
67 public function testIsQueryExpressionAcceptsOnlyStringOrParameter(): void
68 {
69 self::assertTrue(MatchExpressionRewriter::isQueryExpression(new SqlToken(SqlTokenKind::String, "'query'", 0, 0, 0)));
70 self::assertTrue(MatchExpressionRewriter::isQueryExpression(new SqlToken(SqlTokenKind::Parameter, '?', 0, 0, 0)));
71 self::assertFalse(MatchExpressionRewriter::isQueryExpression(new SqlToken(SqlTokenKind::Word, 'query', 0, 0, 0)));
72 }
73
74}
75