package util import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestLicenseKeyRoundTrip(t *testing.T) { priv, pub, err := GenerateEd25519KeyPair() require.NoError(t, err) exp := time.Now().Add(30 * 24 * time.Hour).Unix() payload := LicensePayload{ ShopID: 42, LicenseID: 7, Type: "annual", IssuedAt: time.Now().Unix(), ExpiresAt: &exp, MaxDevices: 3, } token, err := IssueLicenseToken(payload, priv) require.NoError(t, err) assert.NotEmpty(t, token) got, err := VerifyLicenseToken(token, pub) require.NoError(t, err) assert.Equal(t, payload.ShopID, got.ShopID) assert.Equal(t, payload.Type, got.Type) assert.Equal(t, payload.MaxDevices, got.MaxDevices) assert.Equal(t, *payload.ExpiresAt, *got.ExpiresAt) } func TestVerifyLicenseToken_TamperedPayload(t *testing.T) { priv, pub, err := GenerateEd25519KeyPair() require.NoError(t, err) exp := time.Now().Add(30 * 24 * time.Hour).Unix() token, err := IssueLicenseToken(LicensePayload{ ShopID: 1, Type: "trial", IssuedAt: time.Now().Unix(), ExpiresAt: &exp, MaxDevices: 1, }, priv) require.NoError(t, err) // Flip the last byte of the signature to simulate tampering tampered := token[:len(token)-2] + "XX" _, err = VerifyLicenseToken(tampered, pub) assert.Error(t, err, "tampered token must be rejected") } func TestVerifyLicenseToken_WrongKey(t *testing.T) { priv, _, err := GenerateEd25519KeyPair() require.NoError(t, err) _, otherPub, err := GenerateEd25519KeyPair() require.NoError(t, err) exp := time.Now().Add(30 * 24 * time.Hour).Unix() token, err := IssueLicenseToken(LicensePayload{ ShopID: 1, Type: "trial", IssuedAt: time.Now().Unix(), ExpiresAt: &exp, MaxDevices: 1, }, priv) require.NoError(t, err) _, err = VerifyLicenseToken(token, otherPub) assert.ErrorIs(t, err, ErrInvalidLicenseSignature) } func TestVerifyLicenseToken_InvalidFormat(t *testing.T) { _, pub, err := GenerateEd25519KeyPair() require.NoError(t, err) _, err = VerifyLicenseToken("not-a-valid-token", pub) assert.ErrorIs(t, err, ErrInvalidLicenseToken) } // TestLicenseTokenFitsColumnLimit verifies that a realistic (even worst-case) // license token fits within the VARCHAR(768) column limit imposed by the // InnoDB index constraint (768 chars × 4 bytes/char = 3072 bytes max). func TestLicenseTokenFitsColumnLimit(t *testing.T) { const columnLimit = 768 priv, _, err := GenerateEd25519KeyPair() require.NoError(t, err) // Use a large features map to simulate a worst-case payload. exp := time.Now().Add(365 * 24 * time.Hour).Unix() payload := LicensePayload{ ShopID: 999999999, LicenseID: 999999999, Type: "lifetime", IssuedAt: time.Now().Unix(), ExpiresAt: &exp, MaxDevices: 99, Features: map[string]any{ "finance": true, "inventory": true, "reports": true, "export": true, "multi_shop": true, "api_access": true, }, } token, err := IssueLicenseToken(payload, priv) require.NoError(t, err) assert.LessOrEqual(t, len(token), columnLimit, "license token length %d exceeds VARCHAR(%d) column limit", len(token), columnLimit) }