packages/ztd-query-postgres/tests/Unit/Sql/Transaction/PgSqlTransactionStatementParserTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Sql\Transaction;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use ZtdQuery\Platform\Postgres\Sql\PgSqlLexerProfile;
12use ZtdQuery\Platform\Postgres\Sql\Transaction\PgSqlTransactionStatementParser;
13use ZtdQuery\Shadow\ShadowStore;
14use ZtdQuery\Shadow\ShadowTransactions;
15
16#[CoversClass(PgSqlTransactionStatementParser::class)]
17#[UsesClass(PgSqlLexerProfile::class)]
18#[CoversClass(\ZtdQuery\Platform\Postgres\Sql\Transaction\KeywordForm::class)]
19final class PgSqlTransactionStatementParserTest extends TestCase
20{
21    #[DataProvider('providerPostgresTransactionForms')]
22    public function testParsesPostgresTransactionForms(string $sql): void
23    {
24        self::assertNotNull((new PgSqlTransactionStatementParser())->parse($sql));
25    }
26
27    /**
28     * @return array<string, array{string}>
29     */
30    public static function providerPostgresTransactionForms(): array
31    {
32        return [
33            'begin' => ['BEGIN;'],
34            'begin work' => ['BEGIN WORK'],
35            'begin transaction' => ['BEGIN TRANSACTION'],
36            'start' => ['START TRANSACTION'],
37            'commit' => ['COMMIT'],
38            'commit work' => ['COMMIT WORK'],
39            'commit transaction' => ['COMMIT TRANSACTION'],
40            'end' => ['END'],
41            'end work' => ['END WORK'],
42            'end transaction' => ['END TRANSACTION'],
43            'rollback' => ['ROLLBACK'],
44            'rollback work' => ['ROLLBACK WORK'],
45            'rollback transaction' => ['ROLLBACK TRANSACTION'],
46            'savepoint' => ['SAVEPOINT "a""b"'],
47            'rollback to' => ['ROLLBACK TO point'],
48            'rollback to savepoint' => ['ROLLBACK TO SAVEPOINT point'],
49            'release' => ['RELEASE point'],
50            'release savepoint' => ['RELEASE SAVEPOINT point'],
51        ];
52    }
53
54    public function testRejectsNonPostgresAndMalformedForms(): void
55    {
56        $parser = new PgSqlTransactionStatementParser();
57
58        self::assertNull($parser->parse(''));
59        self::assertNull($parser->parse('BEGIN IMMEDIATE'));
60        self::assertNull($parser->parse('SAVEPOINT `point`'));
61        self::assertNull($parser->parse('SAVEPOINT point extra'));
62        self::assertNull($parser->parse('SAVEPOINT "broken'));
63    }
64
65    public function testAppliesUnescapedPostgresSavepointName(): void
66    {
67        $store = new ShadowStore();
68        $store->set('items', [['id' => 1]]);
69        $transactions = new ShadowTransactions($store);
70        $transactions->begin();
71        $statement = (new PgSqlTransactionStatementParser())->parse('SAVEPOINT "a""b"');
72        self::assertNotNull($statement);
73        $statement->apply($transactions);
74        $store->insert('items', [['id' => 2]]);
75
76        $transactions->rollBackTo('a"b');
77
78        self::assertSame([['id' => 1]], $store->get('items'));
79    }
80}
81