- Table-driven compass precedence, malformed YAML, snake_case env and concurrent reads - Party duplicate/missing/cycle, reordered manifest, HasConfig/HasCommands discovery - Isolated backpack services, festival panic payload omission, towel nested isolation
162 lines
4.0 KiB
Go
162 lines
4.0 KiB
Go
package backpack
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/compass"
|
|
"git.golem15.com/golem15/summercms/pact"
|
|
)
|
|
|
|
type ping interface {
|
|
Ping() string
|
|
}
|
|
|
|
type pingImpl struct{ s string }
|
|
|
|
func (p pingImpl) Ping() string { return p.s }
|
|
|
|
func TestPublishLookup(t *testing.T) {
|
|
app := New(&compass.Config{})
|
|
if err := app.Publish[ping](pingImpl{s: "pong"}); err != nil {
|
|
t.Fatalf("Publish: %v", err)
|
|
}
|
|
got, ok := app.Lookup[ping]()
|
|
if !ok {
|
|
t.Fatal("Lookup returned false")
|
|
}
|
|
if got.Ping() != "pong" {
|
|
t.Fatalf("Ping = %q, want pong", got.Ping())
|
|
}
|
|
}
|
|
|
|
func TestLookupAbsentInterface(t *testing.T) {
|
|
app := New(&compass.Config{})
|
|
got, ok := app.Lookup[ping]()
|
|
if ok {
|
|
t.Fatalf("Lookup = (%v, true), want false", got)
|
|
}
|
|
if got != nil {
|
|
t.Fatalf("absent lookup value = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestPublishDuplicateRejected(t *testing.T) {
|
|
app := New(&compass.Config{})
|
|
if err := app.Publish[ping](pingImpl{s: "a"}); err != nil {
|
|
t.Fatalf("Publish: %v", err)
|
|
}
|
|
if err := app.Publish[ping](pingImpl{s: "b"}); err == nil {
|
|
t.Fatal("expected duplicate publish error")
|
|
}
|
|
}
|
|
|
|
func TestServiceRegistriesAreAppScoped(t *testing.T) {
|
|
a := New(&compass.Config{})
|
|
b := New(&compass.Config{})
|
|
if err := a.Publish[ping](pingImpl{s: "a"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, ok := b.Lookup[ping](); ok {
|
|
t.Fatal("app B should not see app A's service")
|
|
}
|
|
}
|
|
|
|
func TestHasPluginUsesCompleteSet(t *testing.T) {
|
|
app := New(&compass.Config{})
|
|
app.SetPlugins([]string{"golem15.hello", "golem15.optional"})
|
|
if !app.HasPlugin("golem15.optional") {
|
|
t.Fatal("HasPlugin(golem15.optional) = false")
|
|
}
|
|
if app.HasPlugin("golem15.missing") {
|
|
t.Fatal("HasPlugin(golem15.missing) = true")
|
|
}
|
|
}
|
|
|
|
func TestTwoAppsIsolateHasPluginAndServices(t *testing.T) {
|
|
a := New(&compass.Config{})
|
|
b := New(&compass.Config{})
|
|
a.SetPlugins([]string{"golem15.optional"})
|
|
b.SetPlugins([]string{"golem15.hello"})
|
|
if err := a.Publish[pact.OptionalMessage](msgService{s: "from-a"}); err != nil {
|
|
t.Fatalf("Publish A: %v", err)
|
|
}
|
|
|
|
if !a.HasPlugin("golem15.optional") {
|
|
t.Fatal("app A should see optional")
|
|
}
|
|
if a.HasPlugin("golem15.hello") {
|
|
t.Fatal("app A should not see hello")
|
|
}
|
|
if !b.HasPlugin("golem15.hello") {
|
|
t.Fatal("app B should see hello")
|
|
}
|
|
if b.HasPlugin("golem15.optional") {
|
|
t.Fatal("app B should not see optional")
|
|
}
|
|
|
|
got, ok := a.Lookup[pact.OptionalMessage]()
|
|
if !ok || got.Message() != "from-a" {
|
|
t.Fatalf("app A lookup = (%v, %v)", got, ok)
|
|
}
|
|
if _, ok := b.Lookup[pact.OptionalMessage](); ok {
|
|
t.Fatal("app B must not see app A's OptionalMessage")
|
|
}
|
|
}
|
|
|
|
func TestPublishNilRejectedAndLookupStaysAbsent(t *testing.T) {
|
|
app := New(&compass.Config{})
|
|
var nilPing ping
|
|
if err := app.Publish[ping](nilPing); err == nil {
|
|
t.Fatal("expected nil publish error")
|
|
}
|
|
if _, ok := app.Lookup[ping](); ok {
|
|
t.Fatal("nil publish must not store a value")
|
|
}
|
|
}
|
|
|
|
func TestDuplicatePublishKeepsFirstValue(t *testing.T) {
|
|
app := New(&compass.Config{})
|
|
if err := app.Publish[ping](pingImpl{s: "first"}); err != nil {
|
|
t.Fatalf("Publish: %v", err)
|
|
}
|
|
if err := app.Publish[ping](pingImpl{s: "second"}); err == nil {
|
|
t.Fatal("expected duplicate error")
|
|
}
|
|
got, ok := app.Lookup[ping]()
|
|
if !ok || got.Ping() != "first" {
|
|
t.Fatalf("Lookup = (%v, %v), want first", got, ok)
|
|
}
|
|
}
|
|
|
|
func TestConcurrentLookupAndHasPlugin(t *testing.T) {
|
|
app := New(&compass.Config{})
|
|
app.SetPlugins([]string{"golem15.hello"})
|
|
if err := app.Publish[ping](pingImpl{s: "pong"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 16; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 80; j++ {
|
|
got, ok := app.Lookup[ping]()
|
|
if !ok || got.Ping() != "pong" {
|
|
t.Errorf("Lookup = (%v, %v)", got, ok)
|
|
return
|
|
}
|
|
if !app.HasPlugin("golem15.hello") || app.HasPlugin("golem15.missing") {
|
|
t.Error("HasPlugin concurrent mismatch")
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
type msgService struct{ s string }
|
|
|
|
func (m msgService) Message() string { return m.s }
|