Config-Abstraction
view release on metacpan or search on metacpan
t/extended_tests.t view on Meta::CPAN
# The XML elsif in _parse_config_string (line 1290-1298) has 0 coverage hits
# because function.t exercises YAML/JSON/INI but not XML via that method.
subtest '_parse_config_string() - XML content parsed via XML::Simple or XML::PP' => sub {
my $proxy = Config::Abstraction::ExtTestProxy->new(
data => { dummy => 1 },
config_dirs => [],
);
my $xml = "<?xml version=\"1.0\"?><config><server>prod</server><port>8080</port></config>";
my $result = $proxy->test_parse_string($xml, 'remote.xml', 'remote-label');
ok(defined($result), 'XML content in _parse_config_string returns defined');
is(ref($result), 'HASH', 'XML result is a hashref');
is($result->{server}, 'prod', 'XML string value correct');
diag("xml result: server=$result->{server}") if $ENV{TEST_VERBOSE} && defined($result);
};
# ===========================================================================
# _load_driver() -- cached failure path (return 0 from negative cache)
# ===========================================================================
# After _load_driver fails for a module once, subsequent calls must return 0
# (the negative-cache path at line 995) rather than retrying the require.
# The FIRST failure returns undef; only the SECOND returns the explicit 0.
subtest '_load_driver() - returns 0 from negative cache on repeated failure' => sub {
my $cfg = Config::Abstraction->new(
data => { k => 'v' },
config_dirs => [],
);
# First call: module does not exist â require fails â sets failed cache â returns undef
my $first = $cfg->_load_driver('No::Such::Module::AtAll::XYZZY9876');
ok(!defined($first) || !$first, 'first call returns false on load failure');
is($cfg->{failed}{'No::Such::Module::AtAll::XYZZY9876'}, 1, 'failure cached');
# Second call: negative cache hit â returns explicit 0 (not undef)
my $second = $cfg->_load_driver('No::Such::Module::AtAll::XYZZY9876');
is($second, 0, 'second call returns 0 from negative cache');
};
# ===========================================================================
# Constructor returns undef when no configuration is loaded (line 422 false)
# ===========================================================================
# When no data is passed AND config_dirs is empty (no files to load),
# the merged config is empty â `scalar(keys %{config})` == 0 â
# the condition at line 422 is FALSE â the constructor returns undef.
subtest 'new() - returns undef when no config data found' => sub {
# Isolate @ARGV and %ENV to prevent any accidental config injection
local @ARGV;
local %ENV = (PATH => $ENV{PATH}); # keep PATH, strip APP_* and others
# No data arg, no config files; config_dirs = [] so no files are searched
my $cfg = Config::Abstraction->new(config_dirs => []);
ok(!defined($cfg), 'constructor returns undef when no configuration data found');
};
# Similarly: constructor returns undef when only config_dirs is absent key=
# (calling new without any arguments at all uses default dirs, but if those
# dirs don't exist on this machine, config will be empty)
subtest 'new() - returns defined object when in-memory data is provided' => sub {
# With data arg, config is always non-empty â line 422 TRUE â returns $self
my $cfg = Config::Abstraction->new(
data => { alive => 1 },
config_dirs => [],
);
ok(defined($cfg), 'constructor returns defined object with in-memory data');
is($cfg->get('alive'), 1, 'in-memory data accessible');
};
# ===========================================================================
# _parse_remote_dir() -- branch coverage for $2 // '/' fallback (line 1154)
# ===========================================================================
# `_parse_remote_dir` is NOT access-guarded, so it can be called directly.
# When the path has no directory component after the hostname (e.g., /../host),
# capture group $2 is undef â `$2 // '/'` returns '/' (line 1154 fallback).
subtest '_parse_remote_dir() - hostname with no path uses / as default' => sub {
my $cfg = Config::Abstraction->new(
data => { dummy => 1 },
config_dirs => [],
);
# With path after hostname: $2 is defined
my ($host, $dir) = $cfg->_parse_remote_dir('/../remotehost/etc/myapp');
is($host, 'remotehost', 'hostname extracted from remote dir spec');
is($dir, '/etc/myapp', 'path component extracted correctly');
# Without path after hostname: $2 is undef â $2 // '/' fires
my ($host2, $dir2) = $cfg->_parse_remote_dir('/../remotehost');
is($host2, 'remotehost', 'hostname extracted with no path component');
is($dir2, '/', '$2 // "/" fallback: dir defaults to / when absent');
};
subtest '_parse_remote_dir() - non-remote path returns empty list' => sub {
my $cfg = Config::Abstraction->new(
data => { dummy => 1 },
config_dirs => [],
);
my @result = $cfg->_parse_remote_dir('/etc/myapp');
is(scalar(@result), 0, 'non-remote path returns empty list');
my @result2 = $cfg->_parse_remote_dir(undef);
is(scalar(@result2), 0, 'undef path returns empty list');
};
# ===========================================================================
# AUTOLOAD data || config fallback (line 1339)
# ===========================================================================
# Line 1339: `my $data = $self->{data} || $self->{'config'}`.
# The `||` right side fires when $self->{data} is falsy (undef or 0 or '').
# Under normal operation, {data} is always set to the merged config hashref
# from the constructor. We can force the right side by deleting {data}
# from an existing object.
subtest 'AUTOLOAD - falls back to config when data slot is absent' => sub {
my $cfg = Config::Abstraction->new(
data => { greet => 'hello' },
config_dirs => [],
sep_char => $SEP_US,
);
# Remove {data} to force the || right side (config) in AUTOLOAD line 1339
delete $cfg->{data};
my $val;
eval { $val = $cfg->greet() };
ok(!$@, 'AUTOLOAD does not crash when data is absent');
is($val, 'hello', 'AUTOLOAD falls back to config when data slot absent');
};
( run in 0.755 second using v1.01-cache-2.11-cpan-14f38c9f855 )