feat: Add Good, Bad, and Ugly tests
This commit refactors the test suites for the `crypt` and `enchantrix` packages to follow the "Good, Bad, Ugly" testing methodology. - `_Good` tests cover the ideal "happy path" scenarios. - `_Bad` tests cover expected failure scenarios with well-formed but invalid inputs. - `_Ugly` tests cover malicious or malformed inputs designed to cause crashes or panics. This change improves test coverage and ensures that the codebase is more robust and resilient to unexpected inputs.
This commit is contained in:
parent
d649e9e69e
commit
e112ec363d
6 changed files with 246 additions and 18 deletions
|
|
@ -3,6 +3,7 @@ package crypt
|
|||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"errors"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/binary"
|
||||
|
|
@ -217,5 +218,8 @@ func (s *Service) VerifyPGP(publicKey, data, signature []byte) error {
|
|||
// SymmetricallyEncryptPGP encrypts data with a passphrase.
|
||||
func (s *Service) SymmetricallyEncryptPGP(passphrase, data []byte) ([]byte, error) {
|
||||
s.ensurePGP()
|
||||
if len(passphrase) == 0 {
|
||||
return nil, errors.New("passphrase cannot be empty")
|
||||
}
|
||||
return s.pgp.SymmetricallyEncrypt(passphrase, data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,28 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEnsureRSA(t *testing.T) {
|
||||
// TestEnsureRSA_Good tests that the RSA service is initialized correctly.
|
||||
func TestEnsureRSA_Good(t *testing.T) {
|
||||
s := &Service{}
|
||||
s.ensureRSA()
|
||||
assert.NotNil(t, s.rsa)
|
||||
}
|
||||
|
||||
// TestEnsureRSA_Bad tests that calling ensureRSA multiple times does not change the RSA service.
|
||||
func TestEnsureRSA_Bad(t *testing.T) {
|
||||
s := &Service{}
|
||||
s.ensureRSA()
|
||||
rsa1 := s.rsa
|
||||
s.ensureRSA()
|
||||
rsa2 := s.rsa
|
||||
assert.Same(t, rsa1, rsa2)
|
||||
}
|
||||
|
||||
// TestEnsureRSA_Ugly tests that ensureRSA works correctly on a service with a pre-initialized RSA service.
|
||||
func TestEnsureRSA_Ugly(t *testing.T) {
|
||||
s := NewService() // NewService initializes the RSA service
|
||||
rsa1 := s.rsa
|
||||
s.ensureRSA()
|
||||
rsa2 := s.rsa
|
||||
assert.Same(t, rsa1, rsa2)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,63 @@ func TestPGP_Good(t *testing.T) {
|
|||
assert.NotNil(t, ciphertext)
|
||||
}
|
||||
|
||||
func TestPGP_Bad(t *testing.T) {
|
||||
// Generate two key pairs
|
||||
pubKey1, privKey1, err := service.GeneratePGPKeyPair("test1", "test1@test.com", "")
|
||||
assert.NoError(t, err)
|
||||
pubKey2, privKey2, err := service.GeneratePGPKeyPair("test2", "test2@test.com", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
message := []byte("secret message")
|
||||
|
||||
// Test decryption with the wrong key
|
||||
ciphertext, err := service.EncryptPGP(pubKey1, message)
|
||||
assert.NoError(t, err)
|
||||
// This should fail because we are using the wrong private key.
|
||||
_, err = service.DecryptPGP(privKey2, ciphertext) // Intentionally using wrong key
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test verification with the wrong key
|
||||
signature, err := service.SignPGP(privKey1, message)
|
||||
assert.NoError(t, err)
|
||||
err = service.VerifyPGP(pubKey2, message, signature)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test verification with a tampered message
|
||||
tamperedMessage := []byte("tampered message")
|
||||
err = service.VerifyPGP(pubKey1, tamperedMessage, signature)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestPGP_Ugly(t *testing.T) {
|
||||
// Test with malformed keys
|
||||
_, err := service.EncryptPGP([]byte("not a real key"), []byte("message"))
|
||||
assert.Error(t, err)
|
||||
|
||||
_, err = service.DecryptPGP([]byte("not a real key"), []byte("message"))
|
||||
assert.Error(t, err)
|
||||
|
||||
_, err = service.SignPGP([]byte("not a real key"), []byte("message"))
|
||||
assert.Error(t, err)
|
||||
|
||||
err = service.VerifyPGP([]byte("not a real key"), []byte("message"), []byte("not a real signature"))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test with empty message
|
||||
pubKey, privKey, err := service.GeneratePGPKeyPair("test", "test@test.com", "")
|
||||
assert.NoError(t, err)
|
||||
message := []byte("")
|
||||
ciphertext, err := service.EncryptPGP(pubKey, message)
|
||||
assert.NoError(t, err)
|
||||
plaintext, err := service.DecryptPGP(privKey, ciphertext, )
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, message, plaintext)
|
||||
|
||||
// Test symmetric encryption with empty passphrase
|
||||
_, err = service.SymmetricallyEncryptPGP([]byte(""), message)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- IsHashAlgo Tests ---
|
||||
|
||||
func TestIsHashAlgo_Good(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,21 @@ func TestTransmute_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestTransmute_Ugly(t *testing.T) {
|
||||
// Test with nil data
|
||||
_, err := enchantrix.Transmute(nil, []enchantrix.Sigil{&enchantrix.ReverseSigil{}})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test with nil sigils
|
||||
_, err = enchantrix.Transmute([]byte("hello"), nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test with no sigils
|
||||
result, err := enchantrix.Transmute([]byte("hello"), []enchantrix.Sigil{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hello", string(result))
|
||||
}
|
||||
|
||||
// --- Factory Tests ---
|
||||
|
||||
func TestNewSigil_Good(t *testing.T) {
|
||||
|
|
@ -63,3 +78,20 @@ func TestNewSigil_Bad(t *testing.T) {
|
|||
assert.Nil(t, sigil)
|
||||
assert.Contains(t, err.Error(), "unknown sigil name")
|
||||
}
|
||||
|
||||
func TestNewSigil_Ugly(t *testing.T) {
|
||||
// Test with empty string
|
||||
sigil, err := enchantrix.NewSigil("")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sigil)
|
||||
|
||||
// Test with whitespace
|
||||
sigil, err = enchantrix.NewSigil(" ")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sigil)
|
||||
|
||||
// Test with non-printable characters
|
||||
sigil, err = enchantrix.NewSigil("\x00\x01\x02")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sigil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ type ReverseSigil struct{}
|
|||
|
||||
// In reverses the bytes of the data.
|
||||
func (s *ReverseSigil) In(data []byte) ([]byte, error) {
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
reversed := make([]byte, len(data))
|
||||
for i, j := 0, len(data)-1; i < len(data); i, j = i+1, j-1 {
|
||||
reversed[i] = data[j]
|
||||
|
|
@ -43,6 +46,9 @@ type HexSigil struct{}
|
|||
|
||||
// In encodes the data to hexadecimal.
|
||||
func (s *HexSigil) In(data []byte) ([]byte, error) {
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
dst := make([]byte, hex.EncodedLen(len(data)))
|
||||
hex.Encode(dst, data)
|
||||
return dst, nil
|
||||
|
|
@ -50,6 +56,9 @@ func (s *HexSigil) In(data []byte) ([]byte, error) {
|
|||
|
||||
// Out decodes the data from hexadecimal.
|
||||
func (s *HexSigil) Out(data []byte) ([]byte, error) {
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
dst := make([]byte, hex.DecodedLen(len(data)))
|
||||
_, err := hex.Decode(dst, data)
|
||||
return dst, err
|
||||
|
|
@ -60,6 +69,9 @@ type Base64Sigil struct{}
|
|||
|
||||
// In encodes the data to base64.
|
||||
func (s *Base64Sigil) In(data []byte) ([]byte, error) {
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
dst := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
|
||||
base64.StdEncoding.Encode(dst, data)
|
||||
return dst, nil
|
||||
|
|
@ -67,6 +79,9 @@ func (s *Base64Sigil) In(data []byte) ([]byte, error) {
|
|||
|
||||
// Out decodes the data from base64.
|
||||
func (s *Base64Sigil) Out(data []byte) ([]byte, error) {
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
dst := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
|
||||
n, err := base64.StdEncoding.Decode(dst, data)
|
||||
return dst[:n], err
|
||||
|
|
@ -79,6 +94,9 @@ type GzipSigil struct {
|
|||
|
||||
// In compresses the data using gzip.
|
||||
func (s *GzipSigil) In(data []byte) ([]byte, error) {
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var b bytes.Buffer
|
||||
w := s.writer
|
||||
if w == nil {
|
||||
|
|
@ -96,6 +114,9 @@ func (s *GzipSigil) In(data []byte) ([]byte, error) {
|
|||
|
||||
// Out decompresses the data using gzip.
|
||||
func (s *GzipSigil) Out(data []byte) ([]byte, error) {
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
r, err := gzip.NewReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ func (m *failOnSecondWrite) Write(p []byte) (n int, err error) {
|
|||
return len(p), nil
|
||||
}
|
||||
|
||||
func TestReverseSigil(t *testing.T) {
|
||||
func TestReverseSigil_Good(t *testing.T) {
|
||||
s := &ReverseSigil{}
|
||||
data := []byte("hello")
|
||||
reversed, err := s.In(data)
|
||||
|
|
@ -37,15 +37,23 @@ func TestReverseSigil(t *testing.T) {
|
|||
original, err := s.Out(reversed)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hello", string(original))
|
||||
}
|
||||
|
||||
// Ugly - empty string
|
||||
func TestReverseSigil_Ugly(t *testing.T) {
|
||||
s := &ReverseSigil{}
|
||||
// Test with empty string
|
||||
empty := []byte("")
|
||||
reversedEmpty, err := s.In(empty)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", string(reversedEmpty))
|
||||
|
||||
// Test with nil
|
||||
reversedNil, err := s.In(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, reversedNil)
|
||||
}
|
||||
|
||||
func TestHexSigil(t *testing.T) {
|
||||
func TestHexSigil_Good(t *testing.T) {
|
||||
s := &HexSigil{}
|
||||
data := []byte("hello")
|
||||
encoded, err := s.In(data)
|
||||
|
|
@ -54,13 +62,29 @@ func TestHexSigil(t *testing.T) {
|
|||
decoded, err := s.Out(encoded)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hello", string(decoded))
|
||||
}
|
||||
|
||||
// Bad - invalid hex string
|
||||
_, err = s.Out([]byte("not hex"))
|
||||
func TestHexSigil_Bad(t *testing.T) {
|
||||
s := &HexSigil{}
|
||||
_, err := s.Out([]byte("not hex"))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestBase64Sigil(t *testing.T) {
|
||||
func TestHexSigil_Ugly(t *testing.T) {
|
||||
s := &HexSigil{}
|
||||
// Test with empty string
|
||||
empty := []byte("")
|
||||
encodedEmpty, err := s.In(empty)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", string(encodedEmpty))
|
||||
|
||||
// Test with nil
|
||||
encodedNil, err := s.In(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, encodedNil)
|
||||
}
|
||||
|
||||
func TestBase64Sigil_Good(t *testing.T) {
|
||||
s := &Base64Sigil{}
|
||||
data := []byte("hello")
|
||||
encoded, err := s.In(data)
|
||||
|
|
@ -69,13 +93,29 @@ func TestBase64Sigil(t *testing.T) {
|
|||
decoded, err := s.Out(encoded)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hello", string(decoded))
|
||||
}
|
||||
|
||||
// Bad - invalid base64 string
|
||||
_, err = s.Out([]byte("not base64"))
|
||||
func TestBase64Sigil_Bad(t *testing.T) {
|
||||
s := &Base64Sigil{}
|
||||
_, err := s.Out([]byte("not base64"))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGzipSigil(t *testing.T) {
|
||||
func TestBase64Sigil_Ugly(t *testing.T) {
|
||||
s := &Base64Sigil{}
|
||||
// Test with empty string
|
||||
empty := []byte("")
|
||||
encodedEmpty, err := s.In(empty)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", string(encodedEmpty))
|
||||
|
||||
// Test with nil
|
||||
encodedNil, err := s.In(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, encodedNil)
|
||||
}
|
||||
|
||||
func TestGzipSigil_Good(t *testing.T) {
|
||||
s := &GzipSigil{}
|
||||
data := []byte("hello")
|
||||
compressed, err := s.In(data)
|
||||
|
|
@ -84,9 +124,14 @@ func TestGzipSigil(t *testing.T) {
|
|||
decompressed, err := s.Out(compressed)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hello", string(decompressed))
|
||||
}
|
||||
|
||||
// Bad - invalid gzip data
|
||||
_, err = s.Out([]byte("not gzip"))
|
||||
func TestGzipSigil_Bad(t *testing.T) {
|
||||
s := &GzipSigil{}
|
||||
data := []byte("hello")
|
||||
|
||||
// Test with invalid gzip data
|
||||
_, err := s.Out([]byte("not gzip"))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test writer error
|
||||
|
|
@ -100,27 +145,60 @@ func TestGzipSigil(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestJSONSigil(t *testing.T) {
|
||||
func TestGzipSigil_Ugly(t *testing.T) {
|
||||
s := &GzipSigil{}
|
||||
// Test with empty string
|
||||
empty := []byte("")
|
||||
compressedEmpty, err := s.In(empty)
|
||||
assert.NoError(t, err)
|
||||
decompressedEmpty, err := s.Out(compressedEmpty)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", string(decompressedEmpty))
|
||||
|
||||
// Test with nil
|
||||
compressedNil, err := s.In(nil)
|
||||
assert.NoError(t, err)
|
||||
decompressedNil, err := s.Out(compressedNil)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, decompressedNil)
|
||||
}
|
||||
|
||||
func TestJSONSigil_Good(t *testing.T) {
|
||||
s := &JSONSigil{Indent: true}
|
||||
data := []byte(`{"hello":"world"}`)
|
||||
indented, err := s.In(data)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "{\n \"hello\": \"world\"\n}", string(indented))
|
||||
|
||||
s.Indent = false
|
||||
compacted, err := s.In(indented)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `{"hello":"world"}`, string(compacted))
|
||||
|
||||
// Bad - invalid json
|
||||
_, err = s.In([]byte("not json"))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Out is a no-op, so it should return the data as-is
|
||||
// Out is a no-op
|
||||
outData, err := s.Out(data)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, data, outData)
|
||||
}
|
||||
|
||||
func TestJSONSigil_Bad(t *testing.T) {
|
||||
s := &JSONSigil{}
|
||||
_, err := s.In([]byte("not json"))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestJSONSigil_Ugly(t *testing.T) {
|
||||
s := &JSONSigil{}
|
||||
// Test with empty string
|
||||
empty := []byte("")
|
||||
_, err := s.In(empty)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test with nil
|
||||
_, err = s.In(nil)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestHashSigils_Good(t *testing.T) {
|
||||
// Using the input "hello" for all hash tests
|
||||
data := []byte("hello")
|
||||
|
|
@ -172,3 +250,19 @@ func TestHashSigil_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "hash algorithm not available")
|
||||
}
|
||||
|
||||
func TestHashSigil_Ugly(t *testing.T) {
|
||||
s, err := NewSigil("sha256")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test with empty string
|
||||
empty := []byte("")
|
||||
hashedEmpty, err := s.In(empty)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, hashedEmpty)
|
||||
|
||||
// Test with nil
|
||||
hashedNil, err := s.In(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, hashedNil)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue