UpdateManifestTest.kt

3.8 KB · raw

1package com.peachpasswords.updates23import org.junit.Assert.assertEquals4import org.junit.Assert.assertNull5import org.junit.Assert.assertTrue6import org.junit.Assert.assertFalse7import org.junit.Test89class UpdateManifestTest {1011    private fun manifest(notesField: String) = """12        {13          "version": "2.3.68",14          $notesField15          "platforms": {16            "$ANDROID_PLATFORM_KEY": {17              "url": "https://kepr.uk/api/peach-android/releases/2.3.68/$ANDROID_PLATFORM_KEY",18              "checksum": "blake3:${"a".repeat(64)}"19            }20          }21        }22    """.trimIndent()2324    @Test25    fun `canonical notes key wins`() {26        val parsed = parseReleaseManifest(manifest(""""notes": "fixed the thing", "release_notes": "old","""))27        assertEquals("fixed the thing", parsed.notes)28        assertEquals("2.3.68", parsed.version)29    }3031    @Test32    fun `legacy release_notes key still accepted`() {33        assertEquals("legacy", parseReleaseManifest(manifest(""""release_notes": "legacy",""")).notes)34    }3536    @Test37    fun `omitted notes are absent, not the string null`() {38        assertNull(parseReleaseManifest(manifest("")).notes)39    }4041    @Test42    fun `json null notes never render as the text null`() {43        assertNull(parseReleaseManifest(manifest(""""notes": null,""")).notes)44        assertNull(parseReleaseManifest(manifest(""""notes": "   ",""")).notes)45    }4647    @Test48    fun `missing platform falls back to the sole artifact`() {49        val body = """50            {"version":"2.3.68","platforms":{"android-arm64":51              {"url":"https://x/y","checksum":"sha256:${"b".repeat(64)}"}}}52        """.trimIndent()53        assertEquals("https://x/y", parseReleaseManifest(body).url)54    }5556    @Test(expected = ManifestFormatException::class)57    fun `no platforms is a hard failure`() {58        parseReleaseManifest("""{"version":"2.3.68"}""")59    }6061    @Test62    fun `version comparison is numeric, not lexical`() {63        assertTrue(isNewerVersion("2.3.100", "2.3.67"))64        assertTrue(isNewerVersion("2.4.0", "2.3.99"))65        assertTrue(isNewerVersion("10.0.0", "9.9.9"))66        assertFalse(isNewerVersion("2.3.67", "2.3.67"))67        assertFalse(isNewerVersion("2.3.9", "2.3.10"))68    }6970    @Test71    fun `debug suffix compares as the release it was built from`() {72        assertEquals("2.3.67", normalizeVersion("2.3.67-debug"))73        assertEquals("2.3.67", normalizeVersion("v2.3.67"))74        assertFalse(isNewerVersion("2.3.67", "2.3.67-debug"))75        assertTrue(isNewerVersion("2.3.68", "2.3.67-debug"))76    }7778    @Test79    fun `only well-formed blake3 and sha256 checksums are accepted`() {80        assertEquals(81            ExpectedChecksum("blake3", "a".repeat(64)),82            parseChecksum("blake3:${"A".repeat(64)}"),83        )84        assertEquals("sha256", parseChecksum("sha256:${"0".repeat(64)}")?.algorithm)85        assertNull(parseChecksum("md5:${"a".repeat(64)}"))86        assertNull(parseChecksum("blake3:tooshort"))87        assertNull(parseChecksum("blake3:${"z".repeat(64)}"))88        assertNull(parseChecksum("nocolon"))89    }9091    /** BLAKE3 known-answer vectors — the checksum gate is only worth anything if this is right. */92    @Test93    fun `blake3 matches the reference vectors`() {94        assertEquals(95            "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262",96            hashStream(ByteArray(0).inputStream(), "blake3"),97        )98        assertEquals(99            "6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85",100            hashStream("abc".toByteArray().inputStream(), "blake3"),101        )102    }103104    @Test105    fun `sha256 matches the reference vector`() {106        assertEquals(107            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",108            hashStream("abc".toByteArray().inputStream(), "sha256"),109        )110    }111}