This commit is contained in:
Yvan
2026-09-22 20:20:23 +08:00
parent bf33e87d72
commit ceaea78b0f
42 changed files with 2845 additions and 154 deletions
+16
View File
@@ -0,0 +1,16 @@
package serial
import (
"crypto/rand"
"encoding/base32"
"fmt"
)
// New appends a 26-character base32 value with 128 random bits to prefix.
func New(prefix string) (string, error) {
var entropy [16]byte
if _, err := rand.Read(entropy[:]); err != nil {
return "", fmt.Errorf("generate serial number: %w", err)
}
return prefix + base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(entropy[:]), nil
}
+96
View File
@@ -0,0 +1,96 @@
package serial
import (
"encoding/base32"
"regexp"
"strings"
"sync"
"testing"
)
var validSerial = regexp.MustCompile(`^(DS|CZ)[A-Z2-7]{26}$`)
func TestNewPreservesPrefixAndPaymentSafeLength(t *testing.T) {
for _, prefix := range []string{"DS", "CZ"} {
t.Run(prefix, func(t *testing.T) {
value, err := New(prefix)
if err != nil {
t.Fatal(err)
}
if len(value) != 28 || !strings.HasPrefix(value, prefix) || !validSerial.MatchString(value) {
t.Fatalf("unexpected serial format: %q", value)
}
entropy, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(strings.TrimPrefix(value, prefix))
if err != nil || len(entropy) != 16 {
t.Fatalf("serial does not carry 128 bits: bytes=%d error=%v", len(entropy), err)
}
})
}
}
func TestNewIsUniqueAcrossRapidSequentialCalls(t *testing.T) {
const count = 10000
for _, prefix := range []string{"DS", "CZ"} {
t.Run(prefix, func(t *testing.T) {
seen := make(map[string]struct{}, count)
for index := 0; index < count; index++ {
value, err := New(prefix)
if err != nil {
t.Fatal(err)
}
if _, exists := seen[value]; exists {
t.Fatalf("duplicate serial at call %d: %q", index, value)
}
seen[value] = struct{}{}
}
})
}
}
func TestNewIsUniqueAcrossParallelCalls(t *testing.T) {
const workers, perWorker = 32, 500
type result struct {
value string
err error
}
start := make(chan struct{})
results := make(chan result, workers)
var group sync.WaitGroup
for worker := 0; worker < workers; worker++ {
group.Add(1)
go func(worker int) {
defer group.Done()
prefix := "DS"
if worker%2 == 0 {
prefix = "CZ"
}
<-start
for index := 0; index < perWorker; index++ {
value, err := New(prefix)
results <- result{value: value, err: err}
}
}(worker)
}
go func() {
group.Wait()
close(results)
}()
close(start)
seen := make(map[string]struct{}, workers*perWorker)
for result := range results {
if result.err != nil {
t.Errorf("generate parallel serial: %v", result.err)
continue
}
if !validSerial.MatchString(result.value) || len(result.value) > 32 {
t.Errorf("invalid parallel serial format: %q", result.value)
}
if _, exists := seen[result.value]; exists {
t.Errorf("duplicate parallel serial: %q", result.value)
}
seen[result.value] = struct{}{}
}
if len(seen) != workers*perWorker {
t.Fatalf("unique serials=%d, want %d", len(seen), workers*perWorker)
}
}