Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cipher/transposition): add fuzz test to transposition cipher #600 #601

Merged
merged 3 commits into from
Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 21 additions & 22 deletions cipher/transposition/transposition.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,16 @@
package transposition

import (
"errors"
"fmt"
"sort"
"strings"
)

type NoTextToEncryptError struct{}
type KeyMissingError struct{}
var ErrNoTextToEncrypt = errors.New("no text to encrypt")
var ErrKeyMissing = errors.New("missing Key")

func (n *NoTextToEncryptError) Error() string {
return "No text to encrypt"
}
func (n *KeyMissingError) Error() string {
return "Missing Key"
}
const placeholder = ' '

func getKey(keyWord string) []int {
keyWord = strings.ToLower(keyWord)
Expand Down Expand Up @@ -51,56 +48,58 @@ func getIndex(wordSet []rune, subString rune) int {
return 0
}

func Encrypt(text []rune, keyWord string) (string, error) {
func Encrypt(text []rune, keyWord string) ([]rune, error) {
key := getKey(keyWord)
space := ' '
keyLength := len(key)
textLength := len(text)
if keyLength <= 0 {
return "", &KeyMissingError{}
return nil, ErrKeyMissing
}
if textLength <= 0 {
return "", &NoTextToEncryptError{}
return nil, ErrNoTextToEncrypt
}
if text[len(text)-1] == placeholder {
return nil, fmt.Errorf("%w: cannot encrypt a text, %q, ending with the placeholder char %q", ErrNoTextToEncrypt, text, placeholder)
}
n := textLength % keyLength

for i := 0; i < keyLength-n; i++ {
text = append(text, space)
text = append(text, placeholder)
}
textLength = len(text)
result := ""
var result []rune
for i := 0; i < textLength; i += keyLength {
transposition := make([]rune, keyLength)
for j := 0; j < keyLength; j++ {
transposition[key[j]-1] = text[i+j]
}
result += string(transposition)
result = append(result, transposition...)
}
return result, nil
}

func Decrypt(text []rune, keyWord string) (string, error) {
func Decrypt(text []rune, keyWord string) ([]rune, error) {
key := getKey(keyWord)
textLength := len(text)
if textLength <= 0 {
return "", &NoTextToEncryptError{}
return nil, ErrNoTextToEncrypt
}
keyLength := len(key)
if keyLength <= 0 {
return "", &KeyMissingError{}
return nil, ErrKeyMissing
}
space := ' '
n := textLength % keyLength
for i := 0; i < keyLength-n; i++ {
text = append(text, space)
text = append(text, placeholder)
}
result := ""
var result []rune
for i := 0; i < textLength; i += keyLength {
transposition := make([]rune, keyLength)
for j := 0; j < keyLength; j++ {
transposition[j] = text[i+key[j]-1]
}
result += string(transposition)
result = append(result, transposition...)
}
result = []rune(strings.TrimRight(string(result), string(placeholder)))
return result, nil
}
84 changes: 57 additions & 27 deletions cipher/transposition/transposition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,18 @@ package transposition
import (
"errors"
"math/rand"
"strings"
"reflect"
"testing"
)

const enAlphabet = "abcdefghijklmnopqrstuvwxyz "
const enAlphabet = "abcdefghijklmnopqrstuvwxyz"

func getTexts() []string {
return []string{
"Ilya Sokolov",
"A slice literal is declared just like an array literal, except you leave out the element count",
"Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.",
"Go’s treatment of errors as values has served us well over the last decade. Although the standard library’s support for errors has been minimal—just the errors.New and fmt.Errorf functions, which produce errors that contain only a message—the built-in error interface allows Go programmers to add whatever information they desire. All it requires is a type that implements an Error method:",
"А тут для примера русский текст",
}
var texts = []string{
"Ilya Sokolov",
"A slice literal is declared just like an array literal, except you leave out the element count",
"Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.",
"Go’s treatment of errors as values has served us well over the last decade. Although the standard library’s support for errors has been minimal—just the errors.New and fmt.Errorf functions, which produce errors that contain only a message—the built-in error interface allows Go programmers to add whatever information they desire. All it requires is a type that implements an Error method:",
"А тут для примера русский текст",
}

func getRandomString() string {
Expand All @@ -36,12 +34,12 @@ func getRandomString() string {
func TestEncrypt(t *testing.T) {
fn := func(text string, keyWord string) (bool, error) {
encrypt, err := Encrypt([]rune(text), keyWord)
if err != nil && !errors.Is(err, &NoTextToEncryptError{}) && !errors.Is(err, &KeyMissingError{}) {
if err != nil && !errors.Is(err, ErrNoTextToEncrypt) && !errors.Is(err, ErrKeyMissing) {
t.Error("Unexpected error ", err)
}
return text == encrypt, err
return text == string(encrypt), err
}
for _, s := range getTexts() {
for _, s := range texts {
if check, err := fn(s, getRandomString()); check || err != nil {
t.Error("String ", s, " not encrypted")
}
Expand All @@ -52,52 +50,84 @@ func TestEncrypt(t *testing.T) {
}

func TestDecrypt(t *testing.T) {
for _, s := range getTexts() {
for _, s := range texts {
keyWord := getRandomString()
encrypt, errEncrypt := Encrypt([]rune(s), keyWord)
if errEncrypt != nil &&
!errors.Is(errEncrypt, &NoTextToEncryptError{}) &&
!errors.Is(errEncrypt, &KeyMissingError{}) {
!errors.Is(errEncrypt, ErrNoTextToEncrypt) &&
!errors.Is(errEncrypt, ErrKeyMissing) {
t.Error("Unexpected error ", errEncrypt)
}
if errEncrypt != nil {
t.Error(errEncrypt)
}
decrypt, errDecrypt := Decrypt([]rune(encrypt), keyWord)
if errDecrypt != nil &&
!errors.Is(errDecrypt, &NoTextToEncryptError{}) &&
!errors.Is(errDecrypt, &KeyMissingError{}) {
!errors.Is(errDecrypt, ErrNoTextToEncrypt) &&
!errors.Is(errDecrypt, ErrKeyMissing) {
t.Error("Unexpected error ", errDecrypt)
}
if errDecrypt != nil {
t.Error(errDecrypt)
}
if encrypt == decrypt {
if reflect.DeepEqual(encrypt, decrypt) {
t.Error("String ", s, " not encrypted")
}
if encrypt == s {
if reflect.DeepEqual(encrypt, s) {
t.Error("String ", s, " not encrypted")
}
}
}

func TestEncryptDecrypt(t *testing.T) {
text := "Test text for checking the algorithm"
text := []rune("Test text for checking the algorithm")
key1 := "testKey"
key2 := "Test Key2"
encrypt, errEncrypt := Encrypt([]rune(text), key1)
encrypt, errEncrypt := Encrypt(text, key1)
if errEncrypt != nil {
t.Error(errEncrypt)
}
decrypt, errDecrypt := Decrypt([]rune(encrypt), key1)
decrypt, errDecrypt := Decrypt(encrypt, key1)
if errDecrypt != nil {
t.Error(errDecrypt)
}
if strings.Contains(decrypt, text) == false {
t.Error("The string was not decrypted correctly")
if !reflect.DeepEqual(decrypt, text) {
t.Errorf("The string was not decrypted correctly %q %q", decrypt, text)
}
decrypt, _ = Decrypt([]rune(encrypt), key2)
if strings.Contains(decrypt, text) == true {
t.Error("The string was decrypted with a different key")
if reflect.DeepEqual(decrypt, text) {
t.Errorf("The string was decrypted with a different key: %q %q", decrypt, text)
}
}

func FuzzTransposition(f *testing.F) {
for _, transpositionTestInput := range texts {
f.Add(transpositionTestInput)
}
f.Fuzz(func(t *testing.T, input string) {
keyword := getRandomString()
message := []rune(input)
encrypted, err := Encrypt(message, keyword)
switch {
case err == nil:
case errors.Is(err, ErrKeyMissing),
errors.Is(err, ErrNoTextToEncrypt):
return
default:
t.Fatalf("unexpected error when encrypting string %q: %v", input, err)
}
decrypted, err := Decrypt([]rune(encrypted), keyword)
switch {
case err == nil:
case errors.Is(err, ErrKeyMissing),
errors.Is(err, ErrNoTextToEncrypt):
return
default:
t.Fatalf("unexpected error when decrypting string %q: %v", encrypted, err)
}

if !reflect.DeepEqual(message, decrypted) {
t.Fatalf("expected: %+v, got: %+v", message, []rune(decrypted))
}
})
}