package amp import ( "io" "math/rand" "strings" "testing" ) func armorDecodeToString(src string) (string, error) { dec, err := NewArmorDecoder(strings.NewReader(src)) if err != nil { return "", err } p, err := io.ReadAll(dec) return string(p), err } func TestArmorDecoder(t *testing.T) { for _, test := range []struct { input string expectedOutput string expectedErr bool }{ {`
0`, "", false, }, {`
0aGVsbG8gd29ybGQK`, "hello world\n", false, }, // bad version indicator {`
1aGVsbG8gd29ybGQK`, "", true, }, // text outside
elements
{`
0aGVsbG8gd29ybGQK
blah blah blah
0aGVsbG8gd29ybGQK
0aGVsbG8gd29ybGQK
blah blah blah
`,
"hello world\n",
false,
},
{`
0QUJDREV
GR0hJSkt
MTU5PUFF
SU1RVVld
junk
YWVowMTI
zNDU2Nzg
5Cg
=
=
`,
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\n",
false,
},
// no elements, hence no version indicator
{`
aGVsbG8gd29ybGQK
blah blah blah
aGVsbG8gd29ybGQK
aGVsbG8gd29ybGQK
blah blah blah
`,
"",
true,
},
// empty elements, hence no version indicator
{`
aGVsbG8gd29ybGQK
blah blah blah
aGVsbG8gd29ybGQK
aGVsbG8gd29ybGQK
blah blah blah
`,
"",
true,
},
// other elements inside
{
"blah 0aGVsbG8gd29
ybGQK
",
"hello world\n",
false,
},
// HTML comment
{
"blah ",
"",
true,
},
// all kinds of ASCII whitespace
{
"blah \x200\x09aG\x0aV\x0csb\x0dG8\x20gd29ybGQK
",
"hello world\n",
false,
},
// bad padding
{`
0QUJDREV
GR0hJSkt
MTU5PUFF
SU1RVVld
junk
YWVowMTI
zNDU2Nzg
5Cg
=
`,
"",
true,
},
/*
// per-chunk base64
// test disabled because Go stdlib handles this incorrectly:
// https://github.com/golang/go/issues/31626
{
"QQ==
Qg==
",
"",
true,
},
*/
// missing
{
"blah 0aGVsbG8gd29ybGQK",
"",
true,
},
// nested
{
"blah 0aGVsbG8gd29
ybGQK
",
"",
true,
},
} {
output, err := armorDecodeToString(test.input)
if test.expectedErr && err == nil {
t.Errorf("%+q → (%+q, %v), expected error", test.input, output, err)
continue
}
if !test.expectedErr && err != nil {
t.Errorf("%+q → (%+q, %v), expected no error", test.input, output, err)
continue
}
if !test.expectedErr && output != test.expectedOutput {
t.Errorf("%+q → (%+q, %v), expected (%+q, %v)",
test.input, output, err, test.expectedOutput, nil)
continue
}
}
}
func armorRoundTrip(s string) (string, error) {
var encoded strings.Builder
enc, err := NewArmorEncoder(&encoded)
if err != nil {
return "", err
}
_, err = io.Copy(enc, strings.NewReader(s))
if err != nil {
return "", err
}
err = enc.Close()
if err != nil {
return "", err
}
return armorDecodeToString(encoded.String())
}
func TestArmorRoundTrip(t *testing.T) {
lengths := make([]int, 0)
// Test short strings and lengths around elementSizeLimit thresholds.
for i := 0; i < bytesPerChunk*2; i++ {
lengths = append(lengths, i)
}
for i := -10; i < +10; i++ {
lengths = append(lengths, elementSizeLimit+i)
lengths = append(lengths, 2*elementSizeLimit+i)
}
for _, n := range lengths {
buf := make([]byte, n)
rand.Read(buf)
input := string(buf)
output, err := armorRoundTrip(input)
if err != nil {
t.Errorf("length %d → error %v", n, err)
continue
}
if output != input {
t.Errorf("length %d → %+q", n, output)
continue
}
}
}