package backup import "testing" // TestReconcileRestoreSecrets covers the safety-critical fail-closed gate + secret reconciliation. func TestReconcileRestoreSecrets(t *testing.T) { nonSecret := map[string]string{"SUBDOMAIN": "trips", "DOMAIN": "demo-felhom.eu"} t.Run("all recovered, no data_key — full env, no error", func(t *testing.T) { recovered := map[string]string{"DB_PASSWORD": "pw", "SECRET_KEY": "deadbeef"} full, missing, err := reconcileRestoreSecrets(nonSecret, recovered, []string{"DB_PASSWORD", "SECRET_KEY"}, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(missing) != 0 { t.Errorf("missing: %v", missing) } // Non-secret + both secrets present, and recovered values used VERBATIM (regenerate nothing). if full["SUBDOMAIN"] != "trips" || full["DB_PASSWORD"] != "pw" || full["SECRET_KEY"] != "deadbeef" { t.Errorf("full env wrong: %v", full) } }) t.Run("data_key missing — FAIL CLOSED (refuse)", func(t *testing.T) { recovered := map[string]string{"DB_PASSWORD": "pw"} // SECRET_KEY (a data_key) is gone full, _, err := reconcileRestoreSecrets(nonSecret, recovered, []string{"DB_PASSWORD", "SECRET_KEY"}, []string{"SECRET_KEY"}) if err == nil { t.Fatal("expected fail-closed error for missing data-encrypting key, got nil") } if full != nil { t.Errorf("full env should be nil on refusal, got %v", full) } }) t.Run("data_key empty value — FAIL CLOSED", func(t *testing.T) { recovered := map[string]string{"SECRET_KEY": ""} // present but empty == unrecoverable _, _, err := reconcileRestoreSecrets(nonSecret, recovered, []string{"SECRET_KEY"}, []string{"SECRET_KEY"}) if err == nil { t.Fatal("empty data-key value must fail closed") } }) t.Run("resettable secret missing — proceed with warning", func(t *testing.T) { recovered := map[string]string{"SECRET_KEY": "deadbeef"} // data_key ok; DB_PASSWORD missing full, missing, err := reconcileRestoreSecrets(nonSecret, recovered, []string{"DB_PASSWORD", "SECRET_KEY"}, []string{"SECRET_KEY"}) if err != nil { t.Fatalf("a missing resettable secret must NOT fail closed: %v", err) } if len(missing) != 1 || missing[0] != "DB_PASSWORD" { t.Errorf("missing should be [DB_PASSWORD], got %v", missing) } if full["SECRET_KEY"] != "deadbeef" { t.Errorf("data-key should be preserved verbatim: %v", full) } if _, present := full["DB_PASSWORD"]; present { t.Errorf("missing resettable secret should be absent, not regenerated") } }) }