Test-APIcast/lib/Test/APIcast/Blackbox.pm
package Test::APIcast::Blackbox;
use strict;
use warnings FATAL => 'all';
use v5.10.1;
use JSON;
use Test::APIcast -Base;
use File::Copy "move";
use File::Temp qw/ tempfile /;
use File::Slurp qw(read_file);
BEGIN {
$ENV{APICAST_OPENRESTY_BINARY} = $ENV{TEST_NGINX_BINARY};
}
our $ApicastBinary = $ENV{TEST_NGINX_APICAST_BINARY} || 'bin/apicast';
our %EnvToNginx = ();
our %ResetEnv = ();
sub env_to_apicast (@) {
my %env = (@_);
# merge two hashes, new %env takes precedence
%EnvToNginx = (%EnvToNginx, %env);
};
my $original_server_port_for_client;
sub set_server_port_for_client (@) {
$original_server_port_for_client = $Test::Nginx::Util::ServerPortForClient;
Test::Nginx::Util::server_port_for_client(shift);
if ($Test::Nginx::Util::Verbose) {
warn("changed ServerPortForClient from $original_server_port_for_client to $Test::Nginx::Util::ServerPortForClient");
}
}
add_block_preprocessor(sub {
my $block = shift;
my $seq = $block->seq_num;
my $name = $block->name;
my $configuration = $block->configuration;
my $backend = $block->backend;
my $backend_name = $block->backend_name || 'test_backend';
my $upstream = $block->upstream;
my $upstream_name = $block->upstream_name || 'test';
my $test = $block->test;
my $sites_d = $block->sites_d || '';
my $ServerPort = $Test::Nginx::Util::ServerPort;
if (defined($test) && !defined($block->request) && !defined($block->raw_request) ) {
my $test_port = Test::APIcast::get_random_port();
$sites_d .= <<_EOC_;
server {
listen $test_port;
server_name test default_server;
set \$apicast_port $ServerPort;
location / {
$test
}
}
_EOC_
set_server_port_for_client($test_port);
$block->set_value('raw_request', "GET / HTTP/1.1\r\nHost: test\r\nConnection: close\r\n\r\n")
}
if (defined $backend) {
$sites_d .= <<_EOC_;
server {
listen $ServerPort;
server_name $backend_name backend;
$backend
}
upstream $backend_name {
server 127.0.0.1:$ServerPort;
}
_EOC_
$ENV{BACKEND_ENDPOINT_OVERRIDE} = "http://test_backend:$ServerPort";
}
if (defined $upstream) {
$sites_d .= <<_EOC_;
server {
listen $ServerPort;
server_name $upstream_name;
$upstream
}
upstream $upstream_name {
server 127.0.0.1:$ServerPort;
}
_EOC_
}
my $configuration_format = $block->configuration_format || 'json';
if (defined $configuration) {
$configuration = Test::Nginx::Util::expand_env_in_config($configuration);
{
local $SIG{__DIE__} = sub {
Test::More::fail("$name - configuration block JSON") || Test::More::diag $_[0];
};
if ($configuration_format eq 'json') {
decode_json($configuration);
}
}
$block->set_value("configuration", $configuration);
$block->set_value("configuration_format", $configuration_format);
}
$block->set_value("config", "$name ($seq)");
$block->set_value('sites_d', $sites_d)
});
my $write_nginx_config = sub {
my $block = shift;
my $FilterHttpConfig = $Test::Nginx::Util::FilterHttpConfig;
my $ConfFile = $Test::Nginx::Util::ConfFile;
my $Workers = $Test::Nginx::Util::Workers;
my $MasterProcessEnabled = $Test::Nginx::Util::MasterProcessEnabled;
my $DaemonEnabled = $Test::Nginx::Util::DaemonEnabled;
my $err_log_file = $block->error_log_file || $Test::Nginx::Util::ErrLogFile;
my $LogLevel = $Test::Nginx::Util::LogLevel;
my $PidFile = $Test::Nginx::Util::PidFile;
my $AccLogFile = $Test::Nginx::Util::AccLogFile;
my $ServerPort = $Test::Nginx::Util::ServerPort;
my $backend_port = Test::APIcast::get_random_port();
my $echo_port = Test::APIcast::get_random_port();
my $management_server_name = $ENV{TEST_NGINX_MANAGEMENT_SERVER_NAME};
my $management_port;
if (defined $management_server_name) {
$management_port = $ServerPort;
$management_server_name = "'$management_server_name'"
} else {
$management_port = Test::APIcast::get_random_port();
$management_server_name = 'nil'
}
my $environment = $block->environment;
my @environments;
my $environment_file = $block->environment_file;
if (defined $environment_file && ref $environment_file eq 'ARRAY') {
@environments = @$environment_file;
} elsif (defined($environment_file)) {
@environments = $environment_file;
}
unshift @environments, 'production';
my $sites_d = $block->sites_d;
my $apicast_cli = $block->apicast || $ApicastBinary;
my $configuration = $block->configuration;
my $conf;
my $configuration_file = $block->configuration_file;
my $configuration_format = $block->configuration_format;
if (defined $configuration_file) {
chomp($configuration_file);
$configuration_file = "$configuration_file";
} else {
if (defined $configuration) {
($conf, $configuration_file) = tempfile(SUFFIX => ".$configuration_format");
print $conf $configuration;
close $conf;
$configuration_file = "$configuration_file";
}
}
my %env = (%EnvToNginx, $block->env);
# reset ENV to memorized state
for my $key (keys %ResetEnv) {
if (defined $ResetEnv{$key}) {
$ENV{$key} = $ResetEnv{$key};
} else {
delete $ENV{$key};
}
delete $ResetEnv{$key};
}
for my $key (keys %env) {
# memorize ENV before changing it
$ResetEnv{$key} = $ENV{$key};
# change ENV to state desired by the test
$ENV{$key} = $env{$key};
}
my ($env, $env_file) = tempfile();
push @environments, $env_file;
my $apicast_cmd = "APICAST_CONFIGURATION_LOADER='test' $apicast_cli start --test";
if (defined $configuration_file && $configuration_file) {
$apicast_cmd .= " --configuration $configuration_file"
} else {
$configuration_file = "";
}
foreach my $ef (@environments) {
$apicast_cmd .= " --environment $ef"
}
if (defined $environment) {
print $env $environment;
} else {
print $env <<_EOC_;
return {
worker_processes = '$Workers',
master_process = '$MasterProcessEnabled',
daemon = '$DaemonEnabled',
error_log = '$err_log_file',
timer_resolution = false,
log_level = '$LogLevel',
pid = '$PidFile',
lua_code_cache = 'on',
access_log = '$AccLogFile',
port = {
apicast = '$ServerPort',
management = '$management_port',
backend = '$backend_port',
echo = '$echo_port',
metrics = '$ServerPort',
},
env = {
THREESCALE_CONFIG_FILE = [[$configuration_file]],
APICAST_CONFIGURATION_LOADER = 'boot'
},
server_name = {
management = $management_server_name
},
sites_d = [============================[$sites_d]============================],
}
_EOC_
}
close $env;
if ($ENV{DEBUG}) {
warn $apicast_cmd;
}
my $apicast = `${apicast_cmd} 2>&1`;
if ($apicast =~ /configuration file (?<file>.+?) test is successful/)
{
open(my $fh, '+>', $ConfFile) or die "cannot open $ConfFile: $!";
my $nginx_config = read_file($+{file});
if ($FilterHttpConfig) {
$nginx_config = $FilterHttpConfig->($nginx_config);
}
print { $fh } $nginx_config;
close($fh);
} else {
warn "Missing config file: $Test::Nginx::Util::ConfFile";
warn $apicast;
}
if ($PidFile && -f $PidFile) {
unlink $PidFile or warn "Couldn't remove $PidFile.\n";
}
$ENV{APICAST_LOADED_ENVIRONMENTS} = join('|',@environments);
};
add_block_preprocessor(sub {
if (defined $original_server_port_for_client) {
Test::Nginx::Util::server_port_for_client($original_server_port_for_client);
undef $original_server_port_for_client;
}
});
BEGIN {
no warnings 'redefine';
sub Test::Nginx::Util::write_config_file ($$) {
my $block = shift;
$write_nginx_config->($block);
Test::APIcast::close_random_ports();
}
}
our @EXPORT = qw(
env_to_apicast
);
1;