golang.org/x/tools/gopls@v0.15.3/internal/lsprpc/goenv_test.go (about)

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package lsprpc_test
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"os"
    12  	"testing"
    13  
    14  	"golang.org/x/tools/gopls/internal/protocol"
    15  	"golang.org/x/tools/internal/event"
    16  	jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2"
    17  	"golang.org/x/tools/internal/testenv"
    18  
    19  	. "golang.org/x/tools/gopls/internal/lsprpc"
    20  )
    21  
    22  func GoEnvMiddleware() (Middleware, error) {
    23  	return BindHandler(func(delegate jsonrpc2_v2.Handler) jsonrpc2_v2.Handler {
    24  		return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) {
    25  			if req.Method == "initialize" {
    26  				if err := addGoEnvToInitializeRequestV2(ctx, req); err != nil {
    27  					event.Error(ctx, "adding go env to initialize", err)
    28  				}
    29  			}
    30  			return delegate.Handle(ctx, req)
    31  		})
    32  	}), nil
    33  }
    34  
    35  // This function is almost identical to addGoEnvToInitializeRequest in lsprpc.go.
    36  // Make changes in parallel.
    37  func addGoEnvToInitializeRequestV2(ctx context.Context, req *jsonrpc2_v2.Request) error {
    38  	var params protocol.ParamInitialize
    39  	if err := json.Unmarshal(req.Params, &params); err != nil {
    40  		return err
    41  	}
    42  	var opts map[string]interface{}
    43  	switch v := params.InitializationOptions.(type) {
    44  	case nil:
    45  		opts = make(map[string]interface{})
    46  	case map[string]interface{}:
    47  		opts = v
    48  	default:
    49  		return fmt.Errorf("unexpected type for InitializationOptions: %T", v)
    50  	}
    51  	envOpt, ok := opts["env"]
    52  	if !ok {
    53  		envOpt = make(map[string]interface{})
    54  	}
    55  	env, ok := envOpt.(map[string]interface{})
    56  	if !ok {
    57  		return fmt.Errorf("env option is %T, expected a map", envOpt)
    58  	}
    59  	goenv, err := GetGoEnv(ctx, env)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	// We don't want to propagate GOWORK unless explicitly set since that could mess with
    64  	// path inference during cmd/go invocations, see golang/go#51825.
    65  	_, goworkSet := os.LookupEnv("GOWORK")
    66  	for govar, value := range goenv {
    67  		if govar == "GOWORK" && !goworkSet {
    68  			continue
    69  		}
    70  		env[govar] = value
    71  	}
    72  	opts["env"] = env
    73  	params.InitializationOptions = opts
    74  	raw, err := json.Marshal(params)
    75  	if err != nil {
    76  		return fmt.Errorf("marshaling updated options: %v", err)
    77  	}
    78  	req.Params = json.RawMessage(raw)
    79  	return nil
    80  }
    81  
    82  type initServer struct {
    83  	protocol.Server
    84  
    85  	params *protocol.ParamInitialize
    86  }
    87  
    88  func (s *initServer) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
    89  	s.params = params
    90  	return &protocol.InitializeResult{}, nil
    91  }
    92  
    93  func TestGoEnvMiddleware(t *testing.T) {
    94  	testenv.NeedsTool(t, "go")
    95  
    96  	ctx := context.Background()
    97  
    98  	server := &initServer{}
    99  	env := new(TestEnv)
   100  	defer env.Shutdown(t)
   101  	l, _ := env.serve(ctx, t, staticServerBinder(server))
   102  	mw, err := GoEnvMiddleware()
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	binder := mw(NewForwardBinder(l.Dialer()))
   107  	l, _ = env.serve(ctx, t, binder)
   108  	conn := env.dial(ctx, t, l.Dialer(), noopBinder, true)
   109  	dispatch := protocol.ServerDispatcherV2(conn)
   110  	initParams := &protocol.ParamInitialize{}
   111  	initParams.InitializationOptions = map[string]interface{}{
   112  		"env": map[string]interface{}{
   113  			"GONOPROXY": "example.com",
   114  		},
   115  	}
   116  	if _, err := dispatch.Initialize(ctx, initParams); err != nil {
   117  		t.Fatal(err)
   118  	}
   119  
   120  	if server.params == nil {
   121  		t.Fatalf("initialize params are unset")
   122  	}
   123  	envOpts := server.params.InitializationOptions.(map[string]interface{})["env"].(map[string]interface{})
   124  
   125  	// Check for an arbitrary Go variable. It should be set.
   126  	if _, ok := envOpts["GOPRIVATE"]; !ok {
   127  		t.Errorf("Go environment variable GOPRIVATE unset in initialization options")
   128  	}
   129  	// Check that the variable present in our user config was not overwritten.
   130  	if got, want := envOpts["GONOPROXY"], "example.com"; got != want {
   131  		t.Errorf("GONOPROXY=%q, want %q", got, want)
   132  	}
   133  }