package main

import (
	"crypto/ed25519"
	"crypto/rand"
	"github.com/golang-jwt/jwt/v5"
	"reflect"
	"sync"
	"testing"
	"time"
)

func TestDemo(t *testing.T) {
	want := []string{"valid=true", "wrong-audience-rejected=true", "expired-rejected=true", "revoked-rejected=true", "rotated", "reuse-revoked-family", "family-revoked"}
	if got := demo(); !reflect.DeepEqual(got, want) {
		t.Fatal(got)
	}
}
func TestRejectClaimsAndKey(t *testing.T) {
	pub, key, _ := ed25519.GenerateKey(rand.Reader)
	tests := map[string]func(*Claims){"issuer": func(c *Claims) { c.Issuer = "https://wrong.example" }, "missing-exp": func(c *Claims) { c.ExpiresAt = nil }, "missing-id": func(c *Claims) { c.ID = "" }, "future-iat": func(c *Claims) { c.IssuedAt = jwt.NewNumericDate(now.Add(time.Minute)) }, "future-nbf": func(c *Claims) { c.NotBefore = jwt.NewNumericDate(now.Add(time.Minute)) }, "empty-sub": func(c *Claims) { c.Subject = "" }}
	for name, mutate := range tests {
		t.Run(name, func(t *testing.T) {
			c := validClaims()
			mutate(&c)
			if verify(sign(c, key), pub, nil) == nil {
				t.Fatal("accepted")
			}
		})
	}
	_, other, _ := ed25519.GenerateKey(rand.Reader)
	if verify(sign(validClaims(), other), pub, nil) == nil {
		t.Fatal("wrong key accepted")
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, validClaims())
	raw, err := token.SignedString([]byte("PUBLIC TEST FIXTURE, NOT A REAL SECRET"))
	if err != nil {
		t.Fatal(err)
	}
	if verify(raw, pub, nil) == nil {
		t.Fatal("wrong alg accepted")
	}
}
func TestConcurrentRefresh(t *testing.T) {
	f := &Family{current: "r0", used: map[string]bool{}}
	var wg sync.WaitGroup
	results := make(chan string, 2)
	for range 2 {
		wg.Go(func() { results <- f.Rotate("r0", "r1") })
	}
	wg.Wait()
	close(results)
	counts := map[string]int{}
	for r := range results {
		counts[r]++
	}
	if counts["rotated"] != 1 || counts["reuse-revoked-family"] != 1 {
		t.Fatal(counts)
	}
}

func TestProfileAndRequiredTimes(t *testing.T) {
	pub, key, _ := ed25519.GenerateKey(rand.Reader)
	for _, name := range []string{"typ", "kid"} {
		token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, validClaims())
		token.Header["typ"] = "orders-at+jwt"
		token.Header["kid"] = "local-test-key"
		token.Header[name] = "wrong"
		raw, err := token.SignedString(key)
		if err != nil {
			t.Fatal(err)
		}
		if verify(raw, pub, nil) == nil {
			t.Fatal("accepted wrong", name)
		}
	}
	for _, name := range []string{"iat", "nbf", "long-life"} {
		c := validClaims()
		switch name {
		case "iat":
			c.IssuedAt = nil
		case "nbf":
			c.NotBefore = nil
		case "long-life":
			c.ExpiresAt = jwt.NewNumericDate(now.Add(time.Hour))
		}
		if verify(sign(c, key), pub, nil) == nil {
			t.Fatal("accepted", name)
		}
	}
}
