package rpc import ( "context" "os" "sync" "testing" "forge.cadoles.com/arcad/edge/pkg/app" "forge.cadoles.com/arcad/edge/pkg/bus" "forge.cadoles.com/arcad/edge/pkg/bus/memory" "forge.cadoles.com/arcad/edge/pkg/module" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) func TestServerExecDeadlock(t *testing.T) { if testing.Verbose() { logger.SetLevel(logger.LevelDebug) } b := memory.NewBus(memory.WithBufferSize(1)) server := app.NewServer( module.ConsoleModuleFactory(), ModuleFactory(b), module.LifecycleModuleFactory(), ) data, err := os.ReadFile("testdata/deadlock.js") if err != nil { t.Fatalf("%+v", errors.WithStack(err)) } ctx := context.Background() t.Log("starting server") if err := server.Start(ctx, "deadlock.js", string(data)); err != nil { t.Fatalf("%+v", errors.WithStack(err)) } defer server.Stop() t.Log("server started") count := 100 delay := 100 var wg sync.WaitGroup wg.Add(count) for i := 0; i < count; i++ { go func(i int) { defer wg.Done() t.Logf("calling %d", i) isCanceled := i%2 == 0 var ctx context.Context if isCanceled { canceledCtx, cancel := context.WithCancel(context.Background()) cancel() ctx = canceledCtx } else { ctx = context.Background() } env := NewRequestEnvelope(ctx, "doSomethingLong", map[string]any{ "i": i, "delay": delay, }) t.Logf("publishing envelope #%d", i) reply, err := b.Request(ctx, env) if err != nil { if errors.Is(err, context.Canceled) && isCanceled { return } if errors.Is(err, bus.ErrNoResponse) && isCanceled { return } t.Errorf("%+v", errors.WithStack(err)) return } result, ok := reply.Message().(int64) if !ok { t.Errorf("response.Result: expected type '%T', got '%T'", int64(0), reply.Message()) return } if e, g := i, int(result); e != g { t.Errorf("response.Result: expected '%v', got '%v'", e, g) return } }(i) } wg.Wait() }