Files
unkinben f56bb6be29
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
Add terraform-provider-gpgvaultsecret
A terraform-plugin-framework provider for the vault-plugin-secrets-gpg engine,
managing engine mounts and OpenPGP keys on Vault/OpenBao.

- gpg_secret_backend resource: mount the engine (+ optional plugin catalog
  registration when a sha256 is given; deregisters on destroy).
- gpg_key resource: create/configure a key (algorithm, identity, exportable,
  deletion_allowed, min_decryption_version); computed public_key/fingerprint/
  key_id/latest_version; destroy auto-enables deletion; import <backend>/<name>.
- gpg_key data source: read a key's metadata + armored public key.
- Talks to Vault/OpenBao via hashicorp/vault/api; address/token fall back to
  VAULT_ADDR/VAULT_TOKEN. Unit tests plus an e2e running real terraform
  apply/destroy against a Vault dev server + the gpg plugin. Release publishes a
  zip to the artifactapi terraform-unkin registry on v* tags.
2026-07-16 23:33:04 +10:00

109 lines
3.0 KiB
Go

package provider
import (
"encoding/json"
"testing"
"github.com/hashicorp/terraform-plugin-framework/types"
)
func TestToInt64(t *testing.T) {
cases := []struct {
in interface{}
want int64
ok bool
}{
{json.Number("3"), 3, true},
{json.Number("3.0"), 3, true},
{float64(5), 5, true},
{int(7), 7, true},
{int64(9), 9, true},
{"nope", 0, false},
{nil, 0, false},
}
for _, c := range cases {
got, ok := toInt64(c.in)
if ok != c.ok || (ok && got != c.want) {
t.Errorf("toInt64(%v) = (%d,%v), want (%d,%v)", c.in, got, ok, c.want, c.ok)
}
}
}
func TestResolveCommand(t *testing.T) {
// explicit command wins
m := secretBackendModel{
Plugin: types.StringValue("vault-plugin-secrets-gpg"),
Command: types.StringValue("custom-binary"),
}
if got := resolveCommand(m); got != "custom-binary" {
t.Errorf("resolveCommand explicit = %q", got)
}
// falls back to plugin name when command is null
m = secretBackendModel{
Plugin: types.StringValue("vault-plugin-secrets-gpg"),
Command: types.StringNull(),
}
if got := resolveCommand(m); got != "vault-plugin-secrets-gpg" {
t.Errorf("resolveCommand fallback = %q", got)
}
}
func TestManagesPlugin(t *testing.T) {
if managesPlugin(secretBackendModel{SHA256: types.StringNull()}) {
t.Error("null sha256 should not manage the plugin")
}
if managesPlugin(secretBackendModel{SHA256: types.StringValue("")}) {
t.Error("empty sha256 should not manage the plugin")
}
if !managesPlugin(secretBackendModel{SHA256: types.StringValue("abc123")}) {
t.Error("set sha256 should manage the plugin")
}
}
func TestKeyIDForLatest(t *testing.T) {
data := map[string]interface{}{
"latest_version": json.Number("2"),
"keys": map[string]interface{}{
"1": map[string]interface{}{"key_id": "AAAA1111"},
"2": map[string]interface{}{"key_id": "BBBB2222"},
},
}
if got := keyIDForLatest(data); got != "BBBB2222" {
t.Errorf("keyIDForLatest = %q, want BBBB2222", got)
}
// missing data yields empty string, not a panic
if got := keyIDForLatest(map[string]interface{}{}); got != "" {
t.Errorf("keyIDForLatest(empty) = %q, want \"\"", got)
}
}
func TestApplyKeyData(t *testing.T) {
m := &keyModel{}
applyKeyData(m, map[string]interface{}{
"algorithm": "ed25519",
"identity": "Me <me@x>",
"exportable": true,
"deletion_allowed": false,
"min_decryption_version": json.Number("1"),
"latest_version": json.Number("1"),
"fingerprint": "DEADBEEF",
"public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----",
"keys": map[string]interface{}{
"1": map[string]interface{}{"key_id": "CAFED00D"},
},
})
if m.Algorithm.ValueString() != "ed25519" {
t.Errorf("algorithm = %q", m.Algorithm.ValueString())
}
if !m.Exportable.ValueBool() {
t.Error("exportable should be true")
}
if m.Fingerprint.ValueString() != "DEADBEEF" {
t.Errorf("fingerprint = %q", m.Fingerprint.ValueString())
}
if m.KeyID.ValueString() != "CAFED00D" {
t.Errorf("key_id = %q", m.KeyID.ValueString())
}
}