Skip to content
quarry Artifact [2e4edc7f44]
Login

Artifact [2e4edc7f44]

Artifact 2e4edc7f444a72599ff26bf6bdc2760ccafb0a57e4be56d395608e0e803e383b:


package api

import (
	"sync"
	"testing"
)

// The registry replaced a bare map because repositories can now be
// added and removed while ops read it on the request path. A race here
// has no symptom until it corrupts one, so this is worth pinning under
// -race rather than reasoning about.
func TestReposConcurrentReadsAndWrites(t *testing.T) {
	rs := NewRepos(map[string]*Repo{"seed": {Code: "s"}})

	var wg sync.WaitGroup
	for i := 0; i < 8; i++ {
		wg.Add(2)
		go func(n int) {
			defer wg.Done()
			for j := 0; j < 200; j++ {
				rs.Get("seed")
				rs.Names()
				rs.Len()
			}
		}(i)
		go func(n int) {
			defer wg.Done()
			for j := 0; j < 200; j++ {
				name := string(rune('a' + n))
				rs.Put(name, &Repo{Code: name})
				rs.Delete(name)
			}
		}(i)
	}
	wg.Wait()

	if _, ok := rs.Get("seed"); !ok {
		t.Error("the seeded repo did not survive concurrent churn")
	}
}

// Names is sorted because callers render it: map order would reshuffle
// the repository list on every page load.
func TestReposNamesAreSorted(t *testing.T) {
	rs := NewRepos(map[string]*Repo{"zeta": {}, "alpha": {}, "mid": {}})
	got := rs.Names()
	want := []string{"alpha", "mid", "zeta"}
	for i := range want {
		if got[i] != want[i] {
			t.Fatalf("Names() = %v, want %v", got, want)
		}
	}
}

// Delete returns what it removed so the caller can close an index and
// stop a pipeline — both outlive the map entry otherwise.
func TestReposDeleteReturnsTheEntry(t *testing.T) {
	rs := NewRepos(map[string]*Repo{"a": {Code: "codeA"}})

	got, ok := rs.Delete("a")
	if !ok || got == nil || got.Code != "codeA" {
		t.Fatalf("Delete returned (%v, %v), want the entry", got, ok)
	}
	if _, ok := rs.Get("a"); ok {
		t.Error("entry still registered after Delete")
	}
	if _, ok := rs.Delete("a"); ok {
		t.Error("second Delete reported success")
	}
}

// A nil registry answers empty rather than panicking, so a Deps built
// without one (docs generation, some tests) still works.
func TestNilReposIsUsable(t *testing.T) {
	var rs *Repos
	if _, ok := rs.Get("x"); ok {
		t.Error("nil registry returned a repo")
	}
	if rs.Len() != 0 || rs.Names() != nil {
		t.Error("nil registry did not report empty")
	}
}