diff --git a/tests/framework/db/ExpressionTest.php b/tests/framework/db/ExpressionTest.php new file mode 100644 index 00000000000..57bb5d6caf3 --- /dev/null +++ b/tests/framework/db/ExpressionTest.php @@ -0,0 +1,89 @@ +expression); + self::assertSame([], $expression->params); + } + + public function testConstructorWithConfig(): void + { + $expression = new Expression('DEFAULT', [], ['expression' => 'overridden']); + + self::assertSame('overridden', $expression->expression); + } + + public function testToString(): void + { + $expression = new Expression('COUNT(*)'); + + self::assertSame('COUNT(*)', (string) $expression); + self::assertSame('COUNT(*)', $expression->__toString()); + } + + public function testImplementsExpressionInterface(): void + { + $expression = new Expression('1'); + + self::assertInstanceOf(ExpressionInterface::class, $expression); + } + + public function testEmptyExpression(): void + { + $expression = new Expression(''); + + self::assertSame('', $expression->expression); + self::assertSame('', (string) $expression); + } + + /** + * @dataProvider expressionsProvider + */ + public function testVariousExpressions(string $sql, array $params): void + { + $expression = new Expression($sql, $params); + + self::assertSame($sql, (string) $expression); + self::assertSame($params, $expression->params); + } + + public static function expressionsProvider(): array + { + return [ + 'simple function' => ['NOW()', []], + 'aggregate' => ['COUNT(*)', []], + 'with placeholder' => ['status = :status', [':status' => 1]], + 'multiple placeholders' => [ + 'age BETWEEN :min AND :max', + [':min' => 18, ':max' => 65], + ], + 'subquery-like' => ['(SELECT MAX(id) FROM users)', []], + 'raw literal' => ['1', []], + 'complex expression' => [ + 'CASE WHEN status = :active THEN 1 ELSE 0 END', + [':active' => 'active'], + ], + ]; + } +} diff --git a/tests/framework/db/JsonExpressionTest.php b/tests/framework/db/JsonExpressionTest.php new file mode 100644 index 00000000000..0d47c4346b5 --- /dev/null +++ b/tests/framework/db/JsonExpressionTest.php @@ -0,0 +1,151 @@ + 1, 'b' => 2]; + $expression = new JsonExpression($data); + + self::assertSame($data, $expression->getValue()); + self::assertNull($expression->getType()); + } + + public function testConstructorWithType(): void + { + $expression = new JsonExpression([1, 2, 3], JsonExpression::TYPE_JSON); + + self::assertSame(JsonExpression::TYPE_JSON, $expression->getType()); + } + + public function testConstructorWithJsonbType(): void + { + $expression = new JsonExpression(['key' => 'val'], JsonExpression::TYPE_JSONB); + + self::assertSame(JsonExpression::TYPE_JSONB, $expression->getType()); + } + + public function testUnwrapsNestedJsonExpression(): void + { + $inner = new JsonExpression(['nested' => true]); + $outer = new JsonExpression($inner); + + self::assertSame(['nested' => true], $outer->getValue()); + } + + public function testUnwrapsNestedJsonExpressionPreservesOuterType(): void + { + $inner = new JsonExpression(['data' => 1], JsonExpression::TYPE_JSON); + $outer = new JsonExpression($inner, JsonExpression::TYPE_JSONB); + + self::assertSame(['data' => 1], $outer->getValue()); + self::assertSame(JsonExpression::TYPE_JSONB, $outer->getType()); + } + + public function testUnwrapsOnlyOneLevel(): void + { + $innermost = new JsonExpression('deep'); + $middle = new JsonExpression($innermost); + $outer = new JsonExpression($middle); + + self::assertSame('deep', $outer->getValue()); + } + + public function testImplementsExpressionInterface(): void + { + $expression = new JsonExpression([]); + + self::assertInstanceOf(ExpressionInterface::class, $expression); + } + + public function testImplementsJsonSerializable(): void + { + $expression = new JsonExpression([]); + + self::assertInstanceOf(\JsonSerializable::class, $expression); + } + + public function testJsonSerializeReturnsValue(): void + { + $data = ['x' => 10, 'y' => 20]; + $expression = new JsonExpression($data); + + self::assertSame($data, $expression->jsonSerialize()); + } + + public function testJsonSerializeWithScalar(): void + { + $expression = new JsonExpression('string value'); + + self::assertSame('string value', $expression->jsonSerialize()); + } + + public function testJsonSerializeWithNull(): void + { + $expression = new JsonExpression(null); + + self::assertNull($expression->jsonSerialize()); + } + + public function testJsonSerializeThrowsOnQueryInterface(): void + { + $query = new Query(); + $expression = new JsonExpression($query); + + $this->expectException(InvalidConfigException::class); + $expression->jsonSerialize(); + } + + public function testJsonEncodeUsesJsonSerialize(): void + { + $data = ['name' => 'test', 'count' => 42]; + $expression = new JsonExpression($data); + + self::assertSame('{"name":"test","count":42}', json_encode($expression)); + } + + /** + * @dataProvider valueTypesProvider + */ + public function testAcceptsVariousValueTypes($value): void + { + $expression = new JsonExpression($value); + + self::assertSame($value, $expression->getValue()); + } + + public static function valueTypesProvider(): array + { + return [ + 'array' => [['a', 'b', 'c']], + 'associative array' => [['key' => 'value']], + 'nested array' => [['a' => ['b' => ['c' => 1]]]], + 'string' => ['simple string'], + 'integer' => [42], + 'float' => [3.14], + 'boolean true' => [true], + 'boolean false' => [false], + 'null' => [null], + 'empty array' => [[]], + ]; + } +} diff --git a/tests/framework/db/TableSchemaTest.php b/tests/framework/db/TableSchemaTest.php new file mode 100644 index 00000000000..f1a28138932 --- /dev/null +++ b/tests/framework/db/TableSchemaTest.php @@ -0,0 +1,141 @@ +name = 'test_table'; + $table->fullName = 'test_table'; + + foreach ($columnNames as $name) { + $column = new ColumnSchema(); + $column->name = $name; + $column->isPrimaryKey = false; + $table->columns[$name] = $column; + } + + return $table; + } + + public function testGetColumnReturnsColumnSchema(): void + { + $table = $this->createTableSchema(); + + $column = $table->getColumn('name'); + + self::assertInstanceOf(ColumnSchema::class, $column); + self::assertSame('name', $column->name); + } + + public function testGetColumnReturnsNullForNonExistent(): void + { + $table = $this->createTableSchema(); + + self::assertNull($table->getColumn('nonexistent')); + } + + public function testGetColumnNames(): void + { + $table = $this->createTableSchema(['id', 'name', 'email']); + + self::assertSame(['id', 'name', 'email'], $table->getColumnNames()); + } + + public function testGetColumnNamesEmpty(): void + { + $table = $this->createTableSchema([]); + + self::assertSame([], $table->getColumnNames()); + } + + public function testColumnNamesProperty(): void + { + $table = $this->createTableSchema(['a', 'b']); + + self::assertSame(['a', 'b'], $table->columnNames); + } + + public function testFixPrimaryKeyWithSingleKey(): void + { + $table = $this->createTableSchema(); + + $table->fixPrimaryKey('id'); + + self::assertSame(['id'], $table->primaryKey); + self::assertTrue($table->columns['id']->isPrimaryKey); + self::assertFalse($table->columns['name']->isPrimaryKey); + self::assertFalse($table->columns['email']->isPrimaryKey); + } + + public function testFixPrimaryKeyWithCompositeKey(): void + { + $table = $this->createTableSchema(['user_id', 'role_id', 'data']); + + $table->fixPrimaryKey(['user_id', 'role_id']); + + self::assertSame(['user_id', 'role_id'], $table->primaryKey); + self::assertTrue($table->columns['user_id']->isPrimaryKey); + self::assertTrue($table->columns['role_id']->isPrimaryKey); + self::assertFalse($table->columns['data']->isPrimaryKey); + } + + public function testFixPrimaryKeyResetsOldPrimaryKey(): void + { + $table = $this->createTableSchema(); + $table->columns['id']->isPrimaryKey = true; + $table->primaryKey = ['id']; + + $table->fixPrimaryKey('name'); + + self::assertSame(['name'], $table->primaryKey); + self::assertFalse($table->columns['id']->isPrimaryKey); + self::assertTrue($table->columns['name']->isPrimaryKey); + } + + public function testFixPrimaryKeyThrowsOnNonExistentColumn(): void + { + $table = $this->createTableSchema(); + + $this->expectException(InvalidArgumentException::class); + $table->fixPrimaryKey('nonexistent'); + } + + public function testFixPrimaryKeyThrowsOnPartialComposite(): void + { + $table = $this->createTableSchema(); + + $this->expectException(InvalidArgumentException::class); + $table->fixPrimaryKey(['id', 'missing']); + } + + public function testFixPrimaryKeyWithEmptyArray(): void + { + $table = $this->createTableSchema(); + $table->columns['id']->isPrimaryKey = true; + $table->primaryKey = ['id']; + + $table->fixPrimaryKey([]); + + self::assertSame([], $table->primaryKey); + self::assertFalse($table->columns['id']->isPrimaryKey); + } +}