From 8928a7d1743a5f71076808b4d919ef289299229f Mon Sep 17 00:00:00 2001 From: mazkev Date: Wed, 28 Oct 2026 07:01:14 +0700 Subject: [PATCH] test(cipher): add fuzz testing, unicode support and edge cases for railfence - Implements FuzzRailfence as requested in issue #480. - Refactors Encrypt and Decrypt to properly operate on runes, enabling full Unicode support. - Optimizes Encrypt to append directly to row slices, avoiding 2D sparse matrix allocation and eliminating the bug where null bytes were dropped. - Adds guards for rails <= 1 in Encrypt and Decrypt to prevent runtime panic on negative or zero rails. - Adds Unicode and edge cases to TestEncrypt and TestDecrypt. --- cipher/railfence/railfence.go | 36 ++++++-------- cipher/railfence/railfence_test.go | 75 ++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 21 deletions(-) diff --git a/cipher/railfence/railfence.go b/cipher/railfence/railfence.go index aeeaed3fa..e2fae0585 100644 --- a/cipher/railfence/railfence.go +++ b/cipher/railfence/railfence.go @@ -11,57 +11,51 @@ import ( ) func Encrypt(text string, rails int) string { - if rails == 1 { + runes := []rune(text) + if rails <= 1 || rails >= len(runes) { return text } - // Create a matrix for the rail fence pattern - matrix := make([][]rune, rails) - for i := range matrix { - matrix[i] = make([]rune, len(text)) - } + rows := make([][]rune, rails) - // Fill the matrix dirDown := false - row, col := 0, 0 - for _, char := range text { + row := 0 + for _, char := range runes { + rows[row] = append(rows[row], char) if row == 0 || row == rails-1 { dirDown = !dirDown } - matrix[row][col] = char - col++ if dirDown { row++ } else { row-- } } + var result strings.Builder - for _, line := range matrix { - for _, char := range line { - if char != 0 { - result.WriteRune(char) - } - } + for _, r := range rows { + result.WriteString(string(r)) } return result.String() } + func Decrypt(cipherText string, rails int) string { - if rails == 1 || rails >= len(cipherText) { + runes := []rune(cipherText) + if rails <= 1 || rails >= len(runes) { return cipherText } // Placeholder for the decrypted message - decrypted := make([]rune, len(cipherText)) + decrypted := make([]rune, len(runes)) // Calculate the zigzag pattern and place characters accordingly index := 0 for rail := 0; rail < rails; rail++ { position := rail down := true // Direction flag - for position < len(cipherText) { - decrypted[position] = rune(cipherText[index]) + for position < len(runes) { + decrypted[position] = runes[index] index++ // Determine the next position based on the current rail and direction diff --git a/cipher/railfence/railfence_test.go b/cipher/railfence/railfence_test.go index 7f7bfa53a..e6f1013ad 100644 --- a/cipher/railfence/railfence_test.go +++ b/cipher/railfence/railfence_test.go @@ -2,6 +2,7 @@ package railfence import ( "testing" + "unicode/utf8" ) func TestEncrypt(t *testing.T) { @@ -35,6 +36,24 @@ func TestEncrypt(t *testing.T) { 100, "hi", }, + { + "Encrypt with Unicode characters", + "Hello, 世界!", + 2, + "Hlo 界el,世!", + }, + { + "Encrypt with edge case: 0 rails", + "hello", + 0, + "hello", + }, + { + "Encrypt with edge case: negative rails", + "hello", + -5, + "hello", + }, } for _, test := range railFenceTestData { @@ -78,6 +97,24 @@ func TestDecrypt(t *testing.T) { 100, "hi", }, + { + "Decrypt with Unicode characters", + "Hlo 界el,世!", + 2, + "Hello, 世界!", + }, + { + "Decrypt with edge case: 0 rails", + "hello", + 0, + "hello", + }, + { + "Decrypt with edge case: negative rails", + "hello", + -5, + "hello", + }, } for _, test := range railFenceTestData { @@ -89,3 +126,41 @@ func TestDecrypt(t *testing.T) { }) } } + +func FuzzRailfence(f *testing.F) { + testCases := []struct { + input string + rails int + }{ + {"hello", 2}, + {"hello world", 3}, + {"hello", 1}, + {"hi", 100}, + {"Hello, 世界!", 2}, + {"The Quick Brown Fox Jumps Over The Lazy Dog", 4}, + } + + for _, tc := range testCases { + f.Add(tc.input, tc.rails) + } + + f.Fuzz(func(t *testing.T, input string, rails int) { + if !utf8.ValidString(input) { + return + } + if rails <= 0 { + if Encrypt(input, rails) != input { + t.Errorf("Encrypt with rails <= 0 should return input unmodified") + } + if Decrypt(input, rails) != input { + t.Errorf("Decrypt with rails <= 0 should return input unmodified") + } + return + } + encrypted := Encrypt(input, rails) + decrypted := Decrypt(encrypted, rails) + if decrypted != input { + t.Fatalf("With input: %q and rails: %d\n\tExpected: %q\n\tGot: %q", input, rails, input, decrypted) + } + }) +}