Config-Abstraction
view release on metacpan or search on metacpan
use strict;
use warnings;
use Test::Most;
use File::Spec;
use File::Slurp qw(write_file);
use Test::TempDir::Tiny;
BEGIN { use_ok('Config::Abstraction') }
local @ARGV = ('--APP_foo=baz');
my $test_dir = tempdir();
write_file("$test_dir/base.yaml", <<'YAML');
---
foo: bar
YAML
my $config = Config::Abstraction->new(
config_dirs => [$test_dir],
env_prefix => 'APP_',
t/edge_cases.t view on Meta::CPAN
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('key'), 'original', 'data intact when no ENV vars match prefix');
};
# ===========================================================================
# Pathological CLI argument edge cases
# ===========================================================================
subtest 'CLI - arg without = sign is ignored' => sub {
local @ARGV = ("--${ENV_PREFIX}RETRIES");
my $cfg = Config::Abstraction->new(
data => { retries => 3 },
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('retries'), 3, 'CLI arg without = sign ignored');
};
subtest 'CLI - arg with empty value sets empty string' => sub {
local @ARGV = ("--${ENV_PREFIX}RETRIES=");
my $cfg = Config::Abstraction->new(
data => { retries => 3 },
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
my $val = $cfg->get('retries');
is($val, '', 'CLI arg with empty value sets empty string');
};
subtest 'CLI - arg with = in value captures full value' => sub {
local @ARGV = ("--${ENV_PREFIX}DSN=host=localhost;port=5432");
my $cfg = Config::Abstraction->new(
data => { dsn => 'original' },
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('dsn'), 'host=localhost;port=5432', 'CLI value with embedded = preserved');
};
subtest 'CLI - non-matching prefix args ignored' => sub {
local @ARGV = ('--OTHERAPP_KEY=value', '--notanoption', 'positional');
my $cfg = Config::Abstraction->new(
data => { key => 'original' },
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('key'), 'original', 'non-matching CLI args ignored');
};
# ===========================================================================
t/extended_tests.t view on Meta::CPAN
};
ok(!$@, 'bare prefix ENV key does not crash');
};
# ===========================================================================
# CLI handling branch coverage
# ===========================================================================
# Exercise single-part CLI path (no double-underscore)
subtest 'CLI - single-part path sets top-level key' => sub {
local @ARGV = ("--${ENV_PREFIX}MODE=production");
my $cfg = Config::Abstraction->new(
data => { mode => 'development' },
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('mode'), 'production', 'single-part CLI path sets key');
};
# Exercise multi-part CLI path (with double-underscore)
subtest 'CLI - three-part path creates two levels of nesting' => sub {
local @ARGV = ("--${ENV_PREFIX}DB__POOL__SIZE=20");
my $cfg = Config::Abstraction->new(
data => {
db => { pool => { size => 5 } },
},
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('db.pool.size'), '20', 'three-part CLI path sets deeply nested key');
};
# Exercise the branch where @ARGV has non-option entries mixed in
subtest 'CLI - non-option ARGV entries ignored' => sub {
local @ARGV = ('positional', "--${ENV_PREFIX}RETRIES=5", '--', 'another');
my $cfg = Config::Abstraction->new(
data => { retries => $EXPECTED_RETRIES },
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('retries'), '5', 'option processed among non-option ARGV entries');
};
# ===========================================================================
t/function.t view on Meta::CPAN
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('database.user'), 'env_user', 'double-underscore ENV creates nested key');
};
# ===========================================================================
# Command-line argument merging (via _load_config internals)
# ===========================================================================
subtest 'CLI args override data values' => sub {
local @ARGV = ("--TESTAPP_RETRIES=77");
my $cfg = Config::Abstraction->new(
data => { retries => $EXPECTED_RETRIES },
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('retries'), '77', 'CLI arg overrides data value');
};
subtest 'CLI args with double-underscore create nested keys' => sub {
# \%NESTED_DATA must not be used here - the CLI merge path modifies nested
# hashrefs in-place via shared references from the shallow copy of 'data',
# which would attempt to modify the Readonly nested hashrefs and die.
# Use a fresh anonymous hash instead so the merge can write freely.
local @ARGV = ('--TESTAPP_DATABASE__USER=cli_user');
my $cfg = Config::Abstraction->new(
data => {
database => { user => $EXPECTED_USER, pass => $EXPECTED_PASS },
retries => $EXPECTED_RETRIES,
},
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('database.user'), 'cli_user', 'CLI double-underscore creates nested key');
t/integration.t view on Meta::CPAN
is($cfg->get('database.user'), $OVERRIDE_USER, 'INI database.user loaded');
is($cfg->get('database.port'), $OVERRIDE_PORT, 'INI database.port loaded');
};
# ===========================================================================
# Full merge precedence stack: data < file < ENV < CLI
# ===========================================================================
subtest 'end-to-end: full merge precedence stack' => sub {
local %ENV = %ENV;
local @ARGV = ("--${ENV_PREFIX}DATABASE__USER=cli_user");
$ENV{"${ENV_PREFIX}DATABASE__PORT"} = $OVERRIDE_PORT;
my $dir = tempdir(CLEANUP => 1);
_write_file($dir, $YAML_BASE,
"database:\n user: file_user\n port: $EXPECTED_PORT\n host: $EXPECTED_HOST\n");
my $cfg = new_ok($MODULE => [
data => _fresh_data(),
config_dirs => [$dir],
env_prefix => $ENV_PREFIX,
env_prefix => $ENV_PREFIX,
);
is($cfg->get('api.rate_limit'), '100', 'mixed underscore ENV key handled correctly');
};
# ===========================================================================
# Command-line argument overrides
# POD: adding --APP_DATABASE__USER=other_user_name to command line
# ===========================================================================
subtest 'CLI arg overrides top-level key' => sub {
local @ARGV = ("--${ENV_PREFIX}RETRIES=77");
my $cfg = Config::Abstraction->new(
data => _fresh_data(),
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('retries'), '77', 'CLI arg overrides top-level key');
};
subtest 'CLI double-underscore creates nested key' => sub {
local @ARGV = ("--${ENV_PREFIX}DATABASE__USER=cli_user");
my $cfg = Config::Abstraction->new(
data => {
database => { user => $EXPECTED_USER, pass => $EXPECTED_PASS },
retries => $EXPECTED_RETRIES,
},
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('database.user'), 'cli_user', 'CLI double-underscore creates nested key');
};
subtest 'CLI arg without matching prefix is ignored' => sub {
local @ARGV = ('--OTHERAPP_RETRIES=999');
my $cfg = Config::Abstraction->new(
data => _fresh_data(),
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('retries'), $EXPECTED_RETRIES, 'non-matching prefix CLI arg ignored');
};
# ===========================================================================
# Merge precedence
# POD: CLI args > Environment > Config file > Defaults
# ===========================================================================
subtest 'merge precedence: CLI overrides ENV overrides data' => sub {
local %ENV = %ENV;
local @ARGV = ("--${ENV_PREFIX}DATABASE__USER=cli_user");
$ENV{"${ENV_PREFIX}DATABASE__USER"} = 'env_user';
my $cfg = Config::Abstraction->new(
data => {
database => { user => $EXPECTED_USER, pass => $EXPECTED_PASS },
},
config_dirs => [],
env_prefix => $ENV_PREFIX,
);
is($cfg->get('database.user'), 'cli_user', 'CLI takes highest precedence');
( run in 1.913 second using v1.01-cache-2.11-cpan-5a3173703d6 )