Repository: grpc/grpc-go
File: test/clientconn_state_transition_test.go (Go)

Code before:

	cc, err := grpc.NewClient(mr.Scheme()+":///", grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		t.Fatalf("Failed to create new client: %v", err)
	}
	defer cc.Close()

	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()

	shortCtx, shortCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
	defer shortCancel()

	cc.Connect()
	testutils.AwaitState(shortCtx, t, cc, connectivity.Idle)

	mr.UpdateState(resolver.State{
		Addresses: []resolver.Address{{Addr: backend.Address}},
	})

	// Check that the channel transitions to CONNECTING then READY
	testutils.AwaitState(shortCtx, t, cc, connectivity.Connecting)
	t.Skipf("After connecting, before READY state: current state = %v\n", cc.GetState())

	testutils.AwaitState(shortCtx, t, cc, connectivity.Ready)
	t.Skipf("After entering READY state: current state = %v\n", cc.GetState())

	mr.UpdateState(resolver.State{Addresses: nil})

	done := make(chan struct{})
	go func() {
		_, err := testgrpc.NewTestServiceClient(cc).EmptyCall(shortCtx, &testgrpc.Empty{})
		if err == nil {
			t.Errorf("Expected RPC to fail, but it succeeded")
		}
		close(done)
	}()

	// Wait for the goroutine to finish
	select {
	case <-done:
		// RPC call completed as expected
	case <-shortCtx.Done():
		t.Error("Timed out waiting for RPC call to complete")
	}
}


Review comment (suggestion):
`Skip` skips the rest of the test, so just put one at the top of the test function and assert the proper behavior here instead.

Code after:
	mr := manual.NewBuilderWithScheme("e2e-test")
	defer mr.Close()

	cc, err := grpc.NewClient(mr.Scheme()+":///", grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		t.Fatalf("Failed to create new client: %v", err)
	}
	defer cc.Close()

	if state := cc.GetState(); state != connectivity.Idle {
		t.Fatalf("Expected initial state to be IDLE, got %v", state)
	}

	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()

	// The channel should transition to CONNECTING automatically when Connect()
	// is called.
	cc.Connect()
	testutils.AwaitState(ctx, t, cc, connectivity.Connecting)

	// Verify that the channel remains in CONNECTING state for a short time.
	shortCtx, shortCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
	defer shortCancel()
	testutils.AwaitNoStateChange(shortCtx, t, cc, connectivity.Connecting)
}

func (s) TestChannelStateTransitionWithRPC(t *testing.T) {
	t.Skip("The channel remains in IDLE until the LB policy updates the state to CONNECTING. This is a bug and the channel should transition to CONNECTING as soon as an RPC call is made. See issue #7686.")

	backend := stubserver.StartTestService(t, nil)
	defer backend.Stop()

	mr := manual.NewBuilderWithScheme("e2e-test")
	defer mr.Close()

	cc, err := grpc.NewClient(mr.Scheme()+":///", grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		t.Fatalf("Failed to create new client: %v", err)
	}
	defer cc.Close()

	if state := cc.GetState(); state != connectivity.Idle {
		t.Fatalf("Expected initial state to be IDLE, got %v", state)
	}

	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()

	// Make an RPC call to transition the channel to CONNECTING.
	go func() {