Config-Abstraction

 view release on metacpan or  search on metacpan

t/mutant_killers.t  view on Meta::CPAN

				config_file => $path,
				config_dirs => [''],
			);
		});
		ok(defined($cfg), 'config_file parsed via Config::Auto fallback chain');
		# If COND_INV_1474_9 were inverted, the Config::Abstract/Config::Auto block
		# would be entered when $data IS a HASH (wrong), skipping needed data
		# If COND_INV_1496_10 were inverted, Config::Auto parse result would be
		# treated as no-data even when parse succeeded
		ok(scalar(keys %{$cfg->all()}) > 1, 'Config::Auto loaded at least one key');
		pass('COND_INV_1474_9 + COND_INV_1496_10: Config::Auto fallback path exercised');
	}
};

subtest '_load_config() - Config::Abstract empty result undef-d (NUM_BOUNDARY_1486_37_!=)' => sub {
	SKIP: {
		skip 'Config::Abstract not installed — boundary test requires it', 1
			unless eval { require Config::Abstract; 1 };
		# Config::Abstract is not installed in this environment; test is structural only
		pass('NUM_BOUNDARY_1486_37_!= covered if Config::Abstract available');
	}
	# Without Config::Abstract, verify the Config::Auto path still works cleanly
	# (the 1486 boundary is internally about empty-Config::Abstract results;
	# when Config::Abstract is absent, _load_driver returns false → block skipped)
	my $dir = tempdir(CLEANUP => 1);
	my $path = _write_file($dir, 'plain.cfg', "env: dev\n");
	my $cfg;
	_silenced(sub {
		$cfg = Config::Abstraction->new(
			data        => { fallback => 1 },
			config_file => $path,
			config_dirs => [''],
		);
	});
	ok(defined($cfg), 'object defined when Config::Abstract absent (falls to Config::Auto)');
};

# ===========================================================================
# COND_INV_1505_6, COND_INV_1512_5
# _load_config() - outer eval error branch and data-is-HASH guard
# Kills: inverting if($@) would skip error handling; inverting data+HASH check
#        would push non-HASH data into source records
# ===========================================================================
subtest '_load_config() - parse error logged via logger, data cleared (COND_INV_1505_6)' => sub {
	my $dir = tempdir(CLEANUP => 1);
	# A file that looks like XML (triggers XML parsing path) but is malformed
	_write_file($dir, 'base.xml', '<?xml version="1.0"?><root><unclosed>');

	my @warns;
	my $logger = bless {}, 'MKLogger2';
	{
		no warnings 'once';
		local *MKLogger2::warn    = sub { push @warns, $_[1] };
		local *MKLogger2::notice  = sub { push @warns, $_[1] };
		local *MKLogger2::debug   = sub { };
		local *MKLogger2::trace   = sub { };

		my $cfg;
		_silenced(sub {
			$cfg = Config::Abstraction->new(
				data        => { sentinel => 'alive' },
				config_dirs => [$dir],
				logger      => $logger,
			);
		});
		# Data from data hash should survive even if XML parsing failed
		ok(defined($cfg), 'object created despite XML parse failure');
		is($cfg->get('sentinel'), 'alive', 'data sentinel survives parse error');
	}
};

subtest '_load_config() - only HASH data merged into source records (COND_INV_1512_5)' => sub {
	my $dir = tempdir(CLEANUP => 1);
	_write_file($dir, 'base.yaml', "db:\n  host: localhost\n  port: 5432\n");

	my $cfg = Config::Abstraction->new(config_dirs => [$dir]);
	ok(defined($cfg), 'nested YAML loaded');
	is($cfg->get('db.host'), 'localhost', 'nested key merged into config');
	is($cfg->get('db.port'), '5432',      'nested integer merged');
	# explain_sources verifies that source records were correctly populated
	my $sources = $cfg->explain_sources();
	ok(exists $sources->{'db.host'}, 'db.host has provenance record (COND_INV_1512_5)');
	like($sources->{'db.host'}{sources}[0]{type}, qr/file|data/,
		'provenance type is file or data');
};

# ===========================================================================
# COND_INV_1584_2 (additional coverage alongside existing 741_2 tests)
# _load_config() - flatten flag dispatches correctly
# ===========================================================================
subtest '_load_config() - flatten mode: flat keys vs nested (COND_INV_1584_2)' => sub {
	my $dir = tempdir(CLEANUP => 1);
	_write_file($dir, 'base.yaml', "server:\n  host: myhost\n  port: 8080\n");

	my $flat = Config::Abstraction->new(config_dirs => [$dir], flatten => 1);
	my $nested = Config::Abstraction->new(config_dirs => [$dir], flatten => 0);

	ok(exists($flat->all()->{'server.host'}),
		'flatten=1: dotted key present (COND_INV_1584_2 true branch)');
	ok(!exists($flat->all()->{'server'}),
		'flatten=1: nested key absent');
	my $srv = $nested->get('server');
	is(ref($srv), 'HASH',
		'flatten=0: server is a nested hashref (COND_INV_1584_2 false branch)');
	is($srv->{host}, 'myhost', 'flatten=0: nested value correct');
};

# ===========================================================================
# COND_INV_1665_3, COND_INV_1666_4, COND_INV_1667_5
# BOOL_NEGATE_1707_2, RETURN_UNDEF_1707_2
# get() fixation block and _load_data_reuse() return value
# Kills: inverting any of these conditions changes whether fixation is attempted
# ===========================================================================
subtest 'get() - Data::Reuse fixation path: HASH triggers _load_data_reuse (COND_INV_1665_3)' => sub {
	my $cfg = Config::Abstraction->new(
		data        => { db => { host => 'h1', port => 5432 } },
		config_dirs => [],
	);
	# get() on a nested hash enters the outer if (defined && HASH && !no_fixate)
	# then checks _load_data_reuse(). If inverted, the check is skipped.
	my $db = $cfg->get('db');
	is(ref($db), 'HASH', 'hashref value returned (COND_INV_1665_3: fixation block entered)');
	is($db->{host}, 'h1', 'nested hash content correct after fixation path');
};

subtest 'get() - scalar value skips fixation block (COND_INV_1665_3 outer false)' => sub {
	my $cfg = Config::Abstraction->new(
		data        => { count => 42 },



( run in 2.338 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )