packages/ztd-query-core/tests/Unit/Shadow/Mutation/RowConstraintsTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Shadow\Mutation;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Exception\DuplicateKeyException;
11use ZtdQuery\Exception\NotNullViolationException;
12use ZtdQuery\Schema\TableDefinition;
13use ZtdQuery\Shadow\Mutation\RowConstraints;
14
15#[CoversClass(RowConstraints::class)]
16#[UsesClass(TableDefinition::class)]
17#[UsesClass(DuplicateKeyException::class)]
18#[UsesClass(NotNullViolationException::class)]
19final class RowConstraintsTest extends TestCase
20{
21    public function testAssertNoNullWhereNoneIsAllowedRefusesANullInSuchAColumn(): void
22    {
23        $constraints = new RowConstraints(
24            new TableDefinition(['id', 'name'], [], ['id'], ['name'], []),
25            'users',
26            'INSERT INTO users VALUES (1, NULL)',
27        );
28
29        $this->expectException(NotNullViolationException::class);
30
31        $constraints->assertNoNullWhereNoneIsAllowed(['id' => 1, 'name' => null]);
32    }
33
34    public function testAssertNoNullWhereNoneIsAllowedLetsAColumnTheRowNeverWroteThrough(): void
35    {
36        $constraints = new RowConstraints(
37            new TableDefinition(['id', 'name'], [], ['id'], ['name'], []),
38            'users',
39            'INSERT INTO users (id) VALUES (1)',
40        );
41
42        $this->expectNotToPerformAssertions();
43
44        $constraints->assertNoNullWhereNoneIsAllowed(['id' => 1]);
45    }
46
47    public function testAssertNoNullWhereNoneIsAllowedLetsEverythingThroughForATableNothingDescribes(): void
48    {
49        $constraints = new RowConstraints(null, 'users', 'INSERT INTO users VALUES (NULL)');
50
51        $this->expectNotToPerformAssertions();
52
53        $constraints->assertNoNullWhereNoneIsAllowed(['name' => null]);
54    }
55
56    public function testAssertNoDuplicateUniqueKeyRefusesARowCollidingWithOneAlreadyThere(): void
57    {
58        $constraints = new RowConstraints(
59            new TableDefinition(['id', 'email'], [], ['id'], [], ['email' => ['email']]),
60            'users',
61            'INSERT INTO users VALUES (2, "a@example.com")',
62        );
63
64        $this->expectException(DuplicateKeyException::class);
65
66        $constraints->assertNoDuplicateUniqueKey(
67            ['id' => 2, 'email' => 'a@example.com'],
68            [['id' => 1, 'email' => 'a@example.com']],
69        );
70    }
71
72    public function testAssertNoDuplicateUniqueKeyLetsARowNotCollideWithItself(): void
73    {
74        $constraints = new RowConstraints(
75            new TableDefinition(['id', 'email'], [], ['id'], [], ['email' => ['email']]),
76            'users',
77            'UPDATE users SET email = "a@example.com" WHERE id = 1',
78        );
79
80        $this->expectNotToPerformAssertions();
81
82        $constraints->assertNoDuplicateUniqueKey(
83            ['id' => 1, 'email' => 'a@example.com'],
84            [['id' => 1, 'email' => 'a@example.com']],
85            ['id'],
86        );
87    }
88
89    public function testAssertNoDuplicateUniqueKeyLetsTwoNullsThroughBecauseNeitherCollides(): void
90    {
91        $constraints = new RowConstraints(
92            new TableDefinition(['id', 'email'], [], ['id'], [], ['email' => ['email']]),
93            'users',
94            'INSERT INTO users VALUES (2, NULL)',
95        );
96
97        $this->expectNotToPerformAssertions();
98
99        $constraints->assertNoDuplicateUniqueKey(
100            ['id' => 2, 'email' => null],
101            [['id' => 1, 'email' => null]],
102        );
103    }
104
105    public function testCarriesNullInReportsAColumnTheRowLeftNull(): void
106    {
107        $constraints = new RowConstraints(null, 'users', '');
108
109        self::assertTrue($constraints->carriesNullIn(['email' => null], ['email']));
110    }
111
112    public function testCarriesNullInReportsAColumnTheRowNeverWrote(): void
113    {
114        $constraints = new RowConstraints(null, 'users', '');
115
116        self::assertTrue($constraints->carriesNullIn(['id' => 1], ['email']));
117    }
118
119    public function testCarriesNullInIsFalseWhereEveryColumnCarriesSomething(): void
120    {
121        $constraints = new RowConstraints(null, 'users', '');
122
123        self::assertFalse($constraints->carriesNullIn(['email' => 'a@example.com'], ['email']));
124    }
125
126    public function testAgreeOnReportsTwoRowsCarryingTheSameValuesThere(): void
127    {
128        $constraints = new RowConstraints(null, 'users', '');
129
130        self::assertTrue($constraints->agreeOn(['id' => 1], ['id' => 1, 'name' => 'a'], ['id']));
131    }
132
133    public function testAgreeOnIsFalseWhereTheRowAlreadyThereCarriesNothing(): void
134    {
135        $constraints = new RowConstraints(null, 'users', '');
136
137        self::assertFalse($constraints->agreeOn(['id' => 1], ['id' => null], ['id']));
138    }
139
140    public function testKeyValuesReadsWhatTheRowCarriesUnderTheColumnNames(): void
141    {
142        $constraints = new RowConstraints(null, 'users', '');
143
144        self::assertSame(['id' => 1, 'email' => null], $constraints->keyValues(['id' => 1], ['id', 'email']));
145    }
146}
147