package user import ( "crypto/sha256" "encoding/base64" "encoding/hex" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestGenerateMagicLinkToken_ShapeAndHashAgree confirms the contract that // HashMagicLinkToken(plaintext) == returned hashHex. Without that, the // consume handler can never look up what request stored. func TestGenerateMagicLinkToken_ShapeAndHashAgree(t *testing.T) { plain, hashHex, err := GenerateMagicLinkToken() require.NoError(t, err) assert.NotEmpty(t, plain) assert.NotEmpty(t, hashHex) assert.Len(t, hashHex, 64, "sha256 hex = 64 chars") assert.Equal(t, hashHex, HashMagicLinkToken(plain), "GenerateMagicLinkToken must return a hash that matches HashMagicLinkToken(plain)") } // TestGenerateMagicLinkToken_PlainIsURLSafeBase64 confirms the link can // be embedded in a URL without further escaping. RawURLEncoding => no // "/", "+", or "=" padding chars. func TestGenerateMagicLinkToken_PlainIsURLSafeBase64(t *testing.T) { plain, _, err := GenerateMagicLinkToken() require.NoError(t, err) for _, bad := range []string{"/", "+", "="} { assert.False(t, strings.Contains(plain, bad), "plaintext token must not contain %q (URL-unsafe)", bad) } decoded, err := base64.RawURLEncoding.DecodeString(plain) require.NoError(t, err, "plaintext must round-trip through RawURLEncoding") assert.Len(t, decoded, 32, "32 bytes of entropy") } // TestGenerateMagicLinkToken_Unique confirms two consecutive calls // produce different tokens (not a deterministic seeding bug). func TestGenerateMagicLinkToken_Unique(t *testing.T) { a, ah, err := GenerateMagicLinkToken() require.NoError(t, err) b, bh, err := GenerateMagicLinkToken() require.NoError(t, err) assert.NotEqual(t, a, b, "plaintexts must differ between calls") assert.NotEqual(t, ah, bh, "hashes must differ between calls") } // TestHashMagicLinkToken_StableAndCorrect confirms HashMagicLinkToken is // a pure function (same input -> same output) AND that it produces the // expected sha256 hex digest. Cross-checked against the stdlib so we // catch any accidental algorithm swap. func TestHashMagicLinkToken_StableAndCorrect(t *testing.T) { const sample = "abc123-test-token" got1 := HashMagicLinkToken(sample) got2 := HashMagicLinkToken(sample) assert.Equal(t, got1, got2, "HashMagicLinkToken must be deterministic") sum := sha256.Sum256([]byte(sample)) want := hex.EncodeToString(sum[:]) assert.Equal(t, want, got1, "HashMagicLinkToken must be sha256 hex") } // TestHashMagicLinkToken_DiffersOnDifferentInput is the tautological // counter-test of stability : different inputs -> different outputs. // Catches the (unlikely) case where someone replaces the impl with // a constant. func TestHashMagicLinkToken_DiffersOnDifferentInput(t *testing.T) { assert.NotEqual(t, HashMagicLinkToken("a"), HashMagicLinkToken("b")) }