package sigil import ( "crypto/md5" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/base64" "encoding/hex" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // --------------------------------------------------------------------------- // ReverseSigil // --------------------------------------------------------------------------- func TestReverseSigil_Good(t *testing.T) { s := &ReverseSigil{} out, err := s.In([]byte("hello")) require.NoError(t, err) assert.Equal(t, []byte("olleh"), out) // Symmetric: Out does the same thing. restored, err := s.Out(out) require.NoError(t, err) assert.Equal(t, []byte("hello"), restored) } func TestReverseSigil_Bad(t *testing.T) { s := &ReverseSigil{} // Empty input returns empty. out, err := s.In([]byte{}) require.NoError(t, err) assert.Equal(t, []byte{}, out) } func TestReverseSigil_Ugly(t *testing.T) { s := &ReverseSigil{} // Nil input returns nil. out, err := s.In(nil) require.NoError(t, err) assert.Nil(t, out) out, err = s.Out(nil) require.NoError(t, err) assert.Nil(t, out) } // --------------------------------------------------------------------------- // HexSigil // --------------------------------------------------------------------------- func TestHexSigil_Good(t *testing.T) { s := &HexSigil{} data := []byte("hello world") encoded, err := s.In(data) require.NoError(t, err) assert.Equal(t, []byte(hex.EncodeToString(data)), encoded) decoded, err := s.Out(encoded) require.NoError(t, err) assert.Equal(t, data, decoded) } func TestHexSigil_Bad(t *testing.T) { s := &HexSigil{} // Invalid hex input. _, err := s.Out([]byte("zzzz")) assert.Error(t, err) // Empty input. out, err := s.In([]byte{}) require.NoError(t, err) assert.Equal(t, []byte{}, out) } func TestHexSigil_Ugly(t *testing.T) { s := &HexSigil{} out, err := s.In(nil) require.NoError(t, err) assert.Nil(t, out) out, err = s.Out(nil) require.NoError(t, err) assert.Nil(t, out) } // --------------------------------------------------------------------------- // Base64Sigil // --------------------------------------------------------------------------- func TestBase64Sigil_Good(t *testing.T) { s := &Base64Sigil{} data := []byte("composable transforms") encoded, err := s.In(data) require.NoError(t, err) assert.Equal(t, []byte(base64.StdEncoding.EncodeToString(data)), encoded) decoded, err := s.Out(encoded) require.NoError(t, err) assert.Equal(t, data, decoded) } func TestBase64Sigil_Bad(t *testing.T) { s := &Base64Sigil{} // Invalid base64 (wrong padding). _, err := s.Out([]byte("!!!")) assert.Error(t, err) // Empty input. out, err := s.In([]byte{}) require.NoError(t, err) assert.Equal(t, []byte{}, out) } func TestBase64Sigil_Ugly(t *testing.T) { s := &Base64Sigil{} out, err := s.In(nil) require.NoError(t, err) assert.Nil(t, out) out, err = s.Out(nil) require.NoError(t, err) assert.Nil(t, out) } // --------------------------------------------------------------------------- // GzipSigil // --------------------------------------------------------------------------- func TestGzipSigil_Good(t *testing.T) { s := &GzipSigil{} data := []byte("the quick brown fox jumps over the lazy dog") compressed, err := s.In(data) require.NoError(t, err) assert.NotEqual(t, data, compressed) decompressed, err := s.Out(compressed) require.NoError(t, err) assert.Equal(t, data, decompressed) } func TestGzipSigil_Bad(t *testing.T) { s := &GzipSigil{} // Invalid gzip data. _, err := s.Out([]byte("not gzip")) assert.Error(t, err) // Empty input compresses to a valid gzip stream. compressed, err := s.In([]byte{}) require.NoError(t, err) assert.NotEmpty(t, compressed) // gzip header is always present decompressed, err := s.Out(compressed) require.NoError(t, err) assert.Equal(t, []byte{}, decompressed) } func TestGzipSigil_Ugly(t *testing.T) { s := &GzipSigil{} out, err := s.In(nil) require.NoError(t, err) assert.Nil(t, out) out, err = s.Out(nil) require.NoError(t, err) assert.Nil(t, out) } // --------------------------------------------------------------------------- // JSONSigil // --------------------------------------------------------------------------- func TestJSONSigil_Good(t *testing.T) { s := &JSONSigil{Indent: false} data := []byte(`{ "key" : "value" }`) compacted, err := s.In(data) require.NoError(t, err) assert.Equal(t, []byte(`{"key":"value"}`), compacted) // Out is passthrough. passthrough, err := s.Out(compacted) require.NoError(t, err) assert.Equal(t, compacted, passthrough) } func TestJSONSigil_Good_Indent(t *testing.T) { s := &JSONSigil{Indent: true} data := []byte(`{"key":"value"}`) indented, err := s.In(data) require.NoError(t, err) assert.Contains(t, string(indented), "\n") assert.Contains(t, string(indented), " ") } func TestJSONSigil_Bad(t *testing.T) { s := &JSONSigil{Indent: false} // Invalid JSON. _, err := s.In([]byte("not json")) assert.Error(t, err) } func TestJSONSigil_Ugly(t *testing.T) { s := &JSONSigil{Indent: false} // json.Compact on nil/empty will produce an error (invalid JSON). _, err := s.In(nil) assert.Error(t, err) // Out with nil is passthrough. out, err := s.Out(nil) require.NoError(t, err) assert.Nil(t, out) } // --------------------------------------------------------------------------- // HashSigil // --------------------------------------------------------------------------- func TestHashSigil_Good(t *testing.T) { data := []byte("hash me") tests := []struct { name string sigilName string size int }{ {"md5", "md5", md5.Size}, {"sha1", "sha1", sha1.Size}, {"sha256", "sha256", sha256.Size}, {"sha512", "sha512", sha512.Size}, {"sha224", "sha224", sha256.Size224}, {"sha384", "sha384", sha512.Size384}, {"sha512-224", "sha512-224", 28}, {"sha512-256", "sha512-256", 32}, {"sha3-224", "sha3-224", 28}, {"sha3-256", "sha3-256", 32}, {"sha3-384", "sha3-384", 48}, {"sha3-512", "sha3-512", 64}, {"ripemd160", "ripemd160", 20}, {"blake2s-256", "blake2s-256", 32}, {"blake2b-256", "blake2b-256", 32}, {"blake2b-384", "blake2b-384", 48}, {"blake2b-512", "blake2b-512", 64}, {"md4", "md4", 16}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, err := NewSigil(tt.sigilName) require.NoError(t, err) hashed, err := s.In(data) require.NoError(t, err) assert.Len(t, hashed, tt.size) // Out is passthrough. passthrough, err := s.Out(hashed) require.NoError(t, err) assert.Equal(t, hashed, passthrough) }) } } func TestHashSigil_Bad(t *testing.T) { // Unsupported hash constant. s := &HashSigil{Hash: 0} _, err := s.In([]byte("data")) assert.Error(t, err) assert.Contains(t, err.Error(), "not available") } func TestHashSigil_Ugly(t *testing.T) { // Hashing empty data should still produce a valid digest. s, err := NewSigil("sha256") require.NoError(t, err) hashed, err := s.In([]byte{}) require.NoError(t, err) assert.Len(t, hashed, sha256.Size) } // --------------------------------------------------------------------------- // NewSigil factory // --------------------------------------------------------------------------- func TestNewSigil_Good(t *testing.T) { names := []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 names { t.Run(name, func(t *testing.T) { s, err := NewSigil(name) require.NoError(t, err) assert.NotNil(t, s) }) } } func TestNewSigil_Bad(t *testing.T) { _, err := NewSigil("nonexistent") assert.Error(t, err) assert.Contains(t, err.Error(), "unknown sigil name") } func TestNewSigil_Ugly(t *testing.T) { _, err := NewSigil("") assert.Error(t, err) } // --------------------------------------------------------------------------- // Transmute / Untransmute // --------------------------------------------------------------------------- func TestTransmute_Good(t *testing.T) { data := []byte("round trip") hexSigil, err := NewSigil("hex") require.NoError(t, err) base64Sigil, err := NewSigil("base64") require.NoError(t, err) chain := []Sigil{hexSigil, base64Sigil} encoded, err := Transmute(data, chain) require.NoError(t, err) assert.NotEqual(t, data, encoded) decoded, err := Untransmute(encoded, chain) require.NoError(t, err) assert.Equal(t, data, decoded) } func TestTransmute_Good_MultiSigil(t *testing.T) { data := []byte("multi sigil pipeline test data") reverseSigil, err := NewSigil("reverse") require.NoError(t, err) hexSigil, err := NewSigil("hex") require.NoError(t, err) base64Sigil, err := NewSigil("base64") require.NoError(t, err) chain := []Sigil{reverseSigil, hexSigil, base64Sigil} encoded, err := Transmute(data, chain) require.NoError(t, err) decoded, err := Untransmute(encoded, chain) require.NoError(t, err) assert.Equal(t, data, decoded) } func TestTransmute_Good_GzipRoundTrip(t *testing.T) { data := []byte("compress then encode then decode then decompress") gzipSigil, err := NewSigil("gzip") require.NoError(t, err) hexSigil, err := NewSigil("hex") require.NoError(t, err) chain := []Sigil{gzipSigil, hexSigil} encoded, err := Transmute(data, chain) require.NoError(t, err) decoded, err := Untransmute(encoded, chain) require.NoError(t, err) assert.Equal(t, data, decoded) } func TestTransmute_Bad(t *testing.T) { // Transmute with a sigil that will fail: hex decode on non-hex input. hexSigil := &HexSigil{} // Calling Out (decode) with invalid input via manual chain. _, err := Untransmute([]byte("not-hex!!"), []Sigil{hexSigil}) assert.Error(t, err) } func TestTransmute_Ugly(t *testing.T) { // Empty sigil chain is a no-op. data := []byte("unchanged") result, err := Transmute(data, nil) require.NoError(t, err) assert.Equal(t, data, result) result, err = Untransmute(data, nil) require.NoError(t, err) assert.Equal(t, data, result) // Nil data through a chain. hexSigil, _ := NewSigil("hex") result, err = Transmute(nil, []Sigil{hexSigil}) require.NoError(t, err) assert.Nil(t, result) }