init
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
package site
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/iceBear67/simplepages/internal/store"
|
||||
)
|
||||
|
||||
func TestRegistryLookupAndDelete(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
if _, ok := r.Lookup("demo"); ok {
|
||||
t.Fatal("an empty registry answered a lookup")
|
||||
}
|
||||
if r.Len() != 0 {
|
||||
t.Fatalf("Len = %d on an empty registry", r.Len())
|
||||
}
|
||||
|
||||
sp := r.Put(&store.Project{ID: 7, Name: "demo", IndexFile: "index.html"})
|
||||
got, ok := r.Lookup("demo")
|
||||
if !ok || got != sp {
|
||||
t.Fatal("Put did not publish the project it returned")
|
||||
}
|
||||
if got.ID != 7 || got.Name != "demo" {
|
||||
t.Errorf("entry = %+v", got)
|
||||
}
|
||||
if r.Len() != 1 {
|
||||
t.Errorf("Len = %d, want 1", r.Len())
|
||||
}
|
||||
|
||||
r.Delete("demo")
|
||||
if _, ok := r.Lookup("demo"); ok {
|
||||
t.Error("a deleted project still resolves")
|
||||
}
|
||||
r.Delete("demo") // deleting twice is not an error
|
||||
r.Delete("never-existed")
|
||||
}
|
||||
|
||||
// A settings change must not disturb what the project is currently serving: the
|
||||
// entry is updated in place rather than rebuilt, so the active pointer survives.
|
||||
func TestRegistryPutKeepsTheActiveDeployment(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
p := &store.Project{ID: 1, Name: "demo", IndexFile: "index.html"}
|
||||
sp := r.Put(p)
|
||||
d := fixture("index.html")
|
||||
sp.Activate(d)
|
||||
|
||||
p2 := &store.Project{ID: 1, Name: "demo", IndexFile: "main.html", SPAFallback: true}
|
||||
again := r.Put(p2)
|
||||
if again != sp {
|
||||
t.Fatal("Put replaced the live entry instead of updating it")
|
||||
}
|
||||
if again.Active() != d {
|
||||
t.Fatal("updating the settings dropped the active deployment")
|
||||
}
|
||||
if cfg := again.Config(); cfg.IndexFile != "main.html" || !cfg.SPAFallback {
|
||||
t.Errorf("config = %+v, want the new settings", cfg)
|
||||
}
|
||||
}
|
||||
|
||||
// A project recreated under the same name is a different project, so its entry
|
||||
// must start empty rather than inherit the old one's deployment.
|
||||
func TestRegistryPutReplacesAnEntryWithADifferentID(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
sp := r.Put(&store.Project{ID: 1, Name: "demo"})
|
||||
sp.Activate(fixture("index.html"))
|
||||
|
||||
again := r.Put(&store.Project{ID: 2, Name: "demo"})
|
||||
if again == sp {
|
||||
t.Fatal("a different project id reused the old entry")
|
||||
}
|
||||
if again.Active() != nil {
|
||||
t.Error("the new project inherited the old project's deployment")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistryReplace(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
r.Put(&store.Project{ID: 1, Name: "gone"})
|
||||
r.Replace([]*Project{
|
||||
NewProject(&store.Project{ID: 2, Name: "a"}),
|
||||
NewProject(&store.Project{ID: 3, Name: "b"}),
|
||||
})
|
||||
if _, ok := r.Lookup("gone"); ok {
|
||||
t.Error("Replace kept a project that is not in the new set")
|
||||
}
|
||||
if r.Len() != 2 {
|
||||
t.Fatalf("Len = %d, want 2", r.Len())
|
||||
}
|
||||
names := map[string]bool{}
|
||||
for _, p := range r.Projects() {
|
||||
names[p.Name] = true
|
||||
}
|
||||
if !names["a"] || !names["b"] {
|
||||
t.Errorf("Projects = %v", names)
|
||||
}
|
||||
}
|
||||
|
||||
// The API's ownership guard compares resolved ids, so this is the lookup that
|
||||
// decides whether one project's key can touch another's deployment.
|
||||
func TestRegistryResolveProject(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
r.Put(&store.Project{ID: 42, Name: "demo"})
|
||||
|
||||
id, err := r.ResolveProject(context.Background(), "demo")
|
||||
if err != nil || id != 42 {
|
||||
t.Fatalf("ResolveProject = (%d, %v), want (42, nil)", id, err)
|
||||
}
|
||||
if _, err := r.ResolveProject(context.Background(), "other"); !errors.Is(err, store.ErrNotFound) {
|
||||
t.Fatalf("ResolveProject on an unknown name = %v, want ErrNotFound", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Readers take no lock at all, so this is worth running under -race: a writer
|
||||
// mutating the map in place instead of cloning it would show up here.
|
||||
func TestRegistryConcurrentReadersAndWriters(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
r.Put(&store.Project{ID: 1, Name: "stable"})
|
||||
stop := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for range 8 {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
default:
|
||||
}
|
||||
if p, ok := r.Lookup("stable"); !ok || p.ID != 1 {
|
||||
t.Error("the stable project vanished while other projects changed")
|
||||
return
|
||||
}
|
||||
r.Lookup("churn")
|
||||
r.Len()
|
||||
}
|
||||
}()
|
||||
}
|
||||
for i := range 500 {
|
||||
r.Put(&store.Project{ID: int64(i + 2), Name: "churn"})
|
||||
r.Delete("churn")
|
||||
}
|
||||
close(stop)
|
||||
wg.Wait()
|
||||
}
|
||||
Reference in New Issue
Block a user