feat: add full Zonemaster stack with Docker and Spanish UI

- Clone all 5 Zonemaster component repos (LDNS, Engine, CLI, Backend, GUI)
- Dockerfile.backend: 8-stage multi-stage build LDNS→Engine→CLI→Backend
- Dockerfile.gui: Astro static build served via nginx
- docker-compose.yml: backend (internal) + frontend (port 5353)
- nginx.conf: root redirects to /es/, /api/ proxied to backend
- zonemaster-gui/config.ts: defaultLanguage set to 'es' (Spanish)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 08:19:24 +02:00
commit 8d4eaa1489
1567 changed files with 204155 additions and 0 deletions

View File

@@ -0,0 +1,891 @@
use strict;
use warnings;
use utf8;
use Test::More tests => 2;
use Test::NoWarnings;
use Test::Differences;
use Test::Exception;
use Log::Any::Test; # Must come before use Log::Any
use File::Basename qw( dirname );
use File::Slurp qw( read_file );
use File::Spec::Functions qw( catfile );
use Log::Any qw( $log );
subtest 'Everything but NoWarnings' => sub {
use_ok( 'Zonemaster::Backend::Config' );
subtest 'Set values' => sub {
my $text = q{
[DB]
engine = sqlite
polling_interval = 1.5
[MYSQL]
host = mysql-host
port = 3456
user = mysql_user
password = mysql_password
database = mysql_database
[POSTGRESQL]
host = postgresql-host
port = 6543
user = postgresql_user
password = postgresql_password
database = postgresql_database
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[LANGUAGE]
locale = sv_FI
[PUBLIC PROFILES]
default = /path/to/default.profile
two = /path/to/two.profile
[PRIVATE PROFILES]
three = /path/to/three.profile
four = /path/to/four.profile
[ZONEMASTER]
max_zonemaster_execution_time = 1200
number_of_processes_for_frontend_testing = 30
number_of_processes_for_batch_testing = 40
lock_on_queue = 1
age_reuse_previous_test = 800
};
my $config = Zonemaster::Backend::Config->parse( $text );
isa_ok $config, 'Zonemaster::Backend::Config', 'parse() return value';
is $config->DB_engine, 'SQLite', 'set: DB.engine';
is $config->DB_polling_interval, 1.5, 'set: DB.polling_interval';
is $config->MYSQL_host, 'mysql-host', 'set: MYSQL.host';
is $config->MYSQL_port, 3456, 'set: MYSQL.port';
is $config->MYSQL_user, 'mysql_user', 'set: MYSQL.user';
is $config->MYSQL_password, 'mysql_password', 'set: MYSQL.password';
is $config->MYSQL_database, 'mysql_database', 'set: MYSQL.database';
is $config->POSTGRESQL_host, 'postgresql-host', 'set: POSTGRESQL.host';
is $config->POSTGRESQL_port, 6543, 'set: POSTGRESQL.port';
is $config->POSTGRESQL_user, 'postgresql_user', 'set: POSTGRESQL.user';
is $config->POSTGRESQL_password, 'postgresql_password', 'set: POSTGRESQL.password';
is $config->POSTGRESQL_database, 'postgresql_database', 'set: POSTGRESQL.database';
is $config->SQLITE_database_file, '/var/db/zonemaster.sqlite', 'set: SQLITE.database_file';
eq_or_diff { $config->LANGUAGE_locale }, { sv => 'sv_FI' }, 'set: LANGUAGE.locale';
eq_or_diff { $config->PUBLIC_PROFILES }, { #
default => '/path/to/default.profile',
two => '/path/to/two.profile'
},
'set: PUBLIC PROFILES';
eq_or_diff { $config->PRIVATE_PROFILES }, { #
three => '/path/to/three.profile',
four => '/path/to/four.profile'
},
'set: PRIVATE PROFILES';
is $config->ZONEMASTER_max_zonemaster_execution_time, 1200, 'set: ZONEMASTER.max_zonemaster_execution_time';
is $config->ZONEMASTER_number_of_processes_for_frontend_testing, 30, 'set: ZONEMASTER.number_of_processes_for_frontend_testing';
is $config->ZONEMASTER_number_of_processes_for_batch_testing, 40, 'set: ZONEMASTER.number_of_processes_for_batch_testing';
is $config->ZONEMASTER_lock_on_queue, 1, 'set: ZONEMASTER.lock_on_queue';
is $config->ZONEMASTER_age_reuse_previous_test, 800, 'set: ZONEMASTER.age_reuse_previous_test';
};
subtest 'Default values' => sub {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
};
my $config = Zonemaster::Backend::Config->parse( $text );
cmp_ok abs( $config->DB_polling_interval - 0.5 ), '<', 0.000001, 'default: DB.polling_interval';
is $config->MYSQL_port, 3306, 'default: MYSQL.port';
is $config->POSTGRESQL_port, 5432, 'default: POSTGRESQL.port';
eq_or_diff { $config->LANGUAGE_locale }, { en => 'en_US' }, 'default: LANGUAGE.locale';
eq_or_diff { $config->PUBLIC_PROFILES }, { default => undef }, 'default: PUBLIC_PROFILES';
eq_or_diff { $config->PRIVATE_PROFILES }, {}, 'default: PRIVATE_PROFILES';
is $config->ZONEMASTER_max_zonemaster_execution_time, 600, 'default: ZONEMASTER.max_zonemaster_execution_time';
is $config->ZONEMASTER_number_of_processes_for_frontend_testing, 20, 'default: ZONEMASTER.number_of_processes_for_frontend_testing';
is $config->ZONEMASTER_number_of_processes_for_batch_testing, 20, 'default: ZONEMASTER.number_of_processes_for_batch_testing';
is $config->ZONEMASTER_lock_on_queue, 0, 'default: ZONEMASTER.lock_on_queue';
is $config->ZONEMASTER_age_reuse_previous_test, 600, 'default: ZONEMASTER.age_reuse_previous_test';
is $config->RPCAPI_enable_add_api_user, 0, 'default: RPCAPI.enable_add_api_user';
is $config->RPCAPI_enable_add_batch_job, 1, 'default: RPCAPI.enable_add_batch_job';
};
SKIP: {
skip "no more deprecated values", 1;
subtest 'Deprecated values and fallbacks that are unconditional' => sub {
$log->clear();
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
};
my $config = Zonemaster::Backend::Config->parse( $text );
};
}
subtest 'Warnings' => sub {
$log->clear();
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = localhost
port = 3333
user = mysql_user
password = mysql_password
database = mysql_database
};
my $config = Zonemaster::Backend::Config->parse( $text );
$log->contains_ok( qr/MYSQL\.port.*MYSQL\.host/, 'warning: MYSQL.host is "localhost" and MYSQL.port defined' );
is $config->MYSQL_host, 'localhost', 'set: MYSQL.host';
is $config->MYSQL_port, 3333, 'set: MYSQL.port';
};
throws_ok {
$log->clear();
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[LANGUAGE]
locale =
};
Zonemaster::Backend::Config->parse( $text );
}
qr/Use of empty LANGUAGE.locale property is not permitted/, 'die: Invalid empty locale tag';
throws_ok {
my $text = '{"this":"is","not":"a","valid":"ini","file":"!"}';
Zonemaster::Backend::Config->parse( $text );
}
qr/Failed to parse config/, 'die: Invalid INI format';
throws_ok {
my $text = q{
[DB]
engine = Excel
[SQLITE]
databse_file = /var/db/zonemaster.sqlite
[ZNMEOTAESR]
lock_on_queue = 1
};
Zonemaster::Backend::Config->parse( $text );
}
qr{section.*ZNMEOTAESR}, 'die: Invalid section name';
throws_ok {
my $text = q{
[DB]
engine = SQLite
pnlilog_iatnvrel = 0.5
[SQLITE]
database_file = /var/db/zonemaster.sqlite
};
Zonemaster::Backend::Config->parse( $text );
}
qr{property.*pnlilog_iatnvrel}, 'die: Invalid property name';
throws_ok {
my $text = q{
[DB]
engine = Excel
};
Zonemaster::Backend::Config->parse( $text );
}
qr/DB\.engine.*Excel/, 'die: Invalid DB.engine value';
throws_ok {
my $text = q{
[DB]
engine = SQLite
polling_interval = hourly
[SQLITE]
databse_file = /var/db/zonemaster.sqlite
};
Zonemaster::Backend::Config->parse( $text );
}
qr{DB\.polling_interval.*hourly}, 'die: Invalid DB.polling_interval value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = 192.0.2.1:3306
user = zonemaster_user
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr{MYSQL\.host.*192.0.2.1:3306}, 'die: Invalid MYSQL.host value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = zonemaster-host
user = Robert'); DROP TABLE Students;--
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr{MYSQL\.user.*Robert'\); DROP TABLE Students;--}, 'die: Invalid MYSQL.user value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = zonemaster-host
user = zonemaster
password = (╯°□°)╯︵ ┻━┻
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr{MYSQL\.password.*\(╯°□°\)╯︵ ┻━┻}, 'die: Invalid MYSQL.password value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = zonemaster-host
user = zonemaster_user
password = zonemaster_password
database = |)/-\'|'/-\|3/-\$[-
};
Zonemaster::Backend::Config->parse( $text );
}
qr{MYSQL\.database.*|\)/-\'|'/-\\|3/-\\$[-}, 'die: Invalid MYSQL.database value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = 192.0.2.1:5432
user = zonemaster_user
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr{POSTGRESQL\.host.*192.0.2.1:5432}, 'die: Invalid POSTGRESQL.host value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = zonemaster-host
user = Robert'); DROP TABLE Students;--
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr{POSTGRESQL\.user.*Robert'\); DROP TABLE Students;--}, 'die: Invalid POSTGRESQL.user value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = zonemaster-host
user = zonemaster
password = (╯°□°)╯︵ ┻━┻
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr{POSTGRESQL\.password.*\(╯°□°\)╯︵ ┻━┻}, 'die: Invalid POSTGRESQL.password value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = zonemaster-host
user = zonemaster_user
password = zonemaster_password
database = |)/-\'|'/-\|3/-\$[-
};
Zonemaster::Backend::Config->parse( $text );
}
qr{POSTGRESQL\.database.*|\)/-\'|'/-\\|3/-\\$[-}, 'die: Invalid POSTGRESQL.database value';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = ./relative/path/to/zonemaster.sqlite
};
Zonemaster::Backend::Config->parse( $text );
}
qr{SQLITE\.database_file.*\./relative/path/to/zonemaster.sqlite}, 'die: Invalid SQLITE.database_file value';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[ZONEMASTER]
max_zonemaster_execution_time = 0
};
Zonemaster::Backend::Config->parse( $text );
}
qr{ZONEMASTER\.max_zonemaster_execution_time.*0}, 'die: Invalid ZONEMASTER.max_zonemaster_execution_time value';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[ZONEMASTER]
lock_on_queue = -1
};
Zonemaster::Backend::Config->parse( $text );
}
qr{ZONEMASTER\.lock_on_queue.*-1}, 'die: Invalid ZONEMASTER.lock_on_queue value';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[ZONEMASTER]
number_of_processes_for_frontend_testing = 0
};
Zonemaster::Backend::Config->parse( $text );
}
qr{ZONEMASTER\.number_of_processes_for_frontend_testing.*0}, 'die: Invalid ZONEMASTER.number_of_processes_for_frontend_testing value';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[ZONEMASTER]
number_of_processes_for_batch_testing = 100000
};
Zonemaster::Backend::Config->parse( $text );
}
qr{ZONEMASTER\.number_of_processes_for_batch_testing.*100000}, 'die: Invalid ZONEMASTER.number_of_processes_for_batch_testing value';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[ZONEMASTER]
age_reuse_previous_test = 0
};
Zonemaster::Backend::Config->parse( $text );
}
qr{ZONEMASTER\.age_reuse_previous_test.*0}, 'die: Invalid ZONEMASTER.age_reuse_previous_test value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
user = zonemaster_user
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/MYSQL\.host/, 'die: Missing MYSQL.host value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = zonemaster-host
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/MYSQL\.user/, 'die: Missing MYSQL.user value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = zonemaster-host
user = zonemaster_user
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/MYSQL\.password/, 'die: Missing MYSQL.password value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = zonemaster-host
user = zonemaster_user
password = zonemaster_password
};
Zonemaster::Backend::Config->parse( $text );
}
qr/MYSQL\.database/, 'die: Missing MYSQL.database value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
user = zonemaster_user
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/POSTGRESQL\.host/, 'die: Missing POSTGRESQL.host value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = zonemaster-host
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/POSTGRESQL\.user/, 'die: Missing POSTGRESQL.user value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = zonemaster-host
user = zonemaster_user
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/POSTGRESQL\.password/, 'die: Missing POSTGRESQL.password value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = zonemaster-host
user = zonemaster_user
password = zonemaster_password
};
Zonemaster::Backend::Config->parse( $text );
}
qr/POSTGRESQL\.database/, 'die: Missing POSTGRESQL.database value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
user = zonemaster_user
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/MYSQL\.host/, 'die: Missing MYSQL.host value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = zonemaster-host
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/MYSQL\.user/, 'die: Missing MYSQL.user value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = zonemaster-host
user = zonemaster_user
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/MYSQL\.password/, 'die: Missing MYSQL.password value';
throws_ok {
my $text = q{
[DB]
engine = MySQL
[MYSQL]
host = zonemaster-host
user = zonemaster_user
password = zonemaster_password
};
Zonemaster::Backend::Config->parse( $text );
}
qr/MYSQL\.database/, 'die: Missing MYSQL.database value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
user = zonemaster_user
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/POSTGRESQL\.host/, 'die: Missing POSTGRESQL.host value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = zonemaster-host
password = zonemaster_password
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/POSTGRESQL\.user/, 'die: Missing POSTGRESQL.user value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = zonemaster-host
user = zonemaster_user
database = zonemaster_database
};
Zonemaster::Backend::Config->parse( $text );
}
qr/POSTGRESQL\.password/, 'die: Missing POSTGRESQL.password value';
throws_ok {
my $text = q{
[DB]
engine = PostgreSQL
[POSTGRESQL]
host = zonemaster-host
user = zonemaster_user
password = zonemaster_password
};
Zonemaster::Backend::Config->parse( $text );
}
qr/POSTGRESQL\.database/, 'die: Missing POSTGRESQL.database value';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[LANGUAGE]
locale = English
};
Zonemaster::Backend::Config->parse( $text );
}
qr/LANGUAGE\.locale.*English/, 'die: Invalid locale_tag in LANGUAGE.locale';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[LANGUAGE]
locale = en_GB en_US
};
Zonemaster::Backend::Config->parse( $text );
}
qr/LANGUAGE\.locale.*en/, 'die: Repeated language code in LANGUAGE.locale';
lives_and {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[PUBLIC PROFILES]
DEFAULT = /path/to/my.profile
[PRIVATE PROFILES]
SECRET = /path/to/my.profile
};
my $config = Zonemaster::Backend::Config->parse( $text );
eq_or_diff { $config->PUBLIC_PROFILES }, { default => '/path/to/my.profile' }, 'normalize profile names under PUBLIC PROFILES';
eq_or_diff { $config->PRIVATE_PROFILES }, { secret => '/path/to/my.profile' }, 'normalize profile names under PRIVATE PROFILES';
};
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[PUBLIC PROFILES]
-invalid-name- = /path/to/my.profile
};
Zonemaster::Backend::Config->parse( $text );
}
qr/PUBLIC PROFILES.*-invalid-name-/, 'die: Invalid profile name in PUBLIC PROFILES';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[PRIVATE PROFILES]
-invalid-name- = /path/to/my.profile
};
Zonemaster::Backend::Config->parse( $text );
}
qr/PRIVATE PROFILES.*-invalid-name-/, 'die: Invalid profile name in PRIVATE PROFILES';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[PUBLIC PROFILES]
valid-name = relative/path/to/my.profile
};
Zonemaster::Backend::Config->parse( $text );
}
qr/absolute.*valid-name/, 'die: Invalid absolute path in PUBLIC PROFILES';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[PRIVATE PROFILES]
valid-name = relative/path/to/my.profile
};
Zonemaster::Backend::Config->parse( $text );
}
qr/absolute.*valid-name/, 'die: Invalid absolute path in PRIVATE PROFILES';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[PUBLIC PROFILES]
valid-name = /path/to/my.profile
valid-name = /path/to/my.profile
};
Zonemaster::Backend::Config->parse( $text );
}
qr/unique.*valid-name/, 'die: Repeated profile name in PUBLIC PROFILES section';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[PRIVATE PROFILES]
valid-name = /path/to/my.profile
valid-name = /path/to/my.profile
};
Zonemaster::Backend::Config->parse( $text );
}
qr/unique.*valid-name/, 'die: Repeated profile name in PRIVATE PROFILES section';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[PUBLIC PROFILES]
pub-and-priv = /path/to/my.profile
[PRIVATE PROFILES]
pub-and-priv = /path/to/my.profile
};
Zonemaster::Backend::Config->parse( $text );
}
qr/unique.*pub-and-priv/, 'die: Repeated profile name across sections';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[PRIVATE PROFILES]
default = /path/to/my.profile
};
Zonemaster::Backend::Config->parse( $text );
}
qr/PRIVATE PROFILES.*default/, 'die: Default profile in PRIVATE PROFILES';
subtest 'RPCAPI experimental aliases' => sub {
subtest 'default values' => sub {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
};
my $config = Zonemaster::Backend::Config->parse( $text );
is $config->RPCAPI_enable_add_api_user, 0, 'default: RPCAPI.enable_add_api_user';
is $config->RPCAPI_enable_add_batch_job, 1, 'default: RPCAPI.enable_add_batch_job';
is $config->RPCAPI_enable_user_create, 0, 'default: RPCAPI.enable_user_create';
is $config->RPCAPI_enable_batch_create, 1, 'default: RPCAPI.enable_batch_create';
};
subtest 'specifying stable and experimental parameters is forbidden' => sub {
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[RPCAPI]
enable_user_create = no
enable_add_api_user = yes
};
Zonemaster::Backend::Config->parse( $text );
}
qr/Error:.+RPCAPI\.enable_add_api_user.+RPCAPI\.enable_user_create/, 'die: RPCAPI stable and experimental alias (add_api_user/user_create)';
throws_ok {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[RPCAPI]
enable_add_batch_job = no
enable_batch_create = no
};
Zonemaster::Backend::Config->parse( $text );
}
qr/Error:.+RPCAPI\.enable_add_batch_job.+RPCAPI\.enable_batch_create/, 'die: RPCAPI stable and experimental alias (batch_job/batch_create)';
};
subtest 'setting alias' => sub {
my $text = q{
[DB]
engine = SQLite
[SQLITE]
database_file = /var/db/zonemaster.sqlite
[RPCAPI]
enable_user_create = no
enable_batch_create = no
};
my $config = Zonemaster::Backend::Config->parse( $text );
is $config->RPCAPI_enable_user_create, 0, 'set: RPCAPI.enable_user_create';
is $config->RPCAPI_enable_batch_create, 0, 'set: RPCAPI.enable_batch_create';
is $config->RPCAPI_enable_add_api_user, 0, 'aliased: RPCAPI.enable_add_api_user';
is $config->RPCAPI_enable_add_batch_job, 0, 'aliased: RPCAPI.enable_add_batch_job';
};
};
{
my $path = catfile( dirname( $0 ), '..', 'share', 'backend_config.ini' );
my $text = read_file( $path );
lives_ok {
Zonemaster::Backend::Config->parse( $text );
} 'default config is valid';
}
};