/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.core.auth.sso

import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Test

/**
 * PKCE Layer B primitives (PLAN.md §4.2). The challenge encoding must match the
 * backend byte-for-byte (`base64url(SHA-256(verifier))`, no padding) or `/exchange`
 * rejects every code — so it is pinned against the RFC 7636 test vector.
 */
class PkceTest {

    // RFC 4648 §5 URL-safe base64 alphabet, no padding.
    private val base64UrlNoPad = Regex("^[A-Za-z0-9_-]+$")

    @Test fun `challenge matches the RFC 7636 vector`() {
        // RFC 7636 Appendix B.
        val verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
        assertEquals("E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", Pkce.challengeOf(verifier))
    }

    @Test fun `verifier is url-safe base64 without padding`() {
        val verifier = Pkce.newVerifier()
        assertTrue("verifier charset: $verifier", base64UrlNoPad.matches(verifier))
        // 32 random bytes → 43 base64 chars (no padding), inside RFC 7636's 43–128.
        assertEquals(43, verifier.length)
    }

    @Test fun `challenge is url-safe base64 without padding`() {
        val challenge = Pkce.challengeOf(Pkce.newVerifier())
        assertTrue("challenge charset: $challenge", base64UrlNoPad.matches(challenge))
        // SHA-256 (32 bytes) → 43 base64 chars, no '=' padding.
        assertEquals(43, challenge.length)
    }

    @Test fun `verifiers and states are unique per call`() {
        assertNotEquals(Pkce.newVerifier(), Pkce.newVerifier())
        assertNotEquals(Pkce.newState(), Pkce.newState())
    }

    @Test fun `state is url-safe base64 without padding`() {
        assertTrue(base64UrlNoPad.matches(Pkce.newState()))
    }
}
