fnox 1.25.1

A flexible secret management tool supporting multiple providers and encryption methods
Documentation
#!/usr/bin/env bats

setup() {
	load 'test_helper/common_setup'
	_common_setup
}

teardown() {
	_common_teardown
}

@test "config recursion finds parent configs" {
	# Create directory structure
	mkdir -p parent/child/grandchild

	# Create parent config (allows recursion to child)
	cat >parent/fnox.toml <<EOF
[providers.test]
type = "age"
recipients = ["age1test"]

[secrets]
PARENT_SECRET = { description = "Parent secret", default = "parent-value" }

[profiles.production.secrets]
PARENT_PROD_SECRET = { description = "Parent prod secret", default = "parent-prod-value" }
EOF

	# Create child config
	cat >parent/child/fnox.toml <<EOF
[secrets]
CHILD_SECRET = { description = "Child secret", default = "child-value" }
PARENT_SECRET = { description = "Override parent secret", default = "child-override-value" }
EOF

	# Change to grandchild directory
	cd parent/child/grandchild

	# Test that we can access merged secrets
	run "$FNOX_BIN" get PARENT_SECRET
	assert_success
	assert_output --partial "child-override-value" # Child overrides parent

	run "$FNOX_BIN" get CHILD_SECRET
	assert_success
	assert_output --partial "child-value"

	run "$FNOX_BIN" get PARENT_PROD_SECRET --profile production
	assert_success
	assert_output --partial "parent-prod-value"
}

@test "config root stops recursion" {
	# Create directory structure
	mkdir -p root/child/grandchild

	# Create root config with root = true
	cat >root/fnox.toml <<EOF
root = true

[providers.test]
type = "age"
recipients = ["age1test"]

[secrets]
ROOT_SECRET = { description = "Root secret", default = "root-value" }
EOF

	# Create grandparent config above root (should be ignored)
	cat >fnox.toml <<EOF
root = true

[secrets]
GRANDPARENT_SECRET = { description = "Should be ignored", default = "grandparent-value" }
EOF

	# Change to child directory
	cd root/child

	# Test that we can access root secrets
	run "$FNOX_BIN" get ROOT_SECRET
	assert_success
	assert_output --partial "root-value"

	# Test that grandparent secrets are not accessible
	run "$FNOX_BIN" get GRANDPARENT_SECRET
	assert_failure
	assert_output --partial "not found"
}

@test "config imports work correctly" {
	# Create imported config
	cat >imported.toml <<EOF
root = true

[providers.test]
type = "age"
recipients = ["age1test"]

[secrets]
IMPORTED_SECRET = { description = "Imported secret", default = "imported-value" }

[profiles.imported.secrets]
IMPORTED_PROFILE_SECRET = { description = "Imported profile secret", default = "imported-profile-value" }
EOF

	# Create main config that imports
	cat >fnox.toml <<EOF
root = true
import = ["./imported.toml"]

[providers.test]
type = "age"
recipients = ["age1test"]

[secrets]
MAIN_SECRET = { description = "Main secret", default = "main-value" }
IMPORTED_SECRET = { description = "Override imported secret", default = "main-override-value" }
EOF

	# Test that we can access imported secrets
	run "$FNOX_BIN" get IMPORTED_SECRET
	assert_success
	assert_output --partial "main-override-value" # Main overrides imported

	run "$FNOX_BIN" get MAIN_SECRET
	assert_success
	assert_output --partial "main-value"

	run "$FNOX_BIN" get IMPORTED_PROFILE_SECRET --profile imported
	assert_success
	assert_output --partial "imported-profile-value"
}

@test "config imports with absolute paths work" {
	# Create imported config
	cat >imported.toml <<EOF
root = true

[providers.test]
type = "age"
recipients = ["age1test"]

[secrets]
ABS_IMPORT_SECRET = { description = "Absolute import secret", default = "abs-import-value" }
EOF

	# Get absolute path
	abs_path="$(pwd)/imported.toml"

	# Create main config with absolute import
	cat >fnox.toml <<EOF
root = true
import = ["${abs_path}"]

[providers.test]
type = "age"
recipients = ["age1test"]

[secrets]
MAIN_SECRET = { description = "Main secret", default = "main-value" }
EOF

	# Test that absolute import works
	run "$FNOX_BIN" get ABS_IMPORT_SECRET
	assert_success
	assert_output --partial "abs-import-value"
}

@test "config import errors are handled gracefully" {
	# Create main config with non-existent import
	cat >fnox.toml <<EOF
root = true
import = ["./nonexistent.toml"]

[secrets]
MAIN_SECRET = { description = "Main secret", default = "main-value" }
EOF

	# Test that import error is reported
	run "$FNOX_BIN" get MAIN_SECRET
	assert_failure
	assert_output --partial "Import file not found"
}

@test "config recursion with imports works together" {
	# Create directory structure
	mkdir -p parent/child

	# Create imported config
	cat >imported.toml <<EOF
root = true

[providers.test]
type = "age"
recipients = ["age1test"]

[secrets]
IMPORTED_SECRET = { description = "Imported secret", default = "imported-value" }
EOF

	# Create parent config with import
	cat >parent/fnox.toml <<EOF
import = ["../imported.toml"]

[providers.test]
type = "age"
recipients = ["age1test"]

[secrets]
PARENT_SECRET = { description = "Parent secret", default = "parent-value" }
EOF

	# Create child config
	cat >parent/child/fnox.toml <<EOF
[secrets]
CHILD_SECRET = { description = "Child secret", default = "child-value" }
EOF

	# Change to child directory
	cd parent/child

	# Test that we can access all secrets
	run "$FNOX_BIN" get IMPORTED_SECRET
	assert_success
	assert_output --partial "imported-value"

	run "$FNOX_BIN" get PARENT_SECRET
	assert_success
	assert_output --partial "parent-value"

	run "$FNOX_BIN" get CHILD_SECRET
	assert_success
	assert_output --partial "child-value"
}

@test "explicit config path bypasses recursion" {
	# Create directory structure
	mkdir -p parent/child

	# Create parent config
	cat >parent/fnox.toml <<EOF
[providers.test]
type = "age"
recipients = ["age1test"]

[secrets]
PARENT_SECRET = { description = "Parent secret", default = "parent-value" }
EOF

	# Create child config
	cat >parent/child/fnox.toml <<EOF
root = true

[secrets]
CHILD_SECRET = { description = "Child secret", default = "child-value" }
EOF

	# Change to child directory and test explicit path
	cd parent/child

	# Use explicit path - should only load that file
	run "$FNOX_BIN" -c ../fnox.toml get PARENT_SECRET
	assert_success
	assert_output --partial "parent-value"

	# Should not find child secret when using parent config
	run "$FNOX_BIN" -c ../fnox.toml get CHILD_SECRET
	assert_failure
	assert_output --partial "not found"
}