CGI-Info
view release on metacpan or search on metacpan
t/30-basics.t view on Meta::CPAN
#!/usr/bin/env perl
use strict;
use warnings;
use File::Spec;
use File::Temp qw/tempfile/;
use Test::Most;
# Load the module being tested
BEGIN { use_ok('CGI::Info') }
local %ENV;
$ENV{'SCRIPT_NAME'} = 'test_script';
$ENV{'HTTP_USER_AGENT'} = 'Mozilla/5.0';
# Test object creation
my $cgi_info = new_ok('CGI::Info');
ok($cgi_info, 'CGI::Info object created');
# Test script_name method
can_ok($cgi_info, 'script_name');
my $script_name = $cgi_info->script_name();
is($script_name, $ENV{'SCRIPT_NAME'}, 'script_name matches the environment variable');
# Test host_name method
can_ok($cgi_info, 'host_name');
like($cgi_info->host_name(), qr/\w+/, 'host_name returns a valid string');
# Test is_mobile method
can_ok($cgi_info, 'is_mobile');
is($cgi_info->is_mobile(), 0, 'is_mobile returns false by default (not a mobile device)');
# Helper to mock environment variables
sub mock_env {
my ($env, $code) = @_;
local %ENV = %$env;
$code->();
}
subtest 'CGI::Info' => sub {
subtest 'Constructor (new)' => sub {
subtest 'should handle invalid parameters gracefully' => sub {
throws_ok { CGI::Info->new('invalid_param', 'value', 'another parm') } qr/^Usage/, 'Dies on invalid args';
};
subtest 'should load config file if provided' => sub {
my ($fh, $config_file) = tempfile(TEMPLATE => 'test_configXXXX', SUFFIX => '.yml', TMPDIR => 1);
print $fh "---\nmax_upload_size: 100\n";
close $fh;
my $info = CGI::Info->new(config_file => $config_file);
is $info->{max_upload_size}, 100, 'Config file loaded correctly';
unlink $config_file;
};
# Expect is deprecated
# subtest 'should reject non-ARRAY expect parameter' => sub {
# throws_ok { CGI::Info->new(expect => {}) } qr/expect must be a reference/, 'Rejects non-array expect';
# };
subtest 'expect has been deprecated' => sub {
throws_ok { CGI::Info->new(expect => {}) } qr/expect has been deprecated/, 'Rejects non-array expect';
};
};
subtest 'script_name, script_path, script_dir' => sub {
subtest 'should handle CLI environment' => sub {
mock_env({}, sub {
my $info = CGI::Info->new();
like $info->script_name, qr/\w+\.t/, "Script name from \$0 in CLI";
ok -e $info->script_path, 'Script path exists';
ok -d $info->script_dir, 'Script directory exists';
});
};
subtest 'should handle CGI environment' => sub {
mock_env({ SCRIPT_NAME => '/cgi-bin/test.cgi', DOCUMENT_ROOT => '/var/www' }, sub {
my $info = CGI::Info->new();
is($info->script_name(), 'test.cgi', 'Correct script name from SCRIPT_NAME');
if($^O eq 'MSWin32') {
like($info->script_path(), qr/\\var\\www\\cgi-bin\\test\.cgi/, 'Script path constructed correctly');
} else {
# http://www.cpantesters.org/cpan/report/00f6f172-6d25-1014-9d67-b345cf55203a
like($info->script_path(), qr/\/var\/www\/cgi-bin\/test\.cgi/, 'Script path constructed correctly');
}
});
};
};
subtest 'params method' => sub {
subtest 'should handle GET requests' => sub {
mock_env({
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'GET',
QUERY_STRING => 'name=John&age=30'
}, sub {
my $info = CGI::Info->new();
my $params = $info->params();
is $params->{name}, 'John', "GET param 'name' correct";
is $params->{age}, '30', "GET param 'age' correct";
});
};
subtest 'should block SQL injection attempts' => sub {
mock_env({
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'GET',
QUERY_STRING => 'id=1%27%20OR%201=1--'
}, sub {
my $info = CGI::Info->new(allow => { id => qr/^\d+$/ });
my $params = $info->params();
is $info->status, 422, 'Status 422 on SQL injection';
ok !defined $params->{id}, 'Blocked malicious parameter';
});
};
subtest 'should handle multipart form uploads' => sub {
mock_env({
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'POST',
CONTENT_TYPE => 'multipart/form-data; boundary=----boundary',
CONTENT_LENGTH => 1000
}, sub {
local *STDIN;
open STDIN, '<', \"------boundary\nContent-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\n\ncontent\n------boundary--";
my $info = CGI::Info->new(upload_dir => File::Spec->tmpdir());
my $params = $info->params();
like($params->{file}, qr/test\.txt/, 'File upload handled');
unlink $params->{'file'}
});
};
subtest 'should reject oversized uploads' => sub {
mock_env({
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'POST',
CONTENT_TYPE => 'application/x-www-form-urlencoded',
CONTENT_LENGTH => 600 * 1024 # 600KB
}, sub {
my $info = CGI::Info->new(max_upload => 500); # 500KB limit
my $params = $info->params;
is $info->status, 413, 'Status 413 on oversized upload';
ok !defined $params, 'No params returned';
});
};
};
subtest 'Security Checks' => sub {
subtest 'should block XSS attempts' => sub {
mock_env({
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'GET',
QUERY_STRING => 'comment=<script>alert(1)</script>'
}, sub {
my $info = CGI::Info->new();
my $params = $info->params;
unlike $params->{comment}, qr/<script>/, 'XSS attempt sanitized';
});
};
subtest 'should prevent directory traversal' => sub {
mock_env({
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'GET',
QUERY_STRING => 'file=../../etc/passwd'
}, sub {
my $info = new_ok('CGI::Info');
my $params = $info->params();
is($info->status(), 403, 'Status 403 on traversal attempt');
});
};
};
subtest 'User Agent Detection' => sub {
subtest 'should detect mobile devices' => sub {
mock_env({ HTTP_USER_AGENT => 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1' }, sub {
my $info = CGI::Info->new();
ok($info->is_mobile(), 'iPhone detected as mobile');
});
};
subtest 'should detect search engines' => sub {
mock_env({
REMOTE_ADDR => '66.249.65.32',
HTTP_USER_AGENT => 'Googlebot/2.1 (+http://www.google.com/bot.html)'
}, sub {
my $info = new_ok('CGI::Info');
ok $info->is_search_engine, 'Googlebot detected as search engine';
});
};
};
subtest 'Directory Methods' => sub {
subtest 'should find valid tmpdir' => sub {
my $info = new_ok('CGI::Info');
ok -d $info->tmpdir, 'tmpdir exists and is directory';
};
subtest 'should handle non-writable logdir' => sub {
my $info = new_ok('CGI::Info');
throws_ok { $info->logdir('/non/existent/path') } qr/Invalid logdir/, 'Handles invalid logdir';
};
( run in 1.178 second using v1.01-cache-2.11-cpan-b16cb0d3907 )