ReplicaMasterAwareRecordIdsAcquirerTest   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 145
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 3
dl 0
loc 145
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 7 1
A testWhenAllRecordsExistInReplica() 0 15 1
A testWhenAllRecordsExistInMaster() 0 15 1
A testWhenAllRecordsDoNotExistInReplicaOrMaster() 0 9 1
A testWhenSomeRecordsDoNotExistInReplicaButExistInMaster() 0 29 1
A assertNoRecordsInDb() 0 9 1
A assertSameRecordsInDb() 0 9 1
A recordsToSelectConditions() 0 8 2
A getTestSubjectInstance() 0 10 1
A getTestRecords() 0 8 1
1
<?php
2
3
namespace Wikibase\TermStore\MediaWiki\Tests\Integration\PackagePrivate\Util;
4
5
use PHPUnit\Framework\TestCase;
6
use Wikibase\TermStore\MediaWiki\PackagePrivate\Util\ReplicaMasterAwareRecordIdsAcquirer;
7
use Wikibase\TermStore\MediaWiki\Tests\Util\FakeLoadBalancer;
8
use Wikimedia\Rdbms\IDatabase;
9
use Wikimedia\Rdbms\DatabaseSqlite;
10
11
class ReplicaMasterAwareRecordIdsAcquirerTest extends TestCase {
12
13
	const TABLE_DDL_FILE_PATH = __DIR__ . '/ReplicaMasterAwareRecordIdsAcquirerTest_tableDDL.sql';
14
	const TABLE_NAME = 'replica_master_aware_record_ids_acquirer_test';
15
	const ID_COLUMN = 'id';
16
17
	/**
18
	 * @var IDatabase $dbMaster
19
	 */
20
	private $dbMaster;
21
22
	/**
23
	 * @var IDatabase $dbReplica
24
	 */
25
	private $dbReplica;
26
27
	public function setUp() {
28
		$this->dbMaster = DatabaseSqlite::newStandaloneInstance( ':memory:' );
29
		$this->dbMaster->sourceFile( self::TABLE_DDL_FILE_PATH );
30
31
		$this->dbReplica = DatabaseSqlite::newStandaloneInstance( ':memory:' );
32
		$this->dbReplica->sourceFile( self::TABLE_DDL_FILE_PATH );
33
	}
34
35
	public function testWhenAllRecordsExistInReplica() {
36
		$records = $this->getTestRecords();
37
38
		$this->dbReplica->insert(
39
			self::TABLE_NAME,
40
			$records
41
		);
42
		$this->assertSameRecordsInDb( $records, $this->dbReplica );
43
44
		$idsAcquirer = $this->getTestSubjectInstance();
45
		$acquiredRecordsWithIds = $idsAcquirer->acquireIds( $records );
46
47
		$this->assertNoRecordsInDb( $records, $this->dbMaster );
48
		$this->assertSameRecordsInDb( $acquiredRecordsWithIds, $this->dbReplica );
49
	}
50
51
	public function testWhenAllRecordsExistInMaster() {
52
		$records = $this->getTestRecords();
53
54
		$this->dbMaster->insert(
55
			self::TABLE_NAME,
56
			$records
57
		);
58
		$this->assertSameRecordsInDb( $records, $this->dbMaster );
59
60
		$idsAcquirer = $this->getTestSubjectInstance();
61
		$acquiredRecordsWithIds = $idsAcquirer->acquireIds( $records );
62
63
		$this->assertNoRecordsInDb( $records, $this->dbReplica );
64
		$this->assertSameRecordsInDb( $acquiredRecordsWithIds, $this->dbMaster );
65
	}
66
67
	public function testWhenAllRecordsDoNotExistInReplicaOrMaster() {
68
		$records = $this->getTestRecords();
69
70
		$idsAcquirer = $this->getTestSubjectInstance();
71
		$acquiredRecordsWithIds = $idsAcquirer->acquireIds( $records );
72
73
		$this->assertNoRecordsInDb( $records, $this->dbReplica );
74
		$this->assertSameRecordsInDb( $acquiredRecordsWithIds, $this->dbMaster );
75
	}
76
77
	public function testWhenSomeRecordsDoNotExistInReplicaButExistInMaster() {
78
		$records = $this->getTestRecords();
79
80
		$recordsInReplica = [ $records[0], $records[1] ];
81
		$recordsInMaster = [ $records[2] ];
82
83
		$this->dbReplica->insert(
84
			self::TABLE_NAME,
85
			$recordsInReplica
86
		);
87
		$this->assertSameRecordsInDb( $recordsInReplica, $this->dbReplica );
88
89
		$this->dbMaster->insert(
90
			self::TABLE_NAME,
91
			$recordsInMaster
92
		);
93
		$this->assertSameRecordsInDb( $recordsInMaster, $this->dbMaster );
94
95
		$idsAcquirer = $this->getTestSubjectInstance();
96
		$acquiredRecordsWithIds = $idsAcquirer->acquireIds( $records );
97
98
		$this->assertSame(
99
			count( $acquiredRecordsWithIds ),
100
			count( $records )
101
		);
102
		$this->assertSameRecordsInDb( [ $records[3] ], $this->dbMaster );
103
		$this->assertNoRecordsInDb( $recordsInReplica, $this->dbMaster );
104
		$this->assertNoRecordsInDb( $recordsInMaster, $this->dbReplica );
105
	}
106
107
	private function assertNoRecordsInDb( array $records, IDatabase $db ) {
108
		$recordsInDbCount = $db->selectRowCount(
109
			self::TABLE_NAME,
110
			'*',
111
			$this->recordsToSelectConditions( $records, $db )
112
		);
113
114
		$this->assertSame( 0, $recordsInDbCount );
115
	}
116
117
	private function assertSameRecordsInDb( array $records, IDatabase $db ) {
118
		$recordsInDbCount = $db->selectRowCount(
119
			self::TABLE_NAME,
120
			'*',
121
			$this->recordsToSelectConditions( $records, $db )
122
		);
123
124
		$this->assertCount( $recordsInDbCount, $records );
125
	}
126
127
	private function recordsToSelectConditions( array $records, IDatabase $db ) {
128
		$conditionsPairs = [];
0 ignored issues
show
Unused Code introduced by
$conditionsPairs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
129
		foreach ( $records as $record ) {
130
			$conditionPairs[] = $db->makeList( $record, IDatabase::LIST_AND );
0 ignored issues
show
Coding Style Comprehensibility introduced by
$conditionPairs was never initialized. Although not strictly required by PHP, it is generally a good practice to add $conditionPairs = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
131
		}
132
133
		return $db->makeList( $conditionPairs, IDatabase::LIST_OR );
0 ignored issues
show
Bug introduced by
The variable $conditionPairs does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
134
	}
135
136
	private function getTestSubjectInstance() {
137
		return new ReplicaMasterAwareRecordIdsAcquirer(
138
			new FakeLoadBalancer( [
139
				'dbr' => $this->dbReplica,
140
				'dbw' => $this->dbMaster,
141
			] ),
142
			self::TABLE_NAME,
143
			self::ID_COLUMN
144
		);
145
	}
146
147
	private function getTestRecords() {
148
		return [
149
			[ 'column_value' => 'valueA1', 'column_id' => '1' ],
150
			[ 'column_value' => 'valueA2', 'column_id' => '2' ],
151
			[ 'column_value' => 'valueA3', 'column_id' => '3' ],
152
			[ 'column_value' => 'valueA4', 'column_id' => '4' ]
153
		];
154
	}
155
}
156
157