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 )