secrets/test/manifest.bats
Brian Majewski 18018dbd3b feat: .secrets.json manifest core — add command, rails, canonical form (EGB-677 stage 1)
- secrets add <path>: explicit manifest writer, idempotent, atomic write
- _validate_dotenv_rel_path: project-relative confinement rail (no .. /
  absolute / shell metas; @ allowed for npm-scoped workspace dirs)
- _check_manifest_file: refuses symlinks, malformed JSON (jq error with
  file named), unsupported schema versions (directed upgrade error)
- canonical serialization: jq --sort-keys + sorted/deduped dotenv —
  add order produces byte-identical manifests
- which: validates + summarizes the manifest (doubles as linter)
- jq required only when a manifest exists/is written
2026-06-07 08:24:35 -07:00

125 lines
3.7 KiB
Bash

#!/usr/bin/env bats
# EGB-677 stage 1: .secrets.json manifest — parse, rails, add, generators.
load test_helper
# ─── A: manifest core — secrets add + rails + canonical form ──────────
@test "add creates .secrets.json with version 2 and the dotenv entry" {
create_project_dir addproj
run "$SECRETS_BIN" add .env
[ "$status" -eq 0 ]
[ -f ".secrets.json" ]
run jq -r '.version' .secrets.json
[ "$output" = "2" ]
run jq -r '.dotenv[0]' .secrets.json
[ "$output" = ".env" ]
}
@test "add is idempotent — no duplicate entries" {
create_project_dir addproj
"$SECRETS_BIN" add .env >/dev/null
run "$SECRETS_BIN" add .env
[ "$status" -eq 0 ]
run jq -r '.dotenv | length' .secrets.json
[ "$output" = "1" ]
}
@test "add accepts nested workspace paths" {
create_project_dir addproj
mkdir -p packages/web
echo "K=v" > packages/web/.env.development
run "$SECRETS_BIN" add packages/web/.env.development
[ "$status" -eq 0 ]
run jq -r '.dotenv | index("packages/web/.env.development") != null' .secrets.json
[ "$output" = "true" ]
}
@test "add accepts npm-scoped workspace paths (@)" {
create_project_dir addproj
mkdir -p "packages/@acme/web"
echo "K=v" > "packages/@acme/web/.env"
run "$SECRETS_BIN" add "packages/@acme/web/.env"
[ "$status" -eq 0 ]
run jq -r '.dotenv | index("packages/@acme/web/.env") != null' .secrets.json
[ "$output" = "true" ]
}
@test "add rejects path traversal (..)" {
create_project_dir addproj
run "$SECRETS_BIN" add ../escape/.env
[ "$status" -eq 1 ]
[[ "$output" == *"project-relative"* ]] || false
[ ! -f ".secrets.json" ]
}
@test "add rejects absolute paths" {
create_project_dir addproj
run "$SECRETS_BIN" add /etc/passwd
[ "$status" -eq 1 ]
[[ "$output" == *"project-relative"* ]] || false
[ ! -f ".secrets.json" ]
}
@test "add rejects shell metacharacters in path" {
create_project_dir addproj
run "$SECRETS_BIN" add '.env;rm -rf ~'
[ "$status" -eq 1 ]
[ ! -f ".secrets.json" ]
}
@test "add requires the file to exist" {
create_project_dir addproj
run "$SECRETS_BIN" add .env.missing
[ "$status" -eq 1 ]
[[ "$output" == *"not found"* ]] || false
}
@test "manifest serialization is canonical — order of adds does not matter" {
create_project_dir addproj
echo "A=1" > .env.alpha
echo "B=2" > .env.beta
"$SECRETS_BIN" add .env.alpha >/dev/null
"$SECRETS_BIN" add .env.beta >/dev/null
cp .secrets.json "$TEST_TMPDIR/order1.json"
rm .secrets.json
"$SECRETS_BIN" add .env.beta >/dev/null
"$SECRETS_BIN" add .env.alpha >/dev/null
cmp -s .secrets.json "$TEST_TMPDIR/order1.json"
}
@test "which shows manifest summary when .secrets.json is present" {
create_project_dir addproj
"$SECRETS_BIN" add .env >/dev/null
run "$SECRETS_BIN" which
[ "$status" -eq 0 ]
[[ "$output" == *".secrets.json"* ]] || false
[[ "$output" == *".env"* ]] || false
}
@test "malformed .secrets.json dies with a directed error naming the file" {
create_project_dir addproj
echo '{ not json' > .secrets.json
run "$SECRETS_BIN" which
[ "$status" -eq 1 ]
[[ "$output" == *".secrets.json"* ]] || false
[[ "$output" == *"invalid"* ]] || false
}
@test "unsupported manifest version dies with a directed upgrade error" {
create_project_dir addproj
echo '{"version": 99, "dotenv": [".env"]}' > .secrets.json
run "$SECRETS_BIN" which
[ "$status" -eq 1 ]
[[ "$output" == *"version 99"* ]] || false
[[ "$output" == *"supports"* ]] || false
}
@test "symlinked .secrets.json is refused" {
create_project_dir addproj
echo '{"version":2,"dotenv":[".env"]}' > "$TEST_TMPDIR/real-manifest.json"
ln -s "$TEST_TMPDIR/real-manifest.json" .secrets.json
run "$SECRETS_BIN" which
[ "$status" -eq 1 ]
[[ "$output" == *"symlink"* ]] || false
}