f1bcb8cd3a
repospawner turns JSON new-repo requests into terraform-git pull requests via kubernetes Jobs, follows those PRs to merge and optionally activates the repository in Woodpecker.
112 lines
2.7 KiB
Go
112 lines
2.7 KiB
Go
package store
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.unkin.net/unkin/repospawner/internal/repospec"
|
|
)
|
|
|
|
func fixedClock(base time.Time) (*Store, func(time.Duration)) {
|
|
s := New()
|
|
now := base
|
|
s.SetClock(func() time.Time { return now })
|
|
return s, func(d time.Duration) { now = now.Add(d) }
|
|
}
|
|
|
|
func TestListNewestFirst(t *testing.T) {
|
|
s, advance := fixedClock(time.Date(2026, 8, 30, 0, 0, 0, 0, time.UTC))
|
|
for _, name := range []string{"first", "second", "third"} {
|
|
s.Put(NewRequest(name, repospec.Request{Name: name}, s.Now()))
|
|
advance(time.Minute)
|
|
}
|
|
got := s.List()
|
|
if len(got) != 3 {
|
|
t.Fatalf("List returned %d requests", len(got))
|
|
}
|
|
for i, want := range []string{"third", "second", "first"} {
|
|
if got[i].Name != want {
|
|
t.Errorf("List()[%d] = %q, want %q", i, got[i].Name, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPutOnlyStampsUpdatedOnChange(t *testing.T) {
|
|
s, advance := fixedClock(time.Date(2026, 8, 30, 0, 0, 0, 0, time.UTC))
|
|
r := NewRequest("id", repospec.Request{Name: "widget", StatusChecks: []string{"a"}}, s.Now())
|
|
s.Put(r)
|
|
stored, _ := s.Get("id")
|
|
|
|
advance(time.Hour)
|
|
s.Put(r)
|
|
again, _ := s.Get("id")
|
|
if !again.Updated.Equal(stored.Updated) {
|
|
t.Errorf("Updated moved on an unchanged Put: %v -> %v", stored.Updated, again.Updated)
|
|
}
|
|
|
|
r.State = StatePROpen
|
|
s.Put(r)
|
|
changed, _ := s.Get("id")
|
|
if !changed.Updated.After(stored.Updated) {
|
|
t.Errorf("Updated did not move on a real change: %v", changed.Updated)
|
|
}
|
|
if !changed.Created.Equal(stored.Created) {
|
|
t.Errorf("Created must not move: %v -> %v", stored.Created, changed.Created)
|
|
}
|
|
}
|
|
|
|
func TestHasActiveName(t *testing.T) {
|
|
s := New()
|
|
r := NewRequest("id", repospec.Request{Name: "widget"}, time.Now())
|
|
s.Put(r)
|
|
if !s.HasActiveName("widget") {
|
|
t.Error("an in-flight request must claim its name")
|
|
}
|
|
if s.HasActiveName("other") {
|
|
t.Error("an unrelated name must be free")
|
|
}
|
|
|
|
for _, state := range []State{StateReady, StateClosed, StateFailed} {
|
|
r.State = state
|
|
s.Put(r)
|
|
if s.HasActiveName("widget") {
|
|
t.Errorf("state %q is terminal and must release the name", state)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewIDIsUnique(t *testing.T) {
|
|
seen := map[string]bool{}
|
|
for i := 0; i < 1000; i++ {
|
|
id := NewID()
|
|
if id == "" {
|
|
t.Fatal("NewID returned an empty id")
|
|
}
|
|
if seen[id] {
|
|
t.Fatalf("NewID repeated %q", id)
|
|
}
|
|
seen[id] = true
|
|
}
|
|
}
|
|
|
|
func TestConcurrentAccess(t *testing.T) {
|
|
s := New()
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 32; i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
id := NewID()
|
|
s.Put(NewRequest(id, repospec.Request{Name: "widget"}, time.Now()))
|
|
s.Get(id)
|
|
s.List()
|
|
s.HasActiveName("widget")
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
if len(s.List()) != 32 {
|
|
t.Errorf("List returned %d requests, want 32", len(s.List()))
|
|
}
|
|
}
|