package gobridge import ( "context" "reflect" "testing" ) // testUser 对应 example/worker.py 里的 User dataclass。 type testUser struct { ID int `json:"id"` Name string `json:"name"` Score float64 `json:"score"` Level string `json:"level,omitempty"` } // TestInvokeModes 覆盖 example/main.go 的 demoPool 演示过的四种调用模式 // (普通调用、流式输出、流式输入、双向流)在 int / struct / slice / []byte // 各种类型组合下的正确性,对应 README「四种调用模式」小节。 func TestInvokeModes(t *testing.T) { pool := newTestPool(t, WithWorkers(2), WithMaxConns(4)) ctx := context.Background() t.Run("普通调用_int", func(t *testing.T) { sum, err := Invoke[int](ctx, pool, "add", 3, 4) if err != nil { t.Fatalf("Invoke: %v", err) } if sum != 7 { t.Fatalf("want 7, got %d", sum) } }) t.Run("流式输出_int", func(t *testing.T) { ch, err := Invoke[chan int](ctx, pool, "range_gen", 1, 6) if err != nil { t.Fatalf("Invoke: %v", err) } var got []int for v := range ch { got = append(got, v) } want := []int{1, 2, 3, 4, 5} if !reflect.DeepEqual(got, want) { t.Fatalf("want %v, got %v", want, got) } }) t.Run("流式输入_int", func(t *testing.T) { inputCh := make(chan int, 10) go func() { for i := 1; i <= 5; i++ { inputCh <- i } close(inputCh) }() total, err := Invoke[int](ctx, pool, "sum_stream", inputCh) if err != nil { t.Fatalf("Invoke: %v", err) } if total != 15 { t.Fatalf("want 15, got %d", total) } }) t.Run("双向流_int", func(t *testing.T) { inputCh := make(chan int, 10) go func() { for i := 1; i <= 5; i++ { inputCh <- i } close(inputCh) }() outCh, err := Invoke[chan int](ctx, pool, "double_stream", inputCh) if err != nil { t.Fatalf("Invoke: %v", err) } var got []int for v := range outCh { got = append(got, v) } want := []int{1, 4, 9, 16, 25} if !reflect.DeepEqual(got, want) { t.Fatalf("want %v, got %v", want, got) } }) t.Run("普通调用_struct", func(t *testing.T) { user, err := Invoke[testUser](ctx, pool, "get_user", 42) if err != nil { t.Fatalf("Invoke: %v", err) } want := testUser{ID: 42, Name: "user_42", Score: 63} if user != want { t.Fatalf("want %+v, got %+v", want, user) } }) users := []testUser{ {ID: 1, Name: "alice", Score: 5.0}, {ID: 2, Name: "bob", Score: 8.0}, {ID: 3, Name: "carol", Score: 12.0}, } t.Run("slice输入_返回标量", func(t *testing.T) { scoreSum, err := Invoke[float64](ctx, pool, "total_score", users) if err != nil { t.Fatalf("Invoke: %v", err) } if scoreSum != 25 { t.Fatalf("want 25, got %v", scoreSum) } }) t.Run("slice输入输出", func(t *testing.T) { enriched, err := Invoke[[]testUser](ctx, pool, "enrich_users", users) if err != nil { t.Fatalf("Invoke: %v", err) } want := []testUser{ {ID: 1, Name: "alice", Score: 5.0, Level: "silver"}, {ID: 2, Name: "bob", Score: 8.0, Level: "silver"}, {ID: 3, Name: "carol", Score: 12.0, Level: "gold"}, } if !reflect.DeepEqual(enriched, want) { t.Fatalf("want %+v, got %+v", want, enriched) } }) t.Run("流式输出_struct", func(t *testing.T) { userCh, err := Invoke[chan testUser](ctx, pool, "gen_users", 3) if err != nil { t.Fatalf("Invoke: %v", err) } var got []testUser for u := range userCh { got = append(got, u) } want := []testUser{ {ID: 1, Name: "user_1", Score: 3}, {ID: 2, Name: "user_2", Score: 6}, {ID: 3, Name: "user_3", Score: 9}, } if !reflect.DeepEqual(got, want) { t.Fatalf("want %+v, got %+v", want, got) } }) t.Run("双向流_struct", func(t *testing.T) { inCh := make(chan testUser, len(users)) go func() { for _, u := range users { inCh <- u } close(inCh) }() procCh, err := Invoke[chan testUser](ctx, pool, "process_users", inCh) if err != nil { t.Fatalf("Invoke: %v", err) } var got []testUser for u := range procCh { got = append(got, u) } want := []testUser{ {ID: 1, Name: "ALICE", Score: 10}, {ID: 2, Name: "BOB", Score: 16}, {ID: 3, Name: "CAROL", Score: 24}, } if !reflect.DeepEqual(got, want) { t.Fatalf("want %+v, got %+v", want, got) } }) t.Run("bytes_输入输出", func(t *testing.T) { rev, err := Invoke[[]byte](ctx, pool, "bytes_reverse", []byte("hello")) if err != nil { t.Fatalf("Invoke: %v", err) } if string(rev) != "olleh" { t.Fatalf("want olleh, got %s", rev) } cat, err := Invoke[[]byte](ctx, pool, "bytes_concat", []byte("foo"), []byte("bar")) if err != nil { t.Fatalf("Invoke: %v", err) } if string(cat) != "foobar" { t.Fatalf("want foobar, got %s", cat) } }) t.Run("bytes_流式输出", func(t *testing.T) { bCh, err := Invoke[chan []byte](ctx, pool, "bytes_chunks", []byte("abcdefgh"), 3) if err != nil { t.Fatalf("Invoke: %v", err) } var got []string for chunk := range bCh { got = append(got, string(chunk)) } want := []string{"abc", "def", "gh"} if !reflect.DeepEqual(got, want) { t.Fatalf("want %v, got %v", want, got) } }) }