Files
jiu/backend/internal/util/license_key_test.go
T
wangjia c553237f15 fix(db): 缩短 license_key 列至 VARCHAR(768) 避免 InnoDB 索引超限 [tsk_Qmk4wT_dkqRR]
原 size:2048 在 utf8mb4 下产生 8192 字节索引,超过 InnoDB 3072 字节上限,
全新环境 AutoMigrate 必失败。改为 VARCHAR(768)(768×4=3072 字节,恰好
在上限内),schema.sql 同步更新列类型并将 uk_license_key 改为全列索引(无
需前缀)。新增测试 TestLicenseTokenFitsColumnLimit 验证最坏情况 token 长度
确实在 768 字符以内。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 12:01:30 +08:00

111 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}