@@ -10,6 +10,129 @@ import kotlin.test.*
1010
1111class PemTest {
1212
13+ @Test
14+ fun testByteArrayConstructorImmutability () {
15+ val content = byteArrayOf(1 , 2 , 3 , 4 )
16+ val expected = ByteString (content)
17+ val doc = PemDocument (PemLabel (" LABEL" ), content)
18+ // mutate the original byte array after construction
19+ content[0 ] = 9
20+ // content must be unaffected (constructor copies input)
21+ assertEquals(expected, doc.content)
22+ }
23+
24+ @Test
25+ fun testEqualsHashCode () {
26+ val doc = PemDocument (PemLabel (" LABEL" ), byteArrayOf(1 , 2 , 3 , 4 ))
27+
28+ // equals/hashCode with the same logical content
29+ val doc2 = PemDocument (PemLabel (" LABEL" ), ByteString (1 , 2 , 3 , 4 ))
30+ assertEquals(doc.content, doc2.content)
31+ assertEquals(doc, doc2)
32+ assertEquals(doc.hashCode(), doc2.hashCode())
33+
34+ // different label -> not equal
35+ val doc3 = PemDocument (PemLabel (" OTHER" ), ByteString (1 , 2 , 3 , 4 ))
36+ assertNotEquals(doc, doc3)
37+
38+ // different content -> not equal
39+ val doc4 = PemDocument (PemLabel (" LABEL" ), ByteString (4 , 3 , 2 , 1 ))
40+ assertNotEquals(doc, doc4)
41+
42+ }
43+
44+ @Test
45+ fun testToString () {
46+ assertEquals(
47+ " PemDocument(label=LABEL, content=ByteString(size=4 hex=01020304))" ,
48+ PemDocument (PemLabel (" LABEL" ), byteArrayOf(1 , 2 , 3 , 4 )).toString()
49+ )
50+ }
51+
52+ @Test
53+ fun testDecodeToSequenceEmptyAndNoPem () {
54+ testPemDecodeSequence(emptyList(), " " )
55+ testPemDecodeSequence(emptyList(), " no pem here" )
56+ }
57+
58+ @Test
59+ fun testDecodeToSequenceWithContentAroundAndBetween (): Unit = testPemDecodeSequence(
60+ expected = listOf (
61+ PemDocument (PemLabel .Certificate , " ONE" .encodeToByteString()),
62+ PemDocument (PemLabel .Certificate , " TWO" .encodeToByteString())
63+ ),
64+ document = """
65+ Some heading that should be ignored
66+ -----BEGIN CERTIFICATE-----
67+ T05F
68+ -----END CERTIFICATE-----
69+ Some text between documents should be ignored too
70+ -----BEGIN CERTIFICATE-----
71+ VFdP
72+ -----END CERTIFICATE-----
73+ Final trailing text that should be ignored as well
74+ """ .trimIndent()
75+ )
76+
77+ @Test
78+ fun testDecodeSourcePartialConsumption () {
79+ val pem = """
80+ IGNORE
81+ -----BEGIN X-----
82+ QQ==
83+ -----END X-----
84+ BETWEEN
85+ -----BEGIN X-----
86+ Qg==
87+ -----END X-----
88+ AFTER
89+ """ .trimIndent()
90+
91+ val buffer = Buffer ()
92+ buffer.writeString(pem)
93+
94+ assertEquals(
95+ expected = PemDocument (PemLabel (" X" ), " A" .encodeToByteString()),
96+ // read from buffer one document
97+ actual = PemDocument .decode(buffer)
98+ )
99+ assertEquals(
100+ expected = PemDocument (PemLabel (" X" ), " B" .encodeToByteString()),
101+ // read from buffer second document
102+ actual = PemDocument .decode(buffer)
103+ )
104+
105+ assertEquals(" AFTER" , buffer.readString())
106+ }
107+
108+ @Test
109+ fun testDecodeToSequenceSourcePartialConsumption () {
110+ val pem = """
111+ IGNORE
112+ -----BEGIN X-----
113+ QQ==
114+ -----END X-----
115+ BETWEEN
116+ -----BEGIN X-----
117+ Qg==
118+ -----END X-----
119+ AFTER
120+ """ .trimIndent()
121+
122+ val buffer = Buffer ()
123+ buffer.writeString(pem)
124+
125+ assertEquals(
126+ listOf (
127+ PemDocument (PemLabel (" X" ), " A" .encodeToByteString()),
128+ PemDocument (PemLabel (" X" ), " B" .encodeToByteString())
129+ ),
130+ PemDocument .decodeToSequence(buffer).take(2 ).toList()
131+ )
132+
133+ assertEquals(" AFTER" , buffer.readString())
134+ }
135+
13136 @Test
14137 fun testHelloWorld () = testPem(
15138 label = " UNKNOWN" ,
@@ -57,6 +180,14 @@ class PemTest {
57180 assertEquals(" Invalid PEM format: missing BEGIN label" , it.message)
58181 }
59182
183+ @Test
184+ fun testDecodingWithEmpty () = testPemDecodeFailure(
185+ document = " "
186+ ) {
187+ assertIs<IllegalArgumentException >(it)
188+ assertEquals(" Invalid PEM format: missing BEGIN label" , it.message)
189+ }
190+
60191 @Test
61192 fun testDecodingWithNoEndLabel () = testPemDecodeFailure(
62193 document = " -----BEGIN UNKNOWN-----\n SGVsbG8gV29ybGQ="
@@ -77,6 +208,31 @@ class PemTest {
77208 assertEquals(" Invalid PEM format: BEGIN(UNKNOWN1) and END(UNKNOWN2) labels mismatch" , it.message)
78209 }
79210
211+ @Test
212+ fun testDecodingWithMissingNewLineAfterBegin () = testPemDecodeFailure(
213+ document = " -----BEGIN X-----"
214+ ) {
215+ assertIs<IllegalArgumentException >(it)
216+ assertEquals(" Invalid PEM format: missing new line after BEGIN label" , it.message)
217+ }
218+
219+ @Test
220+ fun testDecodingWithMissingBeginLabelSuffix () = testPemDecodeFailure(
221+ document = " -----BEGIN X\n QQ==\n -----END X-----"
222+ ) {
223+ assertIs<IllegalArgumentException >(it)
224+ assertEquals(" Invalid PEM format: missing BEGIN label suffix" , it.message)
225+ }
226+
227+ @Test
228+ fun testDecodingWithMissingEndLabelSuffix () = testPemDecodeFailure(
229+ document = " -----BEGIN X-----\n QQ==\n -----END X"
230+ ) {
231+ assertIs<IllegalArgumentException >(it)
232+ assertEquals(" Invalid PEM format: missing END label suffix" , it.message)
233+ }
234+
235+
80236 private fun testPem (
81237 label : String ,
82238 content : ByteString ,
@@ -104,6 +260,25 @@ class PemTest {
104260 } as Source ).buffered()))
105261 }
106262
263+ private fun testPemDecodeSequence (
264+ expected : List <PemDocument >,
265+ document : String ,
266+ ) {
267+ fun assertSequenceEquals (expectedDocs : List <PemDocument >, actualSeq : Sequence <PemDocument >) {
268+ assertEquals(expectedDocs, actualSeq.toList())
269+ }
270+
271+ assertSequenceEquals(expected, PemDocument .decodeToSequence(document))
272+ assertSequenceEquals(expected, PemDocument .decodeToSequence(document.encodeToByteArray()))
273+ assertSequenceEquals(expected, PemDocument .decodeToSequence(document.encodeToByteString()))
274+ assertSequenceEquals(expected, PemDocument .decodeToSequence(Buffer ().also {
275+ it.write(document.encodeToByteArray())
276+ }))
277+ assertSequenceEquals(expected, PemDocument .decodeToSequence((Buffer ().also {
278+ it.write(document.encodeToByteArray())
279+ } as Source ).buffered()))
280+ }
281+
107282 private fun testPemDecodeFailure (
108283 document : String ,
109284 assertThrowable : (Throwable ) -> Unit ,
0 commit comments