10declare(strict_types=1);
11require dirname(__DIR__).
'/tools/update.php';
12require dirname(__DIR__).
'/tools/deploy.php';
13require dirname(__DIR__).
'/vendor/autoload.php';
16if (!in_array(
$driver, [
'mysql',
'pgsql'],
true)) {
throw new InvalidArgumentException(
'Choose mysql or pgsql.'); }
23 if ($expected !== $actual) {
throw new RuntimeException($label.
' failed: '.var_export($actual,
true)); }
29 catch (Throwable
$error) {
updaterCheck(
true, str_contains(
$error->getMessage(), $message),
'Expected '.$message);
return; }
30 throw new RuntimeException(
'Expected failure: '.$message);
33$name =
'lgv_tz_update_test_'.bin2hex(random_bytes(8));
35 'port' => (int)(getenv(
'LGV_TZ_TEST_PORT') ?: (
$driver ===
'pgsql' ? 5432 : 3306)),
36 'database' =>
$name,
'user' => getenv(
'LGV_TZ_TEST_USER') ?: (
$driver ===
'pgsql' ? (getenv(
'USER') ?: get_current_user()) :
'root'),
37 'password' => getenv(
'LGV_TZ_TEST_PASSWORD') ?:
'',
'admin_database' => getenv(
'LGV_TZ_TEST_ADMIN_DATABASE') ?:
'postgres'];
40$work = sys_get_temp_dir().
'/lgv-tz-update-test-'.bin2hex(random_bytes(8));
47 'g_server_secret' =>
'existing-secret'] as $key => $value) {
$config .=
'$'.$key.
' = '.var_export($value,
true).
";\n"; }
50 $connection->exec(
'CREATE TABLE unrelated (value INTEGER)');
51 $connection->exec(
'INSERT INTO unrelated VALUES (42)');
53 $feature = [
'type' =>
'Feature',
'properties' => [
'tzid' =>
'Asia/Tokyo'],
54 'geometry' => [
'type' =>
'Polygon',
'coordinates' => [[[0, 0], [4, 0], [4, 4], [0, 4], [0, 0]]]]];
56 $listener::listener_action($feature);
57 $feature[
'properties'][
'tzid'] =
'America/New_York';
58 $json = json_encode([
'type' =>
'FeatureCollection',
'features' => [$feature]], JSON_THROW_ON_ERROR);
59 $zip =
new ZipArchive();
60 $zip->open(
$work.
'/fixture.zip', ZipArchive::CREATE);
61 $zip->addFromString(
'combined-with-oceans.json', $json);
63 $metadata = [
'tag_name' =>
'test-release',
'assets' => [[
'name' =>
'timezones-with-oceans.geojson.zip',
64 'browser_download_url' =>
'https://example.com/boundaries.zip',
'size' => filesize(
$work.
'/fixture.zip'),
65 'digest' =>
'sha256:'.hash_file(
'sha256',
$work.
'/fixture.zip')]]];
67 $download =
static function(
string $url,
string $destination) use (
$work, &$metadata, &
$calls):
void {
69 if (str_ends_with($url,
'/releases/latest')) { file_put_contents($destination, json_encode($metadata, JSON_THROW_ON_ERROR)); }
70 else { copy(
$work.
'/fixture.zip', $destination); }
74 $report = ob_get_clean();
75 updaterCheck(
true, str_contains($report,
'Latest boundary release: test-release'),
'Version report');
84 updaterCheck(1, (
int)
$connection->query(
'SELECT COUNT(*) FROM timezones')->fetchColumn(),
'Replace rather than append');
85 updaterCheck(42, (
int)
$connection->query(
'SELECT value FROM unrelated')->fetchColumn(),
'Preserve unrelated tables');
88 foreach ([
'boundaries.zip',
'boundaries.json',
'release.json',
'update.json'] as $file) {
95 $original = $metadata;
96 $metadata[
'assets'][0][
'digest'] =
'sha256:'.str_repeat(
'0', 64);
99 $metadata = $original;
100 $metadata[
'assets'] = [];
103 $metadata = $original;
104 $metadata[
'tag_name'] =
'';
107 $metadata = $original;
110 $zip->open(
$work.
'/fixture.zip', ZipArchive::OVERWRITE);
111 $zip->addFromString(
'combined-with-oceans.json', substr($json, 0, -2).
',');
114 $metadata[
'assets'][0][
'size'] = filesize(
$work.
'/fixture.zip');
115 $metadata[
'assets'][0][
'digest'] =
'sha256:'.hash_file(
'sha256',
$work.
'/fixture.zip');
119 $zip->open(
$work.
'/fixture.zip', ZipArchive::OVERWRITE);
120 $zip->addFromString(
'combined-with-oceans.json',
'{"type":"FeatureCollection","features":[]}');
123 $metadata[
'assets'][0][
'size'] = filesize(
$work.
'/fixture.zip');
124 $metadata[
'assets'][0][
'digest'] =
'sha256:'.hash_file(
'sha256',
$work.
'/fixture.zip');
128 $table =
'lgv_tz_update_'.bin2hex(random_bytes(8));
129 $connection->exec(
'CREATE TABLE '.$table.
' (id INTEGER)');
130 $old =
'lgv_tz_old_'.substr($table, -16);
131 $connection->exec(
'CREATE TABLE '.$old.
' (id INTEGER)');
132 file_put_contents(
$work.
'/update.json', json_encode([
'config' =>
$work.
'/config.php',
'table' => $table], JSON_THROW_ON_ERROR));
133 file_put_contents(
$work.
'/boundaries.json',
'partial download');
137 file_put_contents(
$work.
'/update.json', json_encode([
'config' =>
$work.
'/config.php',
'table' =>
'timezones'], JSON_THROW_ON_ERROR));
138 file_put_contents(
$work.
'/boundaries.zip',
'partial download');
140 updaterCheck(
false, is_file(
$work.
'/boundaries.zip'),
'Delete downloads before recovery refusal');
142 unlink(
$work.
'/update.json');
143 $schema =
$driver ===
'pgsql' ?
'current_schema()' :
'DATABASE()';
144 $tables =
$connection->query(
'SELECT table_name FROM information_schema.tables WHERE table_schema = '.$schema.
' ORDER BY table_name')->fetchAll(PDO::FETCH_COLUMN);
145 updaterCheck([
'timezones',
'unrelated'], $tables,
'No staging or backup tables remain');
148 $installed =
$work.
'/installed';
149 mkdir($installed.
'/tools', 0700,
true);
150 mkdir($installed.
'/app/vendor', 0700,
true);
151 mkdir(
$work.
'/downloads', 0700);
152 copy(
$work.
'/config.php', $installed.
'/config.php');
153 foreach ([
'update.php',
'Setup.php'] as $file) { copy(dirname(__DIR__).
'/tools/'.$file, $installed.
'/tools/'.$file); }
154 copy(dirname(__DIR__).
'/update.sh', $installed.
'/update.sh');
155 chmod($installed.
'/update.sh', 0700);
156 file_put_contents($installed.
'/app/vendor/autoload.php',
'<?php require '.var_export(dirname(__DIR__).
'/vendor/autoload.php',
true).
';');
160 $process = proc_open([$installed.
'/update.sh'], [0 => [
'file',
'/dev/null',
'r'], 1 => STDOUT, 2 => STDERR], $pipes,
null,
$environment);
161 if (
$process ===
false) {
throw new RuntimeException(
'Could not start installed updater.'); }
163 updaterCheck([], array_values(array_diff(scandir(
$work.
'/downloads'), [
'.',
'..'])),
'Shell removes all downloaded files');
164 require dirname(__DIR__).
'/src/TestLocations.php';
167 $lng = $case[
'params'][
'lng']; $lat = $case[
'params'][
'lat'];
168 while ($lng < -180) { $lng += 360; }
while ($lng > 180) { $lng -= 360; }
169 while ($lat < -90) { $lat += 180; }
while ($lat > 90) { $lat -= 180; }
173 updaterCheck(
$config, file_get_contents($installed.
'/config.php'),
'Full update preserves server configuration');
174 updaterCheck(42, (
int)
$connection->query(
'SELECT value FROM unrelated')->fetchColumn(),
'Full update preserves unrelated data');
176 echo
'Passed '.$checks.
' '.
$driver.
" updater checks.\n";
178 if (ob_get_level()) { ob_end_clean(); }
$test_locations_param_array
This file has a list of long/lat pairs, along with the expected results, when presented to the server...
if(!is_file($autoload)) $checks
if($driver==='pgsql') $admin
if(realpath($_SERVER['SCRIPT_FILENAME'] ?? '') !==__FILE__) if(PHP_SAPI !=='cli'||$argc !==3||!in_array( $argv[1],[ 'run', 'cleanup'], true)||!is_dir( $argv[2])) if(function_exists('pcntl_async_signals')) try
This is the worker entrypoint.
updaterFailure(callable $operation, string $message)
Expect an operation to fail with a diagnostic message.
updaterCheck($expected, $actual, string $label)
Check a result and count successful assertions.