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:
242
zonemaster-backend/t/rpc_validation.t
Normal file
242
zonemaster-backend/t/rpc_validation.t
Normal file
@@ -0,0 +1,242 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use 5.14.2;
|
||||
use utf8;
|
||||
|
||||
use Test::More tests => 30;
|
||||
use Test::NoWarnings;
|
||||
|
||||
use Cwd;
|
||||
use File::Temp qw[tempdir];
|
||||
use Zonemaster::Backend::Config;
|
||||
use Zonemaster::Backend::RPCAPI;
|
||||
use JSON::Validator::Joi "joi";
|
||||
use JSON::PP;
|
||||
|
||||
###
|
||||
### Setup
|
||||
###
|
||||
|
||||
my $tempdir = tempdir( CLEANUP => 1 );
|
||||
my $cwd = cwd();
|
||||
|
||||
my $config = Zonemaster::Backend::Config->parse( <<EOF );
|
||||
[DB]
|
||||
engine = SQLite
|
||||
|
||||
[SQLITE]
|
||||
database_file = $tempdir/zonemaster.sqlite
|
||||
|
||||
[PUBLIC PROFILES]
|
||||
test = $cwd/t/test_profile.json
|
||||
EOF
|
||||
|
||||
my $rpcapi = Zonemaster::Backend::RPCAPI->new(
|
||||
{
|
||||
dbtype => $config->DB_engine,
|
||||
config => $config,
|
||||
}
|
||||
);
|
||||
|
||||
###
|
||||
### JSONRPC request object construction helper
|
||||
###
|
||||
|
||||
sub jsonrpc
|
||||
{
|
||||
my ($method, $params, $force_undef) = @_;
|
||||
my $object = {
|
||||
jsonrpc => '2.0',
|
||||
id => 'testing',
|
||||
method => $method
|
||||
};
|
||||
if (defined $params or $force_undef) {
|
||||
$object->{params} = $params;
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
###
|
||||
### JSONRPC error response construction helpers
|
||||
###
|
||||
|
||||
sub jsonrpc_error
|
||||
{
|
||||
my ($message, $code, $data, $id) = @_;
|
||||
my $object = {
|
||||
jsonrpc => '2.0',
|
||||
id => $id,
|
||||
error => {
|
||||
message => $message,
|
||||
code => $code
|
||||
}
|
||||
};
|
||||
$object->{error}{data} = $data if defined $data;
|
||||
return $object;
|
||||
}
|
||||
|
||||
sub error_bad_jsonrpc
|
||||
{
|
||||
my ($data) = @_;
|
||||
|
||||
jsonrpc_error('The JSON sent is not a valid request object.', '-32600', $data, undef);
|
||||
}
|
||||
|
||||
sub error_missing_params
|
||||
{
|
||||
jsonrpc_error("Missing 'params' object", '-32602', undef, 'testing');
|
||||
}
|
||||
|
||||
sub error_bad_params
|
||||
{
|
||||
my ($messages) = @_;
|
||||
|
||||
my @data;
|
||||
|
||||
while (@$messages) {
|
||||
my $path = shift @$messages;
|
||||
my $message = shift @$messages;
|
||||
push @data, { path => $path, message => $message };
|
||||
}
|
||||
|
||||
jsonrpc_error('Invalid method parameter(s).', '-32602', \@data, 'testing');
|
||||
}
|
||||
|
||||
sub no_error
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
###
|
||||
### Test wrapper functions
|
||||
###
|
||||
|
||||
sub test_validation
|
||||
{
|
||||
my ($input, $output, $message) = @_;
|
||||
|
||||
my $res = $rpcapi->jsonrpc_validate($input);
|
||||
is_deeply($res, $output, $message) or diag(encode_json($res));
|
||||
}
|
||||
|
||||
|
||||
###
|
||||
### The tests themselves
|
||||
###
|
||||
|
||||
test_validation undef,
|
||||
error_bad_jsonrpc('/: Expected object - got null.'),
|
||||
"Sending undef is an error";
|
||||
|
||||
test_validation JSON::PP::false,
|
||||
error_bad_jsonrpc('/: Expected object - got boolean.'),
|
||||
"Sending a boolean is an error";
|
||||
|
||||
test_validation -1,
|
||||
error_bad_jsonrpc('/: Expected object - got number.'),
|
||||
"Sending a number is an error";
|
||||
|
||||
test_validation "hello",
|
||||
error_bad_jsonrpc('/: Expected object - got string.'),
|
||||
"Sending a string is an error";
|
||||
|
||||
test_validation [qw(a b c)],
|
||||
error_bad_jsonrpc('/: Expected object - got array.'),
|
||||
"Sending an array is an error";
|
||||
|
||||
test_validation {},
|
||||
error_bad_jsonrpc('/jsonrpc: Missing property. /method: Missing property.'),
|
||||
"Sending an empty object is an error";
|
||||
|
||||
test_validation { jsonrpc => '2.0' },
|
||||
error_bad_jsonrpc('/method: Missing property.'),
|
||||
"Sending an incomplete object is an error";
|
||||
|
||||
test_validation { jsonrpc => '2.0', method => 'system_versions' },
|
||||
error_bad_jsonrpc(''),
|
||||
"Sending an object with no ID is an error";
|
||||
|
||||
test_validation { jsonrpc => '2.0', method => 'system_versions', id => JSON::PP::false },
|
||||
error_bad_jsonrpc('/id: Expected null/number/string - got boolean.'),
|
||||
"Sending an object whose ID is a boolean is an error";
|
||||
|
||||
test_validation { jsonrpc => '2.0', method => 'system_versions', id => [qw(a b c)] },
|
||||
error_bad_jsonrpc('/id: Expected null/number/string - got array.'),
|
||||
"Sending an object whose ID is an array is an error";
|
||||
|
||||
test_validation { jsonrpc => '2.0', method => 'system_versions', id => { a => 1 } },
|
||||
error_bad_jsonrpc('/id: Expected null/number/string - got object.'),
|
||||
"Sending an object whose ID is an object is an error";
|
||||
|
||||
test_validation jsonrpc("job_status"),
|
||||
error_missing_params(),
|
||||
"Calling job_status without parameters is an error";
|
||||
|
||||
test_validation jsonrpc("job_status", undef, 1),
|
||||
error_bad_params(["/" => "Expected object - got null."]),
|
||||
"Passing null as parameter to job_status is an error";
|
||||
|
||||
test_validation jsonrpc("job_status", JSON::PP::false),
|
||||
error_bad_params(["/" => "Expected object - got boolean."]),
|
||||
"Passing boolean as parameter to job_status is an error";
|
||||
|
||||
test_validation jsonrpc("job_status", 1),
|
||||
error_bad_params(["/" => "Expected object - got number."]),
|
||||
"Passing number as parameter to job_status is an error";
|
||||
|
||||
test_validation jsonrpc("job_status", "hello"),
|
||||
error_bad_params(["/" => "Expected object - got string."]),
|
||||
"Passing string as parameter to job_status is an error";
|
||||
|
||||
test_validation jsonrpc("job_status", [qw(a b c)]),
|
||||
error_bad_params(["/" => "Expected object - got array."]),
|
||||
"Passing array as parameter to job_status is an error";
|
||||
|
||||
test_validation jsonrpc("job_status", {}),
|
||||
error_bad_params(["/job_id" => "Missing property"]),
|
||||
"Passing empty object as parameter to job_status is an error";
|
||||
|
||||
test_validation jsonrpc("job_status", { job_id => 'this_will_definitely_never_ever_exist' }),
|
||||
error_bad_params(["/job_id" => 'String does not match (?^u:^[0-9a-f]{16}$).']),
|
||||
"Calling job_status with a bad job_id is an error";
|
||||
|
||||
test_validation jsonrpc("job_status", { job_id => '0123456789abcdef', data => "something" }),
|
||||
error_bad_params(["/" => "Properties not allowed: data."]),
|
||||
"Calling job_status with unknown parameters is an error";
|
||||
|
||||
test_validation jsonrpc("job_status", { job_id => '0123456789abcdef' }),
|
||||
no_error,
|
||||
"Calling job_status with a good job_id succeeds";
|
||||
|
||||
test_validation jsonrpc("system_versions"),
|
||||
no_error,
|
||||
"Calling system_versions with no parameters is OK";
|
||||
|
||||
test_validation jsonrpc("system_versions", undef, 1),
|
||||
error_bad_params(["/" => "Expected object - got null."]),
|
||||
"Passing null as parameter to system_versions is an error";
|
||||
|
||||
test_validation jsonrpc("system_versions", JSON::PP::false),
|
||||
error_bad_params(["/" => "Expected object - got boolean."]),
|
||||
"Passing number as parameter to system_versions is an error";
|
||||
|
||||
test_validation jsonrpc("system_versions", -1),
|
||||
error_bad_params(["/" => "Expected object - got number."]),
|
||||
"Passing number as parameter to system_versions is an error";
|
||||
|
||||
test_validation jsonrpc("system_versions", "hello"),
|
||||
error_bad_params(["/" => "Expected object - got string."]),
|
||||
"Passing string as parameter to system_versions is an error";
|
||||
|
||||
test_validation jsonrpc("system_versions", [qw(a b c)]),
|
||||
error_bad_params(["/" => "Expected object - got array."]),
|
||||
"Passing array as parameter to system_versions is an error";
|
||||
|
||||
test_validation jsonrpc("system_versions", { data => "something" }),
|
||||
error_bad_params(["/" => "Properties not allowed: data."]),
|
||||
"Calling system_versions with unrecognized parameter is an error";
|
||||
|
||||
test_validation jsonrpc("system_versions", {}),
|
||||
no_error,
|
||||
"Calling system_versions with empty object succeeds";
|
||||
Reference in New Issue
Block a user