package license import ( cryptorand "crypto/rand" "fmt" "math/rand" "testing" ) // 生产库里的真实 v1 授权码样本 const legacySampleCode = "0D82F1CCD83D72B78FA9" // generateV1 复刻 comm.GenerateLicense 的 v1 编码,仅供测试构造老码。 // 生产代码不再生成 v1,只解析。 func generateV1(productid uint16, batch byte, serial uint32) (string, error) { if batch < 1 { return "", fmt.Errorf("批次必须 >= 1") } if serial > 0xFFFFF { return "", fmt.Errorf("v1 序号上限 0xFFFFF") } seedBytes := make([]byte, 2) if _, err := cryptorand.Read(seedBytes); err != nil { return "", err } seed := int(seedBytes[0])<<8 | int(seedBytes[1]) rng := rand.New(rand.NewSource(int64(seed))) confusion := make([]int, 4) for i := range confusion { confusion[i] = rng.Intn(65536) } confusion[0] ^= int(productid) confusion[1] ^= (int(batch) << 8) | (int(serial>>12) & 0xFF) confusion[2] ^= int(serial) & 0xFFF confusion[3] ^= rng.Intn(65536) code := fmt.Sprintf("%04X", seed) for _, c := range confusion { code += fmt.Sprintf("%04X", c) } return code, nil } func TestParse_LegacySample(t *testing.T) { info, err := Parse(legacySampleCode) if err != nil { t.Fatalf("解析真实老码失败: %v", err) } if info.Version != V1 { t.Errorf("版本应为 v1, 实际 v%d", info.Version) } if info.ProductID != 45069 || info.Batch != 1 || info.Serial != 260 { t.Errorf("解析结果不符: pid=%d batch=%d serial=%d, 期望 45069/1/260", info.ProductID, info.Batch, info.Serial) } } // v2 往返:序号必须能占满 uint32 func TestGenerateV2_RoundTrip(t *testing.T) { pids := []uint16{0, 1, 45058, 45069, 0xFFFF} serials := []uint32{ 0, 1, 15, 16, 0xFFF, 0x1000, 0xFFFFF, 0x100000, 0xFFFFFFF, 0x10000000, 0x12345678, 0xFFFFFFFE, 0xFFFFFFFF, } for _, pid := range pids { for _, serial := range serials { code, err := GenerateV2(pid, serial) if err != nil { t.Fatalf("生成失败 pid=%d serial=%d: %v", pid, serial, err) } if len(code) != totalSegments*segmentLength { t.Fatalf("码长变了: %d, 期望 %d", len(code), totalSegments*segmentLength) } info, err := Parse(code) if err != nil { t.Fatalf("解析失败 %s: %v", code, err) } if info.Version != V2 { t.Errorf("%s 应识别为 v2, 实际 v%d", code, info.Version) } if info.ProductID != pid || info.Serial != serial { t.Errorf("往返不一致: 写入 pid=%d serial=%d, 读出 pid=%d serial=%d", pid, serial, info.ProductID, info.Serial) } } } } // probatch < 240 的 v1 码不能被误判成 v2,且 batch/serial 必须原样解出 func TestParse_V1NotMisdetected(t *testing.T) { const pid = 45069 serials := []uint32{0, 1, 4095, 4096, 65535, 0xFFFFF} for batch := 1; batch < V1ProbatchLimit; batch++ { for _, serial := range serials { code, err := generateV1(pid, byte(batch), serial) if err != nil { t.Fatalf("v1 生成失败 batch=%d serial=%d: %v", batch, serial, err) } info, err := Parse(code) if err != nil { t.Fatalf("v1 解析失败 batch=%d serial=%d: %v", batch, serial, err) } if info.Version != V1 { t.Fatalf("batch=%d serial=%d 的 v1 码被误判为 v%d", batch, serial, info.Version) } if info.ProductID != pid || info.Batch != byte(batch) || info.Serial != serial { t.Fatalf("v1 解析不一致 batch=%d serial=%d -> pid=%d batch=%d serial=%d", batch, serial, info.ProductID, info.Batch, info.Serial) } } } } // 文档化约束:batch >= 240 的 v1 码必然被误判为 v2(判别位 = batch>>4 == 0xF)。 // 迁移脚本必须先验证 MAX(probatch) < V1ProbatchLimit,否则老码序号会解出垃圾。 // 本测试若开始失败,说明判别位挪了位置,前提校验需要跟着改。 func TestParse_V1OverLimitIsMisdetected(t *testing.T) { const pid = 45069 for batch := V1ProbatchLimit; batch <= 0xFF; batch++ { code, err := generateV1(pid, byte(batch), 1) if err != nil { t.Fatalf("v1 生成失败 batch=%d: %v", batch, err) } info, err := Parse(code) if err != nil { t.Fatalf("解析失败 batch=%d: %v", batch, err) } if info.Version != V2 { t.Errorf("batch=%d 的 v1 码应当被误判为 v2(证明前提校验必要),实际 v%d", batch, info.Version) } } } // productid 的提取与版本无关,也不受 v1 批次异常影响 func TestProductID_VersionAgnostic(t *testing.T) { const pid = 45069 v1, err := generateV1(pid, 3, 1234) if err != nil { t.Fatalf("v1 生成失败: %v", err) } v2, err := GenerateV2(pid, 0xDEADBEEF) if err != nil { t.Fatalf("v2 生成失败: %v", err) } for _, code := range []string{v1, v2, legacySampleCode} { got, err := ProductID(code) if err != nil { t.Fatalf("%s 提取 pid 失败: %v", code, err) } if got != pid { t.Errorf("%s 提取 pid=%d, 期望 %d", code, got, pid) } } } // 同产品下连续序号生成的码互不相同(code <-> (seed, pid, serial) 双射) func TestGenerateV2_Uniqueness(t *testing.T) { const pid = 45069 seen := make(map[string]uint32, 20000) for serial := uint32(1); serial <= 20000; serial++ { code, err := GenerateV2(pid, serial) if err != nil { t.Fatalf("生成失败 serial=%d: %v", serial, err) } if prev, dup := seen[code]; dup { t.Fatalf("授权码碰撞: serial=%d 与 serial=%d 都生成 %s", serial, prev, code) } seen[code] = serial } }