Merge pull request #23 from Snider/feature-add-tdd-tests
feat: Add comprehensive TDD tests for crypt package
This commit is contained in:
commit
8e9a7d71fa
10 changed files with 582 additions and 476 deletions
|
|
@ -182,14 +182,14 @@ func demoRSA() {
|
||||||
// 2. Encrypt a message
|
// 2. Encrypt a message
|
||||||
message := []byte("This is a secret message for RSA.")
|
message := []byte("This is a secret message for RSA.")
|
||||||
fmt.Printf("\nOriginal message: %s\n", message)
|
fmt.Printf("\nOriginal message: %s\n", message)
|
||||||
ciphertext, err := cryptService.EncryptRSA(publicKey, message)
|
ciphertext, err := cryptService.EncryptRSA(publicKey, message, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to encrypt with RSA: %v", err)
|
log.Fatalf("Failed to encrypt with RSA: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Encrypted ciphertext (base64): %s\n", base64.StdEncoding.EncodeToString(ciphertext))
|
fmt.Printf("Encrypted ciphertext (base64): %s\n", base64.StdEncoding.EncodeToString(ciphertext))
|
||||||
|
|
||||||
// 3. Decrypt the message
|
// 3. Decrypt the message
|
||||||
decrypted, err := cryptService.DecryptRSA(privateKey, ciphertext)
|
decrypted, err := cryptService.DecryptRSA(privateKey, ciphertext, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to decrypt with RSA: %v", err)
|
log.Fatalf("Failed to decrypt with RSA: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
package crypt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
var service = NewService()
|
|
||||||
|
|
||||||
// --- Hashing Tests ---
|
|
||||||
|
|
||||||
func TestHash_Good(t *testing.T) {
|
|
||||||
payload := "hello"
|
|
||||||
// Test all supported hash types
|
|
||||||
for _, hashType := range []HashType{LTHN, SHA512, SHA256, SHA1, MD5} {
|
|
||||||
hash := service.Hash(hashType, payload)
|
|
||||||
assert.NotEmpty(t, hash, "Hash should not be empty for type %s", hashType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHash_Bad(t *testing.T) {
|
|
||||||
// Using an unsupported hash type should default to SHA256
|
|
||||||
hash := service.Hash("unsupported", "hello")
|
|
||||||
expectedHash := service.Hash(SHA256, "hello")
|
|
||||||
assert.Equal(t, expectedHash, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHash_Ugly(t *testing.T) {
|
|
||||||
// Test with potentially problematic inputs
|
|
||||||
testCases := []string{
|
|
||||||
"", // Empty string
|
|
||||||
" ", // Whitespace
|
|
||||||
"\x00\x01\x02\x03\x04", // Null bytes
|
|
||||||
strings.Repeat("a", 1024*1024), // Large payload (1MB)
|
|
||||||
"こんにちは", // Unicode characters
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
for _, hashType := range []HashType{LTHN, SHA512, SHA256, SHA1, MD5} {
|
|
||||||
hash := service.Hash(hashType, tc)
|
|
||||||
assert.NotEmpty(t, hash, "Hash for ugly input should not be empty for type %s", hashType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Checksum Tests ---
|
|
||||||
|
|
||||||
// Luhn Tests
|
|
||||||
func TestLuhn_Good(t *testing.T) {
|
|
||||||
assert.True(t, service.Luhn("79927398713"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLuhn_Bad(t *testing.T) {
|
|
||||||
assert.False(t, service.Luhn("79927398714"), "Should fail for incorrect checksum")
|
|
||||||
assert.False(t, service.Luhn("7992739871a"), "Should fail for non-numeric input")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLuhn_Ugly(t *testing.T) {
|
|
||||||
assert.False(t, service.Luhn(""), "Should be false for empty string")
|
|
||||||
assert.False(t, service.Luhn(" 1 2 3 "), "Should handle spaces but result in false")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fletcher16 Tests
|
|
||||||
func TestFletcher16_Good(t *testing.T) {
|
|
||||||
assert.Equal(t, uint16(0xC8F0), service.Fletcher16("abcde"))
|
|
||||||
assert.Equal(t, uint16(0x2057), service.Fletcher16("abcdef"))
|
|
||||||
assert.Equal(t, uint16(0x0627), service.Fletcher16("abcdefgh"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFletcher16_Bad(t *testing.T) {
|
|
||||||
// No obviously "bad" inputs that don't fall into "ugly"
|
|
||||||
// For Fletcher, any string is a valid input.
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFletcher16_Ugly(t *testing.T) {
|
|
||||||
assert.Equal(t, uint16(0), service.Fletcher16(""), "Checksum of empty string should be 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fletcher32 Tests
|
|
||||||
func TestFletcher32_Good(t *testing.T) {
|
|
||||||
assert.Equal(t, uint32(0xF04FC729), service.Fletcher32("abcde"))
|
|
||||||
assert.Equal(t, uint32(0x56502D2A), service.Fletcher32("abcdef"))
|
|
||||||
assert.Equal(t, uint32(0xEBE19591), service.Fletcher32("abcdefgh"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFletcher32_Bad(t *testing.T) {
|
|
||||||
// Any string is a valid input.
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFletcher32_Ugly(t *testing.T) {
|
|
||||||
assert.Equal(t, uint32(0), service.Fletcher32(""), "Checksum of empty string should be 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fletcher64 Tests
|
|
||||||
func TestFletcher64_Good(t *testing.T) {
|
|
||||||
assert.Equal(t, uint64(0xc8c6c527646362c6), service.Fletcher64("abcde"))
|
|
||||||
assert.Equal(t, uint64(0xc8c72b276463c8c6), service.Fletcher64("abcdef"))
|
|
||||||
assert.Equal(t, uint64(0x312e2b28cccac8c6), service.Fletcher64("abcdefgh"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFletcher64_Bad(t *testing.T) {
|
|
||||||
// Any string is a valid input.
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFletcher64_Ugly(t *testing.T) {
|
|
||||||
assert.Equal(t, uint64(0), service.Fletcher64(""), "Checksum of empty string should be 0")
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package enchantrix_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Snider/Enchantrix/pkg/enchantrix"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTransmute(t *testing.T) {
|
|
||||||
data := []byte("hello")
|
|
||||||
sigils := []enchantrix.Sigil{
|
|
||||||
&enchantrix.ReverseSigil{},
|
|
||||||
&enchantrix.HexSigil{},
|
|
||||||
}
|
|
||||||
result, err := enchantrix.Transmute(data, sigils)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "6f6c6c6568", string(result))
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
package enchantrix_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Snider/Enchantrix/pkg/enchantrix"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewSigil(t *testing.T) {
|
|
||||||
t.Run("ValidSigils", func(t *testing.T) {
|
|
||||||
validNames := []string{
|
|
||||||
"reverse", "hex", "base64", "gzip", "json", "json-indent",
|
|
||||||
"md4", "md5", "sha1", "sha224", "sha256", "sha384", "sha512",
|
|
||||||
"ripemd160", "sha3-224", "sha3-256", "sha3-384", "sha3-512",
|
|
||||||
"sha512-224", "sha512-256", "blake2s-256", "blake2b-256",
|
|
||||||
"blake2b-384", "blake2b-512",
|
|
||||||
}
|
|
||||||
for _, name := range validNames {
|
|
||||||
sigil, err := enchantrix.NewSigil(name)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, sigil)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidSigil", func(t *testing.T) {
|
|
||||||
sigil, err := enchantrix.NewSigil("invalid-sigil-name")
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Nil(t, sigil)
|
|
||||||
assert.Contains(t, err.Error(), "unknown sigil name")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
package enchantrix_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Snider/Enchantrix/pkg/enchantrix"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestReverseSigil(t *testing.T) {
|
|
||||||
s := &enchantrix.ReverseSigil{}
|
|
||||||
data := []byte("hello")
|
|
||||||
reversed, err := s.In(data)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "olleh", string(reversed))
|
|
||||||
original, err := s.Out(reversed)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "hello", string(original))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexSigil(t *testing.T) {
|
|
||||||
s := &enchantrix.HexSigil{}
|
|
||||||
data := []byte("hello")
|
|
||||||
encoded, err := s.In(data)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "68656c6c6f", string(encoded))
|
|
||||||
decoded, err := s.Out(encoded)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "hello", string(decoded))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBase64Sigil(t *testing.T) {
|
|
||||||
s := &enchantrix.Base64Sigil{}
|
|
||||||
data := []byte("hello")
|
|
||||||
encoded, err := s.In(data)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "aGVsbG8=", string(encoded))
|
|
||||||
decoded, err := s.Out(encoded)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "hello", string(decoded))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGzipSigil(t *testing.T) {
|
|
||||||
s := &enchantrix.GzipSigil{}
|
|
||||||
data := []byte("hello")
|
|
||||||
compressed, err := s.In(data)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotEqual(t, data, compressed)
|
|
||||||
decompressed, err := s.Out(compressed)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "hello", string(decompressed))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJSONSigil(t *testing.T) {
|
|
||||||
s := &enchantrix.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))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHashSigil(t *testing.T) {
|
|
||||||
s := enchantrix.NewHashSigil(crypto.SHA256)
|
|
||||||
data := []byte("hello")
|
|
||||||
hashed, err := s.In(data)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
expectedHash := "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
|
|
||||||
assert.Equal(t, expectedHash, hex.EncodeToString(hashed))
|
|
||||||
unhashed, err := s.Out(hashed)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, hashed, unhashed)
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
package trix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func FuzzDecode(f *testing.F) {
|
|
||||||
// Seed with a valid encoded Trix object
|
|
||||||
validTrix := &Trix{
|
|
||||||
Header: map[string]interface{}{"content_type": "text/plain"},
|
|
||||||
Payload: []byte("hello world"),
|
|
||||||
}
|
|
||||||
validEncoded, _ := Encode(validTrix, "FUZZ")
|
|
||||||
f.Add(validEncoded)
|
|
||||||
|
|
||||||
// Seed with the corrupted header length from the ugly test
|
|
||||||
var buf []byte
|
|
||||||
buf = append(buf, []byte("UGLY")...)
|
|
||||||
buf = append(buf, byte(Version))
|
|
||||||
buf = append(buf, []byte{0, 0, 3, 232}...) // BigEndian representation of 1000
|
|
||||||
buf = append(buf, []byte("{}")...)
|
|
||||||
buf = append(buf, []byte("payload")...)
|
|
||||||
f.Add(buf)
|
|
||||||
|
|
||||||
// Seed with a short, invalid input
|
|
||||||
f.Add([]byte("short"))
|
|
||||||
|
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
|
||||||
// The fuzzer will generate random data here.
|
|
||||||
// We just need to call our function and make sure it doesn't panic.
|
|
||||||
// The fuzzer will report any crashes as failures.
|
|
||||||
_, _ = Decode(data, "FUZZ")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,202 +0,0 @@
|
||||||
package trix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Snider/Enchantrix/pkg/crypt"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestTrixEncodeDecode_Good tests the ideal "happy path" scenario for encoding and decoding.
|
|
||||||
func TestTrixEncodeDecode_Good(t *testing.T) {
|
|
||||||
header := map[string]interface{}{
|
|
||||||
"content_type": "application/octet-stream",
|
|
||||||
"encryption_algorithm": "chacha20poly1035",
|
|
||||||
"nonce": "AAECAwQFBgcICQoLDA0ODxAREhMUFRY=",
|
|
||||||
"created_at": "2025-10-30T12:00:00Z",
|
|
||||||
}
|
|
||||||
payload := []byte("This is a secret message.")
|
|
||||||
trix := &Trix{Header: header, Payload: payload}
|
|
||||||
magicNumber := "TRIX"
|
|
||||||
|
|
||||||
encoded, err := Encode(trix, magicNumber)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
decoded, err := Decode(encoded, magicNumber)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
assert.True(t, reflect.DeepEqual(trix.Header, decoded.Header))
|
|
||||||
assert.Equal(t, trix.Payload, decoded.Payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestTrixEncodeDecode_Bad tests expected failure scenarios with well-formed but invalid inputs.
|
|
||||||
func TestTrixEncodeDecode_Bad(t *testing.T) {
|
|
||||||
t.Run("MismatchedMagicNumber", func(t *testing.T) {
|
|
||||||
trix := &Trix{Header: map[string]interface{}{}, Payload: []byte("payload")}
|
|
||||||
encoded, err := Encode(trix, "GOOD")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = Decode(encoded, "BAD!")
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "invalid magic number")
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidMagicNumberLength", func(t *testing.T) {
|
|
||||||
trix := &Trix{Header: map[string]interface{}{}, Payload: []byte("payload")}
|
|
||||||
_, err := Encode(trix, "TOOLONG")
|
|
||||||
assert.EqualError(t, err, "trix: magic number must be 4 bytes long")
|
|
||||||
|
|
||||||
_, err = Decode([]byte{}, "SHORT")
|
|
||||||
assert.EqualError(t, err, "trix: magic number must be 4 bytes long")
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("MalformedHeaderJSON", func(t *testing.T) {
|
|
||||||
// Create a Trix struct with a header that cannot be marshaled to JSON.
|
|
||||||
header := map[string]interface{}{
|
|
||||||
"unsupported": make(chan int), // Channels cannot be JSON-encoded
|
|
||||||
}
|
|
||||||
trix := &Trix{Header: header, Payload: []byte("payload")}
|
|
||||||
_, err := Encode(trix, "TRIX")
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "json: unsupported type")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestTrixEncodeDecode_Ugly tests malicious or malformed inputs designed to cause crashes or panics.
|
|
||||||
func TestTrixEncodeDecode_Ugly(t *testing.T) {
|
|
||||||
magicNumber := "UGLY"
|
|
||||||
|
|
||||||
t.Run("CorruptedHeaderLength", func(t *testing.T) {
|
|
||||||
// Manually construct a byte slice where the header length is larger than the actual data.
|
|
||||||
var buf []byte
|
|
||||||
buf = append(buf, []byte(magicNumber)...) // Magic Number
|
|
||||||
buf = append(buf, byte(Version)) // Version
|
|
||||||
// Header length of 1000, but the header is only 2 bytes long.
|
|
||||||
buf = append(buf, []byte{0, 0, 3, 232}...) // BigEndian representation of 1000
|
|
||||||
buf = append(buf, []byte("{}")...) // A minimal valid JSON header
|
|
||||||
buf = append(buf, []byte("payload")...)
|
|
||||||
|
|
||||||
_, err := Decode(buf, magicNumber)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Equal(t, err, io.ErrUnexpectedEOF)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("DataTooShort", func(t *testing.T) {
|
|
||||||
// Data is too short to contain even the magic number.
|
|
||||||
data := []byte("BAD")
|
|
||||||
_, err := Decode(data, magicNumber)
|
|
||||||
assert.Error(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("EmptyPayload", func(t *testing.T) {
|
|
||||||
data := []byte{}
|
|
||||||
_, err := Decode(data, magicNumber)
|
|
||||||
assert.Error(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("FuzzedJSON", func(t *testing.T) {
|
|
||||||
// A header that is technically valid but contains unexpected types.
|
|
||||||
header := map[string]interface{}{
|
|
||||||
"payload": map[string]interface{}{"nested": 123},
|
|
||||||
}
|
|
||||||
payload := []byte("some data")
|
|
||||||
trix := &Trix{Header: header, Payload: payload}
|
|
||||||
|
|
||||||
encoded, err := Encode(trix, magicNumber)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
decoded, err := Decode(encoded, magicNumber)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, decoded)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Sigil Tests ---
|
|
||||||
|
|
||||||
func TestPackUnpack_Good(t *testing.T) {
|
|
||||||
originalPayload := []byte("hello world")
|
|
||||||
trix := &Trix{
|
|
||||||
Header: map[string]interface{}{},
|
|
||||||
Payload: originalPayload,
|
|
||||||
InSigils: []string{"reverse", "reverse"}, // Double reverse should be original
|
|
||||||
}
|
|
||||||
|
|
||||||
err := trix.Pack()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, originalPayload, trix.Payload) // Should be back to the original
|
|
||||||
|
|
||||||
err = trix.Unpack()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, originalPayload, trix.Payload) // Should be back to the original again
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPackUnpack_Bad(t *testing.T) {
|
|
||||||
trix := &Trix{
|
|
||||||
Header: map[string]interface{}{},
|
|
||||||
Payload: []byte("some data"),
|
|
||||||
InSigils: []string{"reverse", "invalid-sigil-name"},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := trix.Pack()
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "unknown sigil name")
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Checksum Tests ---
|
|
||||||
|
|
||||||
func TestChecksum_Good(t *testing.T) {
|
|
||||||
trix := &Trix{
|
|
||||||
Header: map[string]interface{}{},
|
|
||||||
Payload: []byte("hello world"),
|
|
||||||
ChecksumAlgo: crypt.SHA256,
|
|
||||||
}
|
|
||||||
encoded, err := Encode(trix, "CHCK")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
decoded, err := Decode(encoded, "CHCK")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, trix.Payload, decoded.Payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChecksum_Bad(t *testing.T) {
|
|
||||||
trix := &Trix{
|
|
||||||
Header: map[string]interface{}{},
|
|
||||||
Payload: []byte("hello world"),
|
|
||||||
ChecksumAlgo: crypt.SHA256,
|
|
||||||
}
|
|
||||||
encoded, err := Encode(trix, "CHCK")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Tamper with the payload
|
|
||||||
encoded[len(encoded)-1] = 0
|
|
||||||
|
|
||||||
_, err = Decode(encoded, "CHCK")
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Equal(t, ErrChecksumMismatch, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChecksum_Ugly(t *testing.T) {
|
|
||||||
t.Run("MissingAlgoInHeader", func(t *testing.T) {
|
|
||||||
trix := &Trix{
|
|
||||||
Header: map[string]interface{}{},
|
|
||||||
Payload: []byte("hello world"),
|
|
||||||
ChecksumAlgo: crypt.SHA256,
|
|
||||||
}
|
|
||||||
encoded, err := Encode(trix, "UGLY")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Manually decode to tamper with the header
|
|
||||||
decoded, err := Decode(encoded, "UGLY")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
delete(decoded.Header, "checksum_algo")
|
|
||||||
|
|
||||||
// Re-encode with the tampered header
|
|
||||||
tamperedEncoded, err := Encode(decoded, "UGLY")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = Decode(tamperedEncoded, "UGLY")
|
|
||||||
assert.Error(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
169
tdd/crypt/crypt_test.go
Normal file
169
tdd/crypt/crypt_test.go
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
package crypt_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Snider/Enchantrix/pkg/crypt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var service = crypt.NewService()
|
||||||
|
|
||||||
|
// --- Hashing Tests ---
|
||||||
|
|
||||||
|
func TestHash_Good(t *testing.T) {
|
||||||
|
payload := "hello"
|
||||||
|
// Test all supported hash types
|
||||||
|
for _, hashType := range []crypt.HashType{crypt.LTHN, crypt.SHA512, crypt.SHA256, crypt.SHA1, crypt.MD5} {
|
||||||
|
hash := service.Hash(hashType, payload)
|
||||||
|
assert.NotEmpty(t, hash, "Hash should not be empty for type %s", hashType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHash_Bad(t *testing.T) {
|
||||||
|
// Using an unsupported hash type should default to SHA256
|
||||||
|
hash := service.Hash("unsupported", "hello")
|
||||||
|
expectedHash := service.Hash(crypt.SHA256, "hello")
|
||||||
|
assert.Equal(t, expectedHash, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHash_Ugly(t *testing.T) {
|
||||||
|
// Test with potentially problematic inputs
|
||||||
|
testCases := []string{
|
||||||
|
"", // Empty string
|
||||||
|
" ", // Whitespace
|
||||||
|
"\x00\x01\x02\x03\x04", // Null bytes
|
||||||
|
strings.Repeat("a", 1024*1024), // Large payload (1MB)
|
||||||
|
"こんにちは", // Unicode characters
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
for _, hashType := range []crypt.HashType{crypt.LTHN, crypt.SHA512, crypt.SHA256, crypt.SHA1, crypt.MD5} {
|
||||||
|
hash := service.Hash(hashType, tc)
|
||||||
|
assert.NotEmpty(t, hash, "Hash for ugly input should not be empty for type %s", hashType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Checksum Tests ---
|
||||||
|
|
||||||
|
// Luhn Tests
|
||||||
|
func TestLuhn_Good(t *testing.T) {
|
||||||
|
assert.True(t, service.Luhn("79927398713"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLuhn_Bad(t *testing.T) {
|
||||||
|
assert.False(t, service.Luhn("79927398714"), "Should fail for incorrect checksum")
|
||||||
|
assert.False(t, service.Luhn("7992739871a"), "Should fail for non-numeric input")
|
||||||
|
assert.False(t, service.Luhn("1"), "Should be false for single digit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLuhn_Ugly(t *testing.T) {
|
||||||
|
assert.False(t, service.Luhn(""), "Should be false for empty string")
|
||||||
|
assert.False(t, service.Luhn(" 1 2 3 "), "Should handle spaces but result in false")
|
||||||
|
assert.False(t, service.Luhn("!@#$%^&*()"), "Should be false for special characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fletcher16 Tests
|
||||||
|
func TestFletcher16_Good(t *testing.T) {
|
||||||
|
assert.Equal(t, uint16(0xC8F0), service.Fletcher16("abcde"))
|
||||||
|
assert.Equal(t, uint16(0x2057), service.Fletcher16("abcdef"))
|
||||||
|
assert.Equal(t, uint16(0x0627), service.Fletcher16("abcdefgh"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFletcher16_Ugly(t *testing.T) {
|
||||||
|
assert.Equal(t, uint16(0), service.Fletcher16(""), "Checksum of empty string should be 0")
|
||||||
|
assert.Equal(t, uint16(0), service.Fletcher16("\x00"), "Checksum of null byte should be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fletcher32 Tests
|
||||||
|
func TestFletcher32_Good(t *testing.T) {
|
||||||
|
assert.Equal(t, uint32(0xF04FC729), service.Fletcher32("abcde"))
|
||||||
|
assert.Equal(t, uint32(0x56502D2A), service.Fletcher32("abcdef"))
|
||||||
|
assert.Equal(t, uint32(0xEBE19591), service.Fletcher32("abcdefgh"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFletcher32_Ugly(t *testing.T) {
|
||||||
|
assert.Equal(t, uint32(0), service.Fletcher32(""), "Checksum of empty string should be 0")
|
||||||
|
// Test odd length string to check padding
|
||||||
|
assert.NotEqual(t, uint32(0), service.Fletcher32("a"), "Checksum of odd length string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fletcher64 Tests
|
||||||
|
func TestFletcher64_Good(t *testing.T) {
|
||||||
|
assert.Equal(t, uint64(0xc8c6c527646362c6), service.Fletcher64("abcde"))
|
||||||
|
assert.Equal(t, uint64(0xc8c72b276463c8c6), service.Fletcher64("abcdef"))
|
||||||
|
assert.Equal(t, uint64(0x312e2b28cccac8c6), service.Fletcher64("abcdefgh"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFletcher64_Ugly(t *testing.T) {
|
||||||
|
assert.Equal(t, uint64(0), service.Fletcher64(""), "Checksum of empty string should be 0")
|
||||||
|
// Test different length strings to check padding
|
||||||
|
assert.NotEqual(t, uint64(0), service.Fletcher64("a"), "Checksum of length 1 string")
|
||||||
|
assert.NotEqual(t, uint64(0), service.Fletcher64("ab"), "Checksum of length 2 string")
|
||||||
|
assert.NotEqual(t, uint64(0), service.Fletcher64("abc"), "Checksum of length 3 string")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- RSA Tests ---
|
||||||
|
|
||||||
|
func TestRSA_Good(t *testing.T) {
|
||||||
|
pubKey, privKey, err := service.GenerateRSAKeyPair(2048)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, pubKey)
|
||||||
|
assert.NotNil(t, privKey)
|
||||||
|
|
||||||
|
// Test encryption and decryption
|
||||||
|
message := []byte("secret message")
|
||||||
|
label := []byte("test label")
|
||||||
|
ciphertext, err := service.EncryptRSA(pubKey, message, label)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
plaintext, err := service.DecryptRSA(privKey, ciphertext, label)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, message, plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRSA_Bad(t *testing.T) {
|
||||||
|
// Test with a key size that is too small
|
||||||
|
_, _, err := service.GenerateRSAKeyPair(1024)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// Test decryption with the wrong key
|
||||||
|
pubKey, privKey, err := service.GenerateRSAKeyPair(2048)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, otherPrivKey, err := service.GenerateRSAKeyPair(2048)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
message := []byte("secret message")
|
||||||
|
ciphertext, err := service.EncryptRSA(pubKey, message, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = service.DecryptRSA(otherPrivKey, ciphertext, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// Test decryption with wrong label
|
||||||
|
label1 := []byte("label1")
|
||||||
|
label2 := []byte("label2")
|
||||||
|
ciphertext, err = service.EncryptRSA(pubKey, message, label1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = service.DecryptRSA(privKey, ciphertext, label2)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRSA_Ugly(t *testing.T) {
|
||||||
|
// Test with malformed keys
|
||||||
|
_, err := service.EncryptRSA([]byte("not a real key"), []byte("message"), nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
_, err = service.DecryptRSA([]byte("not a real key"), []byte("message"), nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// Test with empty message
|
||||||
|
pubKey, privKey, err := service.GenerateRSAKeyPair(2048)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
message := []byte("")
|
||||||
|
ciphertext, err := service.EncryptRSA(pubKey, message, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
plaintext, err := service.DecryptRSA(privKey, ciphertext, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, message, plaintext)
|
||||||
|
}
|
||||||
159
tdd/enchantrix/enchantrix_test.go
Normal file
159
tdd/enchantrix/enchantrix_test.go
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
package enchantrix_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Snider/Enchantrix/pkg/enchantrix"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- Transmute Tests ---
|
||||||
|
|
||||||
|
func TestTransmute_Good(t *testing.T) {
|
||||||
|
data := []byte("hello")
|
||||||
|
sigils := []enchantrix.Sigil{
|
||||||
|
&enchantrix.ReverseSigil{},
|
||||||
|
&enchantrix.HexSigil{},
|
||||||
|
}
|
||||||
|
result, err := enchantrix.Transmute(data, sigils)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "6f6c6c6568", string(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorSigil struct{}
|
||||||
|
|
||||||
|
func (s *errorSigil) In(data []byte) ([]byte, error) {
|
||||||
|
return nil, errors.New("sigil error")
|
||||||
|
}
|
||||||
|
func (s *errorSigil) Out(data []byte) ([]byte, error) {
|
||||||
|
return nil, errors.New("sigil error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTransmute_Bad(t *testing.T) {
|
||||||
|
data := []byte("hello")
|
||||||
|
sigils := []enchantrix.Sigil{
|
||||||
|
&enchantrix.ReverseSigil{},
|
||||||
|
&errorSigil{},
|
||||||
|
}
|
||||||
|
_, err := enchantrix.Transmute(data, sigils)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Factory Tests ---
|
||||||
|
|
||||||
|
func TestNewSigil_Good(t *testing.T) {
|
||||||
|
validNames := []string{
|
||||||
|
"reverse", "hex", "base64", "gzip", "json", "json-indent",
|
||||||
|
"md4", "md5", "sha1", "sha224", "sha256", "sha384", "sha512",
|
||||||
|
"ripemd160", "sha3-224", "sha3-256", "sha3-384", "sha3-512",
|
||||||
|
"sha512-224", "sha512-256", "blake2s-256", "blake2b-256",
|
||||||
|
"blake2b-384", "blake2b-512",
|
||||||
|
}
|
||||||
|
for _, name := range validNames {
|
||||||
|
sigil, err := enchantrix.NewSigil(name)
|
||||||
|
assert.NoError(t, err, "Failed to create sigil: %s", name)
|
||||||
|
assert.NotNil(t, sigil, "Sigil should not be nil for name: %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSigil_Bad(t *testing.T) {
|
||||||
|
sigil, err := enchantrix.NewSigil("invalid-sigil-name")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, sigil)
|
||||||
|
assert.Contains(t, err.Error(), "unknown sigil name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Sigil Tests ---
|
||||||
|
|
||||||
|
func TestReverseSigil(t *testing.T) {
|
||||||
|
s := &enchantrix.ReverseSigil{}
|
||||||
|
data := []byte("hello")
|
||||||
|
reversed, err := s.In(data)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "olleh", string(reversed))
|
||||||
|
original, err := s.Out(reversed)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "hello", string(original))
|
||||||
|
|
||||||
|
// Ugly - empty string
|
||||||
|
empty := []byte("")
|
||||||
|
reversedEmpty, err := s.In(empty)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "", string(reversedEmpty))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHexSigil(t *testing.T) {
|
||||||
|
s := &enchantrix.HexSigil{}
|
||||||
|
data := []byte("hello")
|
||||||
|
encoded, err := s.In(data)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "68656c6c6f", string(encoded))
|
||||||
|
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"))
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBase64Sigil(t *testing.T) {
|
||||||
|
s := &enchantrix.Base64Sigil{}
|
||||||
|
data := []byte("hello")
|
||||||
|
encoded, err := s.In(data)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "aGVsbG8=", string(encoded))
|
||||||
|
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"))
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGzipSigil(t *testing.T) {
|
||||||
|
s := &enchantrix.GzipSigil{}
|
||||||
|
data := []byte("hello")
|
||||||
|
compressed, err := s.In(data)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEqual(t, data, compressed)
|
||||||
|
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"))
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONSigil(t *testing.T) {
|
||||||
|
s := &enchantrix.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHashSigil(t *testing.T) {
|
||||||
|
s := enchantrix.NewHashSigil(crypto.SHA256)
|
||||||
|
data := []byte("hello")
|
||||||
|
hashed, err := s.In(data)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
expectedHash := "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
|
||||||
|
assert.Equal(t, expectedHash, hex.EncodeToString(hashed))
|
||||||
|
unhashed, err := s.Out(hashed)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, hashed, unhashed) // Out is a no-op
|
||||||
|
}
|
||||||
252
tdd/trix/trix_test.go
Normal file
252
tdd/trix/trix_test.go
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
package trix_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Snider/Enchantrix/pkg/crypt"
|
||||||
|
"github.com/Snider/Enchantrix/pkg/trix"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestTrixEncodeDecode_Good tests the ideal "happy path" scenario for encoding and decoding.
|
||||||
|
func TestTrixEncodeDecode_Good(t *testing.T) {
|
||||||
|
header := map[string]interface{}{
|
||||||
|
"content_type": "application/octet-stream",
|
||||||
|
"encryption_algorithm": "chacha20poly1035",
|
||||||
|
"nonce": "AAECAwQFBgcICQoLDA0ODxAREhMUFRY=",
|
||||||
|
"created_at": "2025-10-30T12:00:00Z",
|
||||||
|
}
|
||||||
|
payload := []byte("This is a secret message.")
|
||||||
|
trixOb := &trix.Trix{Header: header, Payload: payload}
|
||||||
|
magicNumber := "TRIX"
|
||||||
|
|
||||||
|
encoded, err := trix.Encode(trixOb, magicNumber)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
decoded, err := trix.Decode(encoded, magicNumber)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.True(t, reflect.DeepEqual(trixOb.Header, decoded.Header))
|
||||||
|
assert.Equal(t, trixOb.Payload, decoded.Payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTrixEncodeDecode_Bad tests expected failure scenarios with well-formed but invalid inputs.
|
||||||
|
func TestTrixEncodeDecode_Bad(t *testing.T) {
|
||||||
|
t.Run("MismatchedMagicNumber", func(t *testing.T) {
|
||||||
|
trixOb := &trix.Trix{Header: map[string]interface{}{}, Payload: []byte("payload")}
|
||||||
|
encoded, err := trix.Encode(trixOb, "GOOD")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = trix.Decode(encoded, "BAD!")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "invalid magic number")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("InvalidMagicNumberLength", func(t *testing.T) {
|
||||||
|
trixOb := &trix.Trix{Header: map[string]interface{}{}, Payload: []byte("payload")}
|
||||||
|
_, err := trix.Encode(trixOb, "TOOLONG")
|
||||||
|
assert.EqualError(t, err, "trix: magic number must be 4 bytes long")
|
||||||
|
|
||||||
|
_, err = trix.Decode([]byte{}, "SHORT")
|
||||||
|
assert.EqualError(t, err, "trix: magic number must be 4 bytes long")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("MalformedHeaderJSON", func(t *testing.T) {
|
||||||
|
// Create a Trix struct with a header that cannot be marshaled to JSON.
|
||||||
|
header := map[string]interface{}{
|
||||||
|
"unsupported": make(chan int), // Channels cannot be JSON-encoded
|
||||||
|
}
|
||||||
|
trixOb := &trix.Trix{Header: header, Payload: []byte("payload")}
|
||||||
|
_, err := trix.Encode(trixOb, "TRIX")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "json: unsupported type")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("HeaderTooLarge", func(t *testing.T) {
|
||||||
|
data := make([]byte, trix.MaxHeaderSize+10)
|
||||||
|
trixOb := &trix.Trix{
|
||||||
|
Header: map[string]interface{}{"large": string(data)},
|
||||||
|
Payload: []byte("payload"),
|
||||||
|
}
|
||||||
|
encoded, err := trix.Encode(trixOb, "TRIX")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = trix.Decode(encoded, "TRIX")
|
||||||
|
assert.ErrorIs(t, err, trix.ErrHeaderTooLarge)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTrixEncodeDecode_Ugly tests malicious or malformed inputs designed to cause crashes or panics.
|
||||||
|
func TestTrixEncodeDecode_Ugly(t *testing.T) {
|
||||||
|
magicNumber := "UGLY"
|
||||||
|
|
||||||
|
t.Run("CorruptedHeaderLength", func(t *testing.T) {
|
||||||
|
// Manually construct a byte slice where the header length is larger than the actual data.
|
||||||
|
var buf []byte
|
||||||
|
buf = append(buf, []byte(magicNumber)...) // Magic Number
|
||||||
|
buf = append(buf, byte(trix.Version)) // Version
|
||||||
|
buf = append(buf, []byte{0, 0, 3, 232}...) // BigEndian representation of 1000
|
||||||
|
buf = append(buf, []byte("{}")...) // A minimal valid JSON header
|
||||||
|
buf = append(buf, []byte("payload")...)
|
||||||
|
|
||||||
|
_, err := trix.Decode(buf, magicNumber)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, err, io.ErrUnexpectedEOF)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("DataTooShort", func(t *testing.T) {
|
||||||
|
data := []byte("BAD")
|
||||||
|
_, err := trix.Decode(data, magicNumber)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("EmptyPayload", func(t *testing.T) {
|
||||||
|
data := []byte{}
|
||||||
|
_, err := trix.Decode(data, magicNumber)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("FuzzedJSON", func(t *testing.T) {
|
||||||
|
header := map[string]interface{}{
|
||||||
|
"payload": map[string]interface{}{"nested": 123},
|
||||||
|
}
|
||||||
|
payload := []byte("some data")
|
||||||
|
trixOb := &trix.Trix{Header: header, Payload: payload}
|
||||||
|
|
||||||
|
encoded, err := trix.Encode(trixOb, magicNumber)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
decoded, err := trix.Decode(encoded, magicNumber)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, decoded)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Sigil Tests ---
|
||||||
|
|
||||||
|
func TestPackUnpack_Good(t *testing.T) {
|
||||||
|
originalPayload := []byte("hello world")
|
||||||
|
trixOb := &trix.Trix{
|
||||||
|
Header: map[string]interface{}{},
|
||||||
|
Payload: originalPayload,
|
||||||
|
InSigils: []string{"reverse", "reverse"}, // Double reverse should be original
|
||||||
|
}
|
||||||
|
|
||||||
|
err := trixOb.Pack()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, originalPayload, trixOb.Payload)
|
||||||
|
|
||||||
|
err = trixOb.Unpack()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, originalPayload, trixOb.Payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPackUnpack_Bad(t *testing.T) {
|
||||||
|
trixOb := &trix.Trix{
|
||||||
|
Header: map[string]interface{}{},
|
||||||
|
Payload: []byte("some data"),
|
||||||
|
InSigils: []string{"reverse", "invalid-sigil-name"},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := trixOb.Pack()
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "unknown sigil name")
|
||||||
|
|
||||||
|
trixOb.InSigils = []string{"hex"}
|
||||||
|
trixOb.Payload = []byte("not hex")
|
||||||
|
err = trixOb.Unpack()
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPackUnpack_Ugly(t *testing.T) {
|
||||||
|
trixOb := &trix.Trix{
|
||||||
|
Header: map[string]interface{}{},
|
||||||
|
Payload: nil, // Nil payload
|
||||||
|
InSigils: []string{"reverse"},
|
||||||
|
}
|
||||||
|
err := trixOb.Pack()
|
||||||
|
assert.NoError(t, err) // Should handle nil payload gracefully
|
||||||
|
|
||||||
|
err = trixOb.Unpack()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Checksum Tests ---
|
||||||
|
|
||||||
|
func TestChecksum_Good(t *testing.T) {
|
||||||
|
trixOb := &trix.Trix{
|
||||||
|
Header: map[string]interface{}{},
|
||||||
|
Payload: []byte("hello world"),
|
||||||
|
ChecksumAlgo: crypt.SHA256,
|
||||||
|
}
|
||||||
|
encoded, err := trix.Encode(trixOb, "CHCK")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
decoded, err := trix.Decode(encoded, "CHCK")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, trixOb.Payload, decoded.Payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChecksum_Bad(t *testing.T) {
|
||||||
|
trixOb := &trix.Trix{
|
||||||
|
Header: map[string]interface{}{},
|
||||||
|
Payload: []byte("hello world"),
|
||||||
|
ChecksumAlgo: crypt.SHA256,
|
||||||
|
}
|
||||||
|
encoded, err := trix.Encode(trixOb, "CHCK")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
encoded[len(encoded)-1] = 0 // Tamper with the payload
|
||||||
|
|
||||||
|
_, err = trix.Decode(encoded, "CHCK")
|
||||||
|
assert.ErrorIs(t, err, trix.ErrChecksumMismatch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChecksum_Ugly(t *testing.T) {
|
||||||
|
t.Run("MissingAlgoInHeader", func(t *testing.T) {
|
||||||
|
trixOb := &trix.Trix{
|
||||||
|
Header: map[string]interface{}{},
|
||||||
|
Payload: []byte("hello world"),
|
||||||
|
ChecksumAlgo: crypt.SHA256,
|
||||||
|
}
|
||||||
|
encoded, err := trix.Encode(trixOb, "UGLY")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
decoded, err := trix.Decode(encoded, "UGLY")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
delete(decoded.Header, "checksum_algo")
|
||||||
|
|
||||||
|
tamperedEncoded, err := trix.Encode(decoded, "UGLY")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = trix.Decode(tamperedEncoded, "UGLY")
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Fuzz Tests ---
|
||||||
|
|
||||||
|
func FuzzDecode(f *testing.F) {
|
||||||
|
validTrix := &trix.Trix{
|
||||||
|
Header: map[string]interface{}{"content_type": "text/plain"},
|
||||||
|
Payload: []byte("hello world"),
|
||||||
|
}
|
||||||
|
validEncoded, _ := trix.Encode(validTrix, "FUZZ")
|
||||||
|
f.Add(validEncoded)
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
|
buf = append(buf, []byte("UGLY")...)
|
||||||
|
buf = append(buf, byte(trix.Version))
|
||||||
|
buf = append(buf, []byte{0, 0, 3, 232}...)
|
||||||
|
buf = append(buf, []byte("{}")...)
|
||||||
|
buf = append(buf, []byte("payload")...)
|
||||||
|
f.Add(buf)
|
||||||
|
|
||||||
|
f.Add([]byte("short"))
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
_, _ = trix.Decode(data, "FUZZ")
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue