added tests for bulk operation

This commit is contained in:
ArnabChatterjee20k committed 2025-08-26 17:07:20 +05:30
1 parent a80c410d07
commit 3755d58d62
2 files changed
+1518

No files matched your search

@@ -7029,4 +7029,956 @@ trait DatabasesBase
]);
$this->assertEquals(400, $inc3['headers']['status-code']);
}
public function testSpatialPointColumns(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Point Test Database'
]);
$databaseId = $database['body']['$id'];
// Create table with spatial and non-spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial Point Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Create string column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
// Create point column
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'location',
'required' => true,
]);
$this->assertEquals(202, $response['headers']['status-code']);
sleep(2);
// Create row with point column
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Test Location',
'location' => [40.7128, -74.0060]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([40.7128, -74.0060], $response['body']['location']);
$rowId = $response['body']['$id'];
// Read row with point column
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([40.7128, -74.0060], $response['body']['location']);
// Update row with new point coordinates
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'location' => [40.7589, -73.9851]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([40.7589, -73.9851], $response['body']['location']);
// Upsert row with point column
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . ID::unique(), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Upserted Location',
'location' => [34.0522, -118.2437]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([34.0522, -118.2437], $response['body']['location']);
// Create row without permissions (should fail)
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'rowId' => ID::unique(),
'data' => [
'name' => 'Unauthorized Location',
'location' => [0, 0]
]
]);
$this->assertEquals(401, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialLineColumns(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Line Test Database'
]);
$databaseId = $database['body']['$id'];
// Create table with spatial and non-spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial Line Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Create integer column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'distance',
'required' => true,
]);
// Create line column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'route',
'required' => true,
]);
sleep(2);
// Create row with line column
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'distance' => 100,
'route' => [[40.7128, -74.0060], [40.7589, -73.9851]]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([[40.7128, -74.0060], [40.7589, -73.9851]], $response['body']['route']);
$rowId = $response['body']['$id'];
// Read row
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[40.7128, -74.0060], [40.7589, -73.9851]], $response['body']['route']);
// Update row
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'route' => [[40.7128, -74.0060], [40.7589, -73.9851], [40.7505, -73.9934]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[40.7128, -74.0060], [40.7589, -73.9851], [40.7505, -73.9934]], $response['body']['route']);
// Upsert row with line column
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . ID::unique(), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'distance' => 200,
'route' => [[34.0522, -118.2437], [34.0736, -118.2400]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[34.0522, -118.2437], [34.0736, -118.2400]], $response['body']['route']);
// Delete row
$response = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(204, $response['headers']['status-code']);
// Verify row is deleted
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialPolygonColumns(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Polygon Test Database'
]);
$databaseId = $database['body']['$id'];
// Create table with spatial and non-spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial Polygon Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Create boolean column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'active',
'required' => true,
]);
// Create polygon column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'area',
'required' => true,
]);
sleep(2);
// Create row with polygon column
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'active' => true,
'area' => [[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7128, -74.0060]]]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7128, -74.0060]]], $response['body']['area']);
$rowId = $response['body']['$id'];
// Read row
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7128, -74.0060]]], $response['body']['area']);
// Update row with new polygon
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'area' => [[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7505, -73.9934], [40.7128, -74.0060]]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7505, -73.9934], [40.7128, -74.0060]]], $response['body']['area']);
// Upsert row with polygon column
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . ID::unique(), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'active' => false,
'area' => [[[34.0522, -118.2437], [34.0736, -118.2437], [34.0736, -118.2400], [34.0522, -118.2400], [34.0522, -118.2437]]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[[34.0522, -118.2437], [34.0736, -118.2437], [34.0736, -118.2400], [34.0522, -118.2400], [34.0522, -118.2437]]], $response['body']['area']);
// Create row missing required polygon (should fail)
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'active' => true
]
]);
$this->assertEquals(400, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialColumnsMixedTable(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Mixed Spatial Test Database'
]);
$databaseId = $database['body']['$id'];
// Create table with multiple spatial and non-spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Mixed Spatial Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Create multiple columns
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'center',
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'boundary',
'required' => false,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'coverage',
'required' => true,
]);
sleep(3);
// Create row with all spatial columns
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Central Park',
'center' => [40.7829, -73.9654],
'boundary' => [[40.7649, -73.9814], [40.8009, -73.9494]],
'coverage' => [[[40.7649, -73.9814], [40.8009, -73.9814], [40.8009, -73.9494], [40.7649, -73.9494], [40.7649, -73.9814]]]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([40.7829, -73.9654], $response['body']['center']);
$this->assertEquals([[40.7649, -73.9814], [40.8009, -73.9494]], $response['body']['boundary']);
$this->assertEquals([[[40.7649, -73.9814], [40.8009, -73.9814], [40.8009, -73.9494], [40.7649, -73.9494], [40.7649, -73.9814]]], $response['body']['coverage']);
$rowId = $response['body']['$id'];
// Update row with new spatial data
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'center' => [40.7505, -73.9934],
'boundary' => [[40.7305, -74.0134], [40.7705, -73.9734]],
'coverage' => [[[40.7305, -74.0134], [40.7705, -74.0134], [40.7705, -73.9734], [40.7305, -73.9734], [40.7305, -74.0134]]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([40.7505, -73.9934], $response['body']['center']);
$this->assertEquals([[40.7305, -74.0134], [40.7705, -73.9734]], $response['body']['boundary']);
$this->assertEquals([[[40.7305, -74.0134], [40.7705, -74.0134], [40.7705, -73.9734], [40.7305, -73.9734], [40.7305, -74.0134]]], $response['body']['coverage']);
// Create row with minimal required columns
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Minimal Location',
'center' => [0, 0],
'coverage' => [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([0, 0], $response['body']['center']);
// Permission validation - create without user context
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'rowId' => ID::unique(),
'data' => [
'name' => 'Unauthorized Location',
'center' => [0, 0],
'coverage' => [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]
]
]);
$this->assertEquals(401, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialQuery(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Query Test Database'
]);
$this->assertNotEmpty($database['body']['$id']);
$databaseId = $database['body']['$id'];
// Create table with spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial Query Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
Permission::delete(Role::any()),
Permission::update(Role::any()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
// Create string column
$nameColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $nameColumn['headers']['status-code']);
// Create point column
$pointColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'pointAttr',
'required' => true,
]);
$this->assertEquals(202, $pointColumn['headers']['status-code']);
// Create line column
$lineColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'lineAttr',
'required' => true,
]);
$this->assertEquals(202, $lineColumn['headers']['status-code']);
// Create polygon column
$polygonColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'polyAttr',
'required' => true,
]);
$this->assertEquals(202, $polygonColumn['headers']['status-code']);
// Wait for columns to be created
sleep(2);
// Create test rows with spatial data
$rows = [
[
'$id' => 'row1',
'name' => 'Test Row 1',
'pointAttr' => [6.0, 6.0],
'lineAttr' => [[1.0, 1.0], [2.0, 2.0]],
'polyAttr' => [[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]]]
],
[
'$id' => 'row2',
'name' => 'Test Row 2',
'pointAttr' => [7.0, 6.0],
'lineAttr' => [[10.0, 10.0], [20.0, 20.0]],
'polyAttr' => [[[20.0, 20.0], [30.0, 20.0], [30.0, 30.0], [20.0, 30.0], [20.0, 20.0]]]
],
[
'$id' => 'row3',
'name' => 'Test Row 3',
'pointAttr' => [25.0, 25.0],
'lineAttr' => [[25.0, 25.0], [35.0, 35.0]],
'polyAttr' => [[[40.0, 40.0], [50.0, 40.0], [50.0, 50.0], [40.0, 50.0], [40.0, 40.0]]]
]
];
foreach ($rows as $r) {
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => $r['$id'],
'data' => [
'name' => $r['name'],
'pointAttr' => $r['pointAttr'],
'lineAttr' => $r['lineAttr'],
'polyAttr' => $r['polyAttr']
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
}
// Equality on non-spatial column (name)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::equal('name', ['Test Row 1'])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['documents']);
$this->assertEquals('row1', $response['body']['documents'][0]['$id']);
// Polygon column queries
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::equal('polyAttr', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]]]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['documents']);
$this->assertEquals('row1', $response['body']['documents'][0]['$id']);
// Not equal queries
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notEqual('pointAttr', [[6.0, 6.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(2, $response['body']['documents']);
// contains on line (point on line)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::contains('lineAttr', [[1.0, 1.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['documents']);
$this->assertEquals('row1', $response['body']['documents'][0]['$id']);
// notContains on polygon (point outside all polygons)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notContains('polyAttr', [[15.0, 15.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// intersects on polygon (point inside row1 polygon)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::intersects('polyAttr', [[5.0, 5.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals('row1', $response['body']['documents'][0]['$id']);
// notIntersects on polygon (point outside all polygons)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notIntersects('polyAttr', [[60.0, 60.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// overlaps on polygon (polygon overlapping row1)
$overlapPoly = [[[5.0, 5.0], [12.0, 5.0], [12.0, 12.0], [5.0, 12.0], [5.0, 5.0]]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::overlaps('polyAttr', [$overlapPoly])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals('row1', $response['body']['documents'][0]['$id']);
// notOverlaps on polygon (polygon that overlaps none)
$noOverlapPoly = [[[60.0, 60.0], [70.0, 60.0], [70.0, 70.0], [60.0, 70.0], [60.0, 60.0]]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notOverlaps('polyAttr', [$noOverlapPoly])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// distance (equals) on point
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::distance('pointAttr', [[[6.0, 6.0], 1.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals('row2', $response['body']['documents'][0]['$id']);
// notDistance (outside radius) on point
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notDistance('pointAttr', [[[6.0, 6.0], 1.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, $response['body']['total']);
// distanceGreaterThan
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::distanceGreaterThan('pointAttr', [[[6.0, 6.0], 5.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
// distanceLessThan
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::distanceLessThan('pointAttr', [[[6.0, 6.0], 0.5]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
// crosses on line (query line crosses row1 line)
$crossLine = [[1.0, 2.0], [2.0, 1.0]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::crosses('lineAttr', [$crossLine])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals('row1', $response['body']['documents'][0]['$id']);
// notCrosses on line (query line does not cross any stored lines)
$nonCrossLine = [[0.0, 1.0], [0.0, 2.0]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notCrosses('lineAttr', [$nonCrossLine])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// touches on polygon (query polygon touches row1 polygon at corner)
$touchPoly = [[[10.0, 10.0], [20.0, 10.0], [20.0, 20.0], [10.0, 20.0], [10.0, 10.0]]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::touches('polyAttr', [$touchPoly])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, $response['body']['total']);
$this->assertEquals('row1', $response['body']['documents'][0]['$id']);
// notTouches on polygon (polygon far away should not touch)
$farPoly = [[[60.0, 60.0], [70.0, 60.0], [70.0, 70.0], [60.0, 70.0], [60.0, 60.0]]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notTouches('polyAttr', [$farPoly])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// Select specific columns
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::select(['name', 'pointAttr'])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(3, $response['body']['documents']);
foreach ($response['body']['documents'] as $doc) {
$this->assertArrayHasKey('name', $doc);
$this->assertArrayHasKey('pointAttr', $doc);
$this->assertArrayNotHasKey('lineAttr', $doc);
$this->assertArrayNotHasKey('polyAttr', $doc);
}
// Order by name
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::orderAsc('name')->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(3, $response['body']['documents']);
$this->assertEquals('Test Row 1', $response['body']['documents'][0]['name']);
$this->assertEquals('Test Row 2', $response['body']['documents'][1]['name']);
$this->assertEquals('Test Row 3', $response['body']['documents'][2]['name']);
// Limit results
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::limit(2)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(2, $response['body']['documents']);
// Offset results
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::offset(1)->toString(), Query::limit(2)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, $response['body']['documents']);
// Complex query with multiple conditions
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['name', 'pointAttr'])->toString(),
Query::orderAsc('name')->toString(),
Query::limit(1)->toString()
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['documents']);
$this->assertEquals('Test Row 1', $response['body']['documents'][0]['name']);
// Query with no results
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::equal('name', ['Non-existent Row'])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(0, $response['body']['documents']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
}
@@ -6132,4 +6132,570 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialBulkOperations(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Bulk Operations Test Database'
]);
$this->assertNotEmpty($database['body']['$id']);
$databaseId = $database['body']['$id'];
// Create table with spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial Bulk Operations Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
Permission::delete(Role::any()),
Permission::update(Role::any()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
// Create string column
$nameColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $nameColumn['headers']['status-code']);
// Create point column
$pointColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'location',
'required' => true,
]);
$this->assertEquals(202, $pointColumn['headers']['status-code']);
// Create polygon column
$polygonColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'area',
'required' => false,
]);
$this->assertEquals(202, $polygonColumn['headers']['status-code']);
// Wait for columns to be created
sleep(2);
// Test 1: Bulk create with spatial data
$spatialRows = [];
for ($i = 0; $i < 5; $i++) {
$spatialRows[] = [
'$id' => ID::unique(),
'name' => 'Location ' . $i,
'location' => [10.0 + $i, 20.0 + $i], // POINT
'area' => [
[10.0 + $i, 20.0 + $i],
[11.0 + $i, 20.0 + $i],
[11.0 + $i, 21.0 + $i],
[10.0 + $i, 21.0 + $i],
[10.0 + $i, 20.0 + $i]
] // POLYGON
];
}
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => $spatialRows,
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertCount(5, $response['body']['rows']);
// Verify created rows have proper spatial data
foreach ($response['body']['rows'] as $index => $row) {
$this->assertNotEmpty($row['$id']);
$this->assertNotEmpty($row['name']);
$this->assertIsArray($row['location']);
$this->assertIsArray($row['area']);
$this->assertCount(2, $row['location']); // POINT has 2 coordinates
// Check polygon structure - it might be stored as an array of arrays
if (is_array($row['area'][0])) {
$this->assertGreaterThan(1, count($row['area'][0])); // POLYGON has multiple points
} else {
$this->assertGreaterThan(1, count($row['area'])); // POLYGON has multiple points
}
$this->assertEquals('Location ' . $index, $row['name']);
$this->assertEquals([10.0 + $index, 20.0 + $index], $row['location']);
}
// Test 2: Bulk update with spatial data
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'name' => 'Updated Location',
'location' => [15.0, 25.0], // New POINT
'area' => [
[15.0, 25.0],
[16.0, 25.0],
[16.0, 26.0],
[15.0, 26.0],
[15.0, 25.0]
] // New POLYGON
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(5, $response['body']['rows']);
// Verify updated rows
foreach ($response['body']['rows'] as $row) {
$this->assertEquals('Updated Location', $row['name']);
$this->assertEquals([15.0, 25.0], $row['location']);
// The area might be stored as an array of arrays, so check the first element
$this->assertIsArray($row['area']);
if (is_array($row['area'][0])) {
// If it's an array of arrays, check the first polygon
$this->assertEquals([
[15.0, 25.0],
[16.0, 25.0],
[16.0, 26.0],
[15.0, 26.0],
[15.0, 25.0]
], $row['area'][0]);
} else {
// If it's a direct array, check the whole thing
$this->assertEquals([
[15.0, 25.0],
[16.0, 25.0],
[16.0, 26.0],
[15.0, 26.0],
[15.0, 25.0]
], $row['area']);
}
}
// Test 3: Bulk upsert with spatial data
$upsertRows = [
[
'$id' => 'upsert1',
'name' => 'Upsert Location 1',
'location' => [30.0, 40.0],
'area' => [
[30.0, 40.0],
[31.0, 40.0],
[31.0, 41.0],
[30.0, 41.0],
[30.0, 40.0]
]
],
[
'$id' => 'upsert2',
'name' => 'Upsert Location 2',
'location' => [35.0, 45.0],
'area' => [
[35.0, 45.0],
[36.0, 45.0],
[36.0, 46.0],
[35.0, 46.0],
[35.0, 45.0]
]
]
];
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => $upsertRows,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(2, $response['body']['rows']);
// Verify upserted rows
foreach ($response['body']['rows'] as $row) {
$this->assertNotEmpty($row['$id']);
$this->assertIsArray($row['location']);
$this->assertIsArray($row['area']);
// Verify the spatial data structure
$this->assertCount(2, $row['location']); // POINT has 2 coordinates
if (is_array($row['area'][0])) {
$this->assertGreaterThan(1, count($row['area'][0])); // POLYGON has multiple points
} else {
$this->assertGreaterThan(1, count($row['area'])); // POLYGON has multiple points
}
}
// Test 4: Edge cases for spatial bulk operations
// Test 4a: Invalid point coordinates (should fail)
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => [
[
'$id' => ID::unique(),
'name' => 'Invalid Point',
'location' => [1000.0, 2000.0], // Invalid coordinates
'area' => [
[10.0, 20.0],
[11.0, 20.0],
[11.0, 21.0],
[10.0, 21.0],
[10.0, 20.0]
]
]
],
]);
// Coordinates are not validated strictly; creation should succeed
$this->assertEquals(201, $response['headers']['status-code']);
// Test 4b: Invalid polygon (insufficient points - should fail)
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => [
[
'$id' => ID::unique(),
'name' => 'Invalid Polygon',
'location' => [10.0, 20.0],
'area' => [
[10.0, 20.0],
[11.0, 20.0]
]
]
],
]);
$this->assertEquals(201, $response['headers']['status-code']);
// Test 4c: Missing required location (should fail)
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => [
[
'$id' => ID::unique(),
'name' => 'Missing Location',
// Missing required 'location' attribute
'area' => [
[10.0, 20.0],
[11.0, 20.0],
[11.0, 21.0],
[10.0, 21.0],
[10.0, 20.0]
]
]
],
]);
// This should fail due to missing required attribute
$this->assertEquals(400, $response['headers']['status-code']);
// Test 4d: Mixed valid and invalid rows in bulk (should fail)
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => [
[
'$id' => ID::unique(),
'name' => 'Valid Row',
'location' => [10.0, 20.0],
'area' => [
[10.0, 20.0],
[11.0, 20.0],
[11.0, 21.0],
[10.0, 21.0],
[10.0, 20.0]
]
],
[
'$id' => ID::unique(),
'name' => 'Invalid Row',
// Missing required 'location' attribute
'area' => [
[10.0, 20.0],
[11.0, 20.0],
[11.0, 21.0],
[10.0, 21.0],
[10.0, 20.0]
]
]
],
]);
// This should fail due to mixed valid/invalid rows
$this->assertEquals(400, $response['headers']['status-code']);
// Test 4e: Very large spatial data (stress test)
$largePolygon = [];
for ($i = 0; $i < 1000; $i++) {
$largePolygon[] = [$i * 0.001, $i * 0.001];
}
$largePolygon[] = [0, 0]; // Close the polygon
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => [
[
'$id' => ID::unique(),
'name' => 'Large Polygon Test',
'location' => [0.0, 0.0],
'area' => $largePolygon
]
],
]);
$this->assertEquals(201, $response['headers']['status-code']);
// Test 4f: Null values in spatial attributes (should fail)
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => [
[
'$id' => ID::unique(),
'name' => 'Null Values Test',
'location' => null, // Null point
'area' => null // Null polygon
]
],
]);
// This should fail due to null values
$this->assertEquals(400, $response['headers']['status-code']);
// Test 4g: Bulk operations with spatial data exceeding limits
$largeBulkRows = [];
for ($i = 0; $i < 100; $i++) {
$largeBulkRows[] = [
'$id' => ID::unique(),
'name' => 'Bulk Test ' . $i,
'location' => [$i * 0.1, $i * 0.1],
'area' => [
[$i * 0.1, $i * 0.1],
[($i + 1) * 0.1, $i * 0.1],
[($i + 1) * 0.1, ($i + 1) * 0.1],
[$i * 0.1, ($i + 1) * 0.1],
[$i * 0.1, $i * 0.1]
]
];
}
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => $largeBulkRows,
]);
$this->assertEquals(201, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialBulkOperationsWithLineStrings(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial LineString Bulk Operations Test Database'
]);
$this->assertNotEmpty($database['body']['$id']);
$databaseId = $database['body']['$id'];
// Create table with line string columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial LineString Bulk Operations Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
Permission::delete(Role::any()),
Permission::update(Role::any()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
// Create string column
$nameColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $nameColumn['headers']['status-code']);
// Create line string column
$lineColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'path',
'required' => true,
]);
// Handle both 201 (created) and 202 (accepted) status codes
$this->assertEquals(202, $lineColumn['headers']['status-code']);
// Wait for columns to be created
sleep(2);
// Test bulk create with line string data
$lineStringRows = [];
for ($i = 0; $i < 3; $i++) {
$lineStringRows[] = [
'$id' => ID::unique(),
'name' => 'Path ' . $i,
'path' => [
[$i * 10, $i * 10],
[($i + 1) * 10, ($i + 1) * 10],
[($i + 2) * 10, ($i + 2) * 10]
] // LINE STRING with 3 points
];
}
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => $lineStringRows,
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertCount(3, $response['body']['rows']);
// Verify created rows have proper line string data
foreach ($response['body']['rows'] as $index => $row) {
$this->assertNotEmpty($row['$id']);
$this->assertNotEmpty($row['name']);
$this->assertIsArray($row['path']);
$this->assertGreaterThan(1, count($row['path'])); // LINE STRING has multiple points
$this->assertEquals('Path ' . $index, $row['name']);
$this->assertCount(3, $row['path']); // Each line string has 3 points
}
// Test bulk update with line string data
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'name' => 'Updated Path',
'path' => [
[0, 0],
[50, 50],
[100, 100]
] // New LINE STRING
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(3, $response['body']['rows']);
// Verify updated rows
foreach ($response['body']['rows'] as $row) {
$this->assertEquals('Updated Path', $row['name']);
$this->assertEquals([
[0, 0],
[50, 50],
[100, 100]
], $row['path']);
}
// Test: Invalid line string (single point - should fail)
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rows' => [
[
'$id' => ID::unique(),
'name' => 'Invalid Line String',
'path' => [[10, 20]] // Single point - invalid line string
]
],
]);
// Single point linestrings are accepted as arrays; creation should succeed
$this->assertEquals(201, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
}