Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

services/luci/internal/web/shortid_test.go

Raw
package web

import (
	"math/rand/v2"
	"reflect"
	"strconv"
	"strings"
	"testing"

	"bugabinga.net/luci/internal/history"
	"bugabinga.net/luci/internal/inbox"
	"bugabinga.net/luci/internal/proquint"
	"bugabinga.net/luci/internal/runstate"
)

func TestBuildShortenerMatchesReference(t *testing.T) {
	tests := []struct {
		name           string
		events         []history.Event
		pending        []inbox.Pending
		active         []runstate.Active
		wantRunShort   map[string]string
		wantNoRunShort []string
		wantRevShort   map[string]string
		wantNoRevShort []string
	}{
		{
			name: "parse failures and zero aliases",
			events: []history.Event{
				{RunID: "0", Rev: "abcd"},
				{RunID: "+0", Rev: "abc"},
				{RunID: "not-a-number", Rev: "abcd0"},
			},
			pending:        []inbox.Pending{{ID: "9223372036854775808"}},
			active:         []runstate.Active{{RunID: "-0", Rev: "abce"}},
			wantNoRunShort: []string{"0", "+0", "-0", "not-a-number", "9223372036854775808"},
			wantRevShort:   map[string]string{"abcd0": "abcd0", "abce": "abce"},
			wantNoRevShort: []string{"abc"},
		},
		{
			name: "negative ids and block boundaries",
			events: []history.Event{
				{RunID: "0"},
				{RunID: "1"},
				{RunID: "65536"},
				{RunID: "-1"},
			},
			wantRunShort: map[string]string{
				"0":     proquint.Encode(0),
				"1":     proquint.Encode(1),
				"65536": proquint.Encode(65536)[:17],
				"-1":    proquint.Encode(-1)[:5],
			},
		},
		{
			name: "duplicate equivalent encodings",
			events: []history.Event{
				{RunID: "1"},
				{RunID: "0001"},
			},
			pending:        []inbox.Pending{{ID: "+1"}},
			wantNoRunShort: []string{"1", "0001", "+1"},
		},
		{
			name: "revision boundaries",
			events: []history.Event{
				{RunID: "1", Rev: "abc"},
				{RunID: "2", Rev: "abcd"},
				{RunID: "3", Rev: "abcd0"},
				{RunID: "4", Rev: "abce"},
				{RunID: "5", Rev: ""},
			},
			wantRevShort:   map[string]string{"abcd0": "abcd0", "abce": "abce"},
			wantNoRevShort: []string{"abc", "abcd"},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			want := referenceShortener(test.events, test.pending, test.active)
			for range 100 {
				if got := buildShortener(test.events, test.pending, test.active); !reflect.DeepEqual(got, want) {
					t.Fatalf("shortener=%#v want %#v", got, want)
				}
			}
			got := buildShortener(test.events, test.pending, test.active)
			for id, short := range test.wantRunShort {
				if got.runShort[id] != short {
					t.Fatalf("run id %q shortened to %q want %q", id, got.runShort[id], short)
				}
			}
			for _, id := range test.wantNoRunShort {
				if _, ok := got.runShort[id]; ok {
					t.Fatalf("run id %q unexpectedly shortened to %q", id, got.runShort[id])
				}
			}
			for rev, short := range test.wantRevShort {
				if got.revShort[rev] != short {
					t.Fatalf("revision %q shortened to %q want %q", rev, got.revShort[rev], short)
				}
			}
			for _, rev := range test.wantNoRevShort {
				if _, ok := got.revShort[rev]; ok {
					t.Fatalf("revision %q unexpectedly shortened to %q", rev, got.revShort[rev])
				}
			}
		})
	}
}

func TestBuildShortenerMatchesReferenceRandomized(t *testing.T) {
	random := rand.New(rand.NewPCG(20260927, 1))
	for set := range 50 {
		events, pending, active := randomShortenerInputs(random, 1+random.IntN(80))
		want := referenceShortener(events, pending, active)
		for range 10 {
			if got := buildShortener(events, pending, active); !reflect.DeepEqual(got, want) {
				t.Fatalf("set %d: shortener=%#v want %#v", set, got, want)
			}
		}
	}
}

func randomShortenerInputs(random *rand.Rand, count int) ([]history.Event, []inbox.Pending, []runstate.Active) {
	events := make([]history.Event, 0, count)
	pending := make([]inbox.Pending, 0, count)
	active := make([]runstate.Active, 0, count)
	for i := range count {
		id := randomShortenerID(random, i)
		rev := randomShortenerRevision(random, i)
		switch i % 3 {
		case 0:
			events = append(events, history.Event{RunID: id, Rev: rev})
		case 1:
			pending = append(pending, inbox.Pending{ID: id})
		case 2:
			active = append(active, runstate.Active{RunID: id, Rev: rev})
		}
	}
	return events, pending, active
}

func randomShortenerID(random *rand.Rand, index int) string {
	switch index % 8 {
	case 0:
		return strconv.FormatInt(random.Int64(), 10)
	case 1:
		return "0"
	case 2:
		return "+0"
	case 3:
		return "-0"
	case 4:
		return "invalid-" + strconv.Itoa(random.IntN(8))
	case 5:
		return "9223372036854775808"
	case 6:
		return "000" + strconv.Itoa(random.IntN(4))
	default:
		return strconv.FormatInt(-random.Int64N(1<<32), 10)
	}
}

func randomShortenerRevision(random *rand.Rand, index int) string {
	length := index % 9
	if length == 0 {
		return ""
	}
	const hex = "0123456789abcdef"
	var text strings.Builder
	for range length {
		text.WriteByte(hex[random.IntN(len(hex))])
	}
	return text.String()
}

// referenceShortener preserves buildShortener's pre-optimization algorithm.
func referenceShortener(events []history.Event, pending []inbox.Pending, active []runstate.Active) shortener {
	ids := map[string]bool{}
	for _, event := range events {
		ids[event.RunID] = true
	}
	for _, current := range pending {
		ids[current.ID] = true
	}
	for _, current := range active {
		ids[current.RunID] = true
	}
	revs := map[string]bool{}
	for _, event := range events {
		if event.Rev != "" {
			revs[event.Rev] = true
		}
	}
	for _, current := range active {
		if current.Rev != "" {
			revs[current.Rev] = true
		}
	}
	runShort := map[string]string{}
	for id := range ids {
		blocks := strings.Split(proquint.Encode(parseID(id)), "-")
		for keep := 1; keep <= len(blocks); keep++ {
			prefix := strings.Join(blocks[:keep], "-")
			if referenceUniquePrefix(ids, prefix) {
				runShort[id] = prefix
				break
			}
		}
	}
	revShort := map[string]string{}
	for rev := range revs {
		for length := 4; length <= len(rev); length++ {
			if referenceUniqueHexPrefix(revs, rev[:length]) {
				revShort[rev] = rev[:length]
				break
			}
		}
	}
	return shortener{runShort: runShort, revShort: revShort}
}

func referenceUniquePrefix(ids map[string]bool, prefix string) bool {
	count := 0
	for id := range ids {
		if strings.HasPrefix(proquint.Encode(parseID(id)), prefix) {
			count++
		}
	}
	return count == 1
}

func referenceUniqueHexPrefix(revs map[string]bool, prefix string) bool {
	count := 0
	for rev := range revs {
		if strings.HasPrefix(rev, prefix) {
			count++
		}
	}
	return count == 1
}