package backpack import ( "sync" "testing" "git.golem15.com/golem15/summercms/modules/compass" "git.golem15.com/golem15/summercms/modules/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 }