97 lines
2.4 KiB
Go
97 lines
2.4 KiB
Go
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)
|
|
}
|
|
}
|