package com.peachpasswords.codex import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test import kotlin.random.Random /** * Codex suite (brief Suite 11): deflate/inflate fidelity, Reed-Solomon erasure * limits (remove 1 / max / max+1), and full envelope round-trips for small and * large vault blobs. */ class CodexSuiteTest { // ── Deflate / inflate ───────────────────────────────────────────────────── @Test fun deflate_inflate_round_trips_small() { val data = "the quick brown duck".toByteArray() assertArrayEquals(data, CodexCodec.inflate(CodexCodec.deflate(data))) } @Test fun deflate_inflate_round_trips_empty() { assertArrayEquals(ByteArray(0), CodexCodec.inflate(CodexCodec.deflate(ByteArray(0)))) } @Test fun deflate_inflate_round_trips_large_random() { val rng = Random(42) val data = ByteArray(50_000) { rng.nextInt().toByte() } assertArrayEquals(data, CodexCodec.inflate(CodexCodec.deflate(data))) } @Test fun deflate_shrinks_compressible_data() { val data = "A".repeat(10_000).toByteArray() assertTrue(CodexCodec.deflate(data).size < data.size) } // ── Parity sizing ───────────────────────────────────────────────────────── @Test fun parity_shards_are_at_least_one() { assertTrue(computeParityShards(1) >= 1) assertTrue(computeParityShards(6) >= 1) } @Test fun parity_shards_scale_with_data() { // ~15% ratio: more data shards → at least as many parity shards. assertTrue(computeParityShards(100) >= computeParityShards(10)) } // ── Reed-Solomon erasure limits ─────────────────────────────────────────── private fun encodedShards(data: Int, parity: Int, size: Int, seed: Int): Pair, Array> { val rng = Random(seed) val original = Array(data) { ByteArray(size) { rng.nextInt().toByte() } } val shards = Array(data + parity) { i -> if (i < data) original[i].copyOf() else ByteArray(size) } ReedSolomon.encode(shards, data, parity) return original to shards } @Test fun rs_recovers_after_removing_one_shard() { val data = 6; val parity = computeParityShards(data) val (original, shards) = encodedShards(data, parity, 32, 1) val present = BooleanArray(data + parity) { true } shards[2] = ByteArray(0); present[2] = false ReedSolomon.reconstruct(shards, data, parity, present) for (i in 0 until data) assertArrayEquals(original[i], shards[i]) } @Test fun rs_recovers_after_removing_max_erasures() { val data = 6; val parity = computeParityShards(data) val (original, shards) = encodedShards(data, parity, 32, 2) val present = BooleanArray(data + parity) { true } // Drop exactly `parity` shards — the maximum tolerable. repeat(parity) { shards[it] = ByteArray(0); present[it] = false } ReedSolomon.reconstruct(shards, data, parity, present) for (i in 0 until data) assertArrayEquals(original[i], shards[i]) } @Test fun rs_fails_beyond_max_erasures() { val data = 6; val parity = computeParityShards(data) val (_, shards) = encodedShards(data, parity, 32, 3) val present = BooleanArray(data + parity) { true } // Drop parity+1 shards — one too many to reconstruct. repeat(parity + 1) { shards[it] = ByteArray(0); present[it] = false } var thrown: CodexException? = null try { ReedSolomon.reconstruct(shards, data, parity, present) } catch (e: CodexException) { thrown = e } assertEquals(CodexErrorCode.INSUFFICIENT_CHUNKS, thrown?.code) } // ── Envelope round-trips ────────────────────────────────────────────────── private fun payload(blobLen: Int) = CodexPayload( version = 2, verifyCode = "PX-AB12-CD34", vaultBlob = "v".repeat(blobLen), timestamp = 1, entryCount = 3, vaultFingerprint = "ABCD".repeat(8), generatedAt = 2, ) private fun encodePayload(p: CodexPayload): Pair, CodexLayout> { val bytes = CodexCodec.json.encodeToString(CodexPayload.serializer(), p).toByteArray() return CodexCodec.encodeToEnvelopes(CodexCodec.deflate(bytes), p.verifyCode) } @Test fun every_envelope_passes_integrity() { val (envelopes, layout) = encodePayload(payload(3000)) assertTrue(envelopes.all { CodexCodec.verifyChunkIntegrity(it) }) assertEquals(layout.dataChunks + layout.parityChunks, envelopes.size) } @Test fun full_set_reconstructs_payload() { val p = payload(3000) val (envelopes, _) = encodePayload(p) val recovered = CodexCodec.reconstructCompressed(envelopes) val parsed = CodexCodec.json.decodeFromString(CodexPayload.serializer(), String(CodexCodec.inflate(recovered))) assertEquals(p, parsed) } @Test fun large_blob_round_trips_through_envelopes() { val p = payload(40_000) val (envelopes, _) = encodePayload(p) val recovered = CodexCodec.reconstructCompressed(envelopes) val parsed = CodexCodec.json.decodeFromString(CodexPayload.serializer(), String(CodexCodec.inflate(recovered))) assertEquals(p, parsed) } @Test fun tampered_envelope_fails_integrity() { val (envelopes, _) = encodePayload(payload(500)) val tampered = envelopes.first().copy(d = CodexCodec.base64Url("evil".toByteArray())) assertFalse(CodexCodec.verifyChunkIntegrity(tampered)) } // ── Helpers ─────────────────────────────────────────────────────────────── @Test fun base64url_round_trips() { val rng = Random(9) val bytes = ByteArray(256) { rng.nextInt().toByte() } assertArrayEquals(bytes, CodexCodec.fromBase64Url(CodexCodec.base64Url(bytes))) } @Test fun sha256_hex_is_stable_and_64_chars() { val h = CodexCodec.sha256Hex("peach".toByteArray()) assertEquals(64, h.length) assertEquals(h, CodexCodec.sha256Hex("peach".toByteArray())) } }