package secret import ( "errors" "strings" "testing" ) func withKey(t *testing.T, key string) { t.Helper() mu.RLock() prev := master mu.RUnlock() Install([]byte(key)) t.Cleanup(func() { Install(prev) }) } func TestSealOpensForTheSamePurposeAndOwner(t *testing.T) { withKey(t, "test master key") sealed, err := Seal("t.col", "row-1", "hunter2") if err != nil { t.Fatal(err) } if !IsSealed(sealed) || strings.Contains(sealed, "hunter2") { t.Fatalf("sealed = %q, want an opaque v1. value", sealed) } plain, err := Open("t.col", "row-1", sealed) if err != nil || plain != "hunter2" { t.Fatalf("Open = %q, %v; want hunter2", plain, err) } again, _ := Seal("t.col", "row-1", "hunter2") if again == sealed { t.Error("two seals of one value are identical; the nonce is not random") } } func TestOpenRefusesAnythingElse(t *testing.T) { withKey(t, "test master key") sealed, _ := Seal("t.col", "row-1", "hunter2") cases := map[string]func() (string, error){ "another owner": func() (string, error) { return Open("t.col", "row-2", sealed) }, "another purpose": func() (string, error) { return Open("t.other", "row-1", sealed) }, "plain text": func() (string, error) { return Open("t.col", "row-1", "hunter2") }, "truncated": func() (string, error) { return Open("t.col", "row-1", sealed[:len(sealed)-4]) }, "not base64": func() (string, error) { return Open("t.col", "row-1", "v1.!!!") }, "too short": func() (string, error) { return Open("t.col", "row-1", "v1.AAAA") }, } for name, open := range cases { if _, err := open(); !errors.Is(err, ErrCorrupt) { t.Errorf("%s: err = %v, want ErrCorrupt", name, err) } } Install([]byte("a different key")) if _, err := Open("t.col", "row-1", sealed); !errors.Is(err, ErrCorrupt) { t.Errorf("another master key: err = %v, want ErrCorrupt", err) } } func TestEmptyStaysEmpty(t *testing.T) { withKey(t, "test master key") if s, err := Seal("t.col", "o", ""); s != "" || err != nil { t.Errorf("Seal(\"\") = %q, %v; want the empty string", s, err) } if s, err := Open("t.col", "o", ""); s != "" || err != nil { t.Errorf("Open(\"\") = %q, %v; want the empty string", s, err) } empty := "" if p, err := SealPtr("t.col", "o", &empty); p != nil || err != nil { t.Errorf("SealPtr(\"\") = %v, %v; want nil", p, err) } if p := RevealPtr("t.col", "o", nil); p != nil { t.Errorf("RevealPtr(nil) = %v, want nil", p) } } func TestNoKeyIsAnErrorNotAPanic(t *testing.T) { withKey(t, "") if _, err := Seal("t.col", "o", "x"); !errors.Is(err, ErrNoKey) { t.Errorf("Seal: err = %v, want ErrNoKey", err) } if _, err := Open("t.col", "o", "v1.AAAA"); !errors.Is(err, ErrNoKey) { t.Errorf("Open: err = %v, want ErrNoKey", err) } } func TestRevealReadsAnUnopenableValueAsUnset(t *testing.T) { withKey(t, "test master key") if got := Reveal("t.col", "o", "not sealed"); got != "" { t.Errorf("Reveal = %q, want the empty string", got) } bad := "not sealed" if got := RevealPtr("t.col", "o", &bad); got != nil { t.Errorf("RevealPtr = %q, want nil", *got) } } func TestEveryColumnHasADistinctPurpose(t *testing.T) { seen := map[string]bool{} for _, c := range Columns { if seen[c.Purpose()] { t.Errorf("%s is listed twice", c.Purpose()) } seen[c.Purpose()] = true } if !seen[AcquisitionAPIKey.Purpose()] || !seen[SubsonicPassword.Purpose()] { t.Error("Columns is missing a declared column") } }