package com.peachpasswords.updates import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Assert.assertFalse import org.junit.Test class UpdateManifestTest { private fun manifest(notesField: String) = """ { "version": "2.3.68", $notesField "platforms": { "$ANDROID_PLATFORM_KEY": { "url": "https://kepr.uk/api/peach-android/releases/2.3.68/$ANDROID_PLATFORM_KEY", "checksum": "blake3:${"a".repeat(64)}" } } } """.trimIndent() @Test fun `canonical notes key wins`() { val parsed = parseReleaseManifest(manifest(""""notes": "fixed the thing", "release_notes": "old",""")) assertEquals("fixed the thing", parsed.notes) assertEquals("2.3.68", parsed.version) } @Test fun `legacy release_notes key still accepted`() { assertEquals("legacy", parseReleaseManifest(manifest(""""release_notes": "legacy",""")).notes) } @Test fun `omitted notes are absent, not the string null`() { assertNull(parseReleaseManifest(manifest("")).notes) } @Test fun `json null notes never render as the text null`() { assertNull(parseReleaseManifest(manifest(""""notes": null,""")).notes) assertNull(parseReleaseManifest(manifest(""""notes": " ",""")).notes) } @Test fun `missing platform falls back to the sole artifact`() { val body = """ {"version":"2.3.68","platforms":{"android-arm64": {"url":"https://x/y","checksum":"sha256:${"b".repeat(64)}"}}} """.trimIndent() assertEquals("https://x/y", parseReleaseManifest(body).url) } @Test(expected = ManifestFormatException::class) fun `no platforms is a hard failure`() { parseReleaseManifest("""{"version":"2.3.68"}""") } @Test fun `version comparison is numeric, not lexical`() { assertTrue(isNewerVersion("2.3.100", "2.3.67")) assertTrue(isNewerVersion("2.4.0", "2.3.99")) assertTrue(isNewerVersion("10.0.0", "9.9.9")) assertFalse(isNewerVersion("2.3.67", "2.3.67")) assertFalse(isNewerVersion("2.3.9", "2.3.10")) } @Test fun `debug suffix compares as the release it was built from`() { assertEquals("2.3.67", normalizeVersion("2.3.67-debug")) assertEquals("2.3.67", normalizeVersion("v2.3.67")) assertFalse(isNewerVersion("2.3.67", "2.3.67-debug")) assertTrue(isNewerVersion("2.3.68", "2.3.67-debug")) } @Test fun `only well-formed blake3 and sha256 checksums are accepted`() { assertEquals( ExpectedChecksum("blake3", "a".repeat(64)), parseChecksum("blake3:${"A".repeat(64)}"), ) assertEquals("sha256", parseChecksum("sha256:${"0".repeat(64)}")?.algorithm) assertNull(parseChecksum("md5:${"a".repeat(64)}")) assertNull(parseChecksum("blake3:tooshort")) assertNull(parseChecksum("blake3:${"z".repeat(64)}")) assertNull(parseChecksum("nocolon")) } /** BLAKE3 known-answer vectors — the checksum gate is only worth anything if this is right. */ @Test fun `blake3 matches the reference vectors`() { assertEquals( "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262", hashStream(ByteArray(0).inputStream(), "blake3"), ) assertEquals( "6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85", hashStream("abc".toByteArray().inputStream(), "blake3"), ) } @Test fun `sha256 matches the reference vector`() { assertEquals( "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", hashStream("abc".toByteArray().inputStream(), "sha256"), ) } }