package gobridge import ( "context" "fmt" "reflect" "sync" "testing" ) // testGoService 对应 example/main.go 里的 goService,实现 Handler 接口, // 公开方法自动暴露给 Python 通过 call_go() 调用。用于覆盖 example/main.go // 的 demoServer 演示过的 WithHandlers/call_go 全双工功能。 type testGoService struct { pool Pool // 供 EnrichName 内部再调 Python mu sync.Mutex logLines []string } func (s *testGoService) Multiply(ctx context.Context, a, b int) (int, error) { return a * b, nil } func (s *testGoService) Log(msg string) { s.mu.Lock() defer s.mu.Unlock() s.logLines = append(s.logLines, msg) } func (s *testGoService) takeLogLines() []string { s.mu.Lock() defer s.mu.Unlock() lines := s.logLines s.logLines = nil return lines } // EnrichName 内部通过 Invoke 调用 Python 的 to_upper,构成 Go→Python→Go→Python 四层链路。 func (s *testGoService) EnrichName(ctx context.Context, name string) (string, error) { upper, err := Invoke[string](ctx, s.pool, "to_upper", name) if err != nil { return "", err } return "Hello, " + upper + "!", nil } func (s *testGoService) MakeUser(ctx context.Context, uid int) (testUser, error) { return testUser{ID: uid, Name: fmt.Sprintf("user_%d", uid), Score: float64(uid) * 1.5}, nil } func newTestServerPool(t *testing.T, svc *testGoService) Pool { t.Helper() pool := newTestPool(t, WithWorkers(1), WithHandlers(svc)) svc.pool = pool return pool } // TestHandlers 覆盖 example/main.go 的 demoServer 演示过的 WithHandlers/call_go // 全双工功能:Python 调用 Go 方法、流式输出过程中回调 Go、Go→Python→Go→Python // 四层链路、call_go[T] 自动构造 dataclass。 func TestHandlers(t *testing.T) { svc := &testGoService{} pool := newTestServerPool(t, svc) ctx := context.Background() t.Run("Python调用Go方法", func(t *testing.T) { result, err := Invoke[int](ctx, pool, "compute_with_go_mul", 6, 7) if err != nil { t.Fatalf("Invoke: %v", err) } if result != 42 { t.Fatalf("want 42, got %d", result) } }) t.Run("流式输出过程中回调Go", func(t *testing.T) { ch, err := Invoke[chan int](ctx, pool, "squared_with_log", 4) if err != nil { t.Fatalf("Invoke: %v", err) } var got []int for v := range ch { got = append(got, v) } want := []int{1, 4, 9, 16} if !reflect.DeepEqual(got, want) { t.Fatalf("want %v, got %v", want, got) } wantLogs := []string{ "yielding 1² = 1", "yielding 2² = 4", "yielding 3² = 9", "yielding 4² = 16", } if gotLogs := svc.takeLogLines(); !reflect.DeepEqual(gotLogs, wantLogs) { t.Fatalf("want log lines %v, got %v", wantLogs, gotLogs) } }) t.Run("四层全双工链路", func(t *testing.T) { // full_chain("world") → call_go[str]("EnrichName","world") // → Invoke[string](ctx, serv, "to_upper", "world") → "WORLD" // ← "Hello, WORLD!" greeting, err := Invoke[string](ctx, pool, "full_chain", "world") if err != nil { t.Fatalf("Invoke: %v", err) } if greeting != "Hello, WORLD!" { t.Fatalf("want %q, got %q", "Hello, WORLD!", greeting) } }) t.Run("call_go自动构造dataclass", func(t *testing.T) { enriched, err := Invoke[testUser](ctx, pool, "get_user_via_go", 12) if err != nil { t.Fatalf("Invoke: %v", err) } want := testUser{ID: 12, Name: "user_12", Score: 18, Level: "gold"} if enriched != want { t.Fatalf("want %+v, got %+v", want, enriched) } }) }