github.com/jfrog/jfrog-cli-platform-services@v1.2.0/commands/list_cmd_test.go (about)

     1  package commands
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"os"
    11  	"regexp"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/jfrog/jfrog-cli-platform-services/model"
    20  )
    21  
    22  func TestListCommand(t *testing.T) {
    23  	tests := []struct {
    24  		name           string
    25  		commandArgs    []string
    26  		token          string
    27  		serverBehavior listServerStubBehavior
    28  		wantErr        error
    29  		assertOutput   func(t *testing.T, content []byte)
    30  	}{
    31  		{
    32  			name: "list",
    33  			serverBehavior: listServerStubBehavior{
    34  				existingWorkers: []*model.WorkerDetails{
    35  					{
    36  						Key:         "wk-0",
    37  						Action:      model.ActionAfterCreate,
    38  						Description: "run wk-0",
    39  						Enabled:     true,
    40  						SourceCode:  "export default async () => ({ 'S': 'OK'})",
    41  					},
    42  				},
    43  			},
    44  			assertOutput: func(t *testing.T, content []byte) {
    45  				assert.Equalf(t, "wk-0,AFTER_CREATE,run wk-0,true", strings.TrimSpace(string(content)), "invalid csv received")
    46  			},
    47  		},
    48  		{
    49  			name:        "list worker of type",
    50  			commandArgs: []string{"AFTER_CREATE"},
    51  			serverBehavior: listServerStubBehavior{
    52  				wantAction: "AFTER_CREATE",
    53  				existingWorkers: []*model.WorkerDetails{
    54  					{
    55  						Key:         "wk-0",
    56  						Action:      model.ActionAfterCreate,
    57  						Description: "run wk-0",
    58  						Enabled:     true,
    59  						SourceCode:  "export default async () => ({ 'S': 'OK'})",
    60  					},
    61  					{
    62  						Key:         "wk-1",
    63  						Action:      model.ActionBeforeDownload,
    64  						Description: "run wk-1",
    65  						Enabled:     true,
    66  						SourceCode:  "export default async () => ({ 'S': 'OK'})",
    67  					},
    68  				},
    69  			},
    70  			assertOutput: func(t *testing.T, content []byte) {
    71  				assert.Equalf(t, "wk-0,AFTER_CREATE,run wk-0,true", strings.TrimSpace(string(content)), "invalid csv received")
    72  			},
    73  		},
    74  		{
    75  			name:        "list for JSON",
    76  			commandArgs: []string{"--" + model.FlagJsonOutput},
    77  			serverBehavior: listServerStubBehavior{
    78  				existingWorkers: []*model.WorkerDetails{
    79  					{
    80  						Key:         "wk-1",
    81  						Action:      model.ActionGenericEvent,
    82  						Description: "run wk-1",
    83  						Enabled:     false,
    84  						SourceCode:  "export default async () => ({ 'S': 'OK'})",
    85  					},
    86  				},
    87  			},
    88  			assertOutput: func(t *testing.T, content []byte) {
    89  				workers := getAllResponse{}
    90  				require.NoError(t, json.Unmarshal(content, &workers))
    91  				assert.Len(t, workers.Workers, 1)
    92  				assert.Equalf(t, "wk-1", workers.Workers[0].Key, "Key mismatch")
    93  				assert.Equalf(t, model.ActionGenericEvent, workers.Workers[0].Action, "Action mismatch")
    94  				assert.Equalf(t, "run wk-1", workers.Workers[0].Description, "Descritption mismatch")
    95  				assert.Equalf(t, false, workers.Workers[0].Enabled, "Enabled mismatch")
    96  				assert.Equalf(t, "export default async () => ({ 'S': 'OK'})", workers.Workers[0].SourceCode, "Source Code mismatch")
    97  			},
    98  		},
    99  		{
   100  			name:        "fails if timeout exceeds",
   101  			commandArgs: []string{"--" + model.FlagTimeout, "500"},
   102  			serverBehavior: listServerStubBehavior{
   103  				waitFor: 5 * time.Second,
   104  			},
   105  			wantErr: errors.New("request timed out after 500ms"),
   106  		},
   107  		{
   108  			name:        "fails if invalid timeout",
   109  			commandArgs: []string{"--" + model.FlagTimeout, "abc"},
   110  			wantErr:     errors.New("invalid timeout provided"),
   111  		},
   112  	}
   113  
   114  	for _, tt := range tests {
   115  		t.Run(tt.name, func(t *testing.T) {
   116  			ctx, cancelCtx := context.WithCancel(context.Background())
   117  			t.Cleanup(cancelCtx)
   118  
   119  			runCmd := createCliRunner(t, GetListCommand())
   120  
   121  			err := os.Setenv(model.EnvKeyServerUrl, newListServerStub(t, ctx, &tt.serverBehavior))
   122  			require.NoError(t, err)
   123  			t.Cleanup(func() {
   124  				_ = os.Unsetenv(model.EnvKeyServerUrl)
   125  			})
   126  
   127  			if tt.token == "" && tt.serverBehavior.wantBearerToken == "" {
   128  				tt.token = t.Name()
   129  				tt.serverBehavior.wantBearerToken = t.Name()
   130  			}
   131  
   132  			err = os.Setenv(model.EnvKeyAccessToken, tt.token)
   133  			require.NoError(t, err)
   134  			t.Cleanup(func() {
   135  				_ = os.Unsetenv(model.EnvKeyAccessToken)
   136  			})
   137  
   138  			var output bytes.Buffer
   139  			cliOut = &output
   140  			t.Cleanup(func() {
   141  				cliOut = os.Stdout
   142  			})
   143  
   144  			cmd := append([]string{"worker", "list"}, tt.commandArgs...)
   145  
   146  			err = runCmd(cmd...)
   147  
   148  			cancelCtx()
   149  
   150  			if tt.wantErr == nil {
   151  				assert.NoError(t, err)
   152  			} else {
   153  				assert.EqualError(t, tt.wantErr, err.Error())
   154  			}
   155  
   156  			if tt.assertOutput != nil {
   157  				tt.assertOutput(t, output.Bytes())
   158  			}
   159  		})
   160  	}
   161  }
   162  
   163  var listUrlPattern = regexp.MustCompile(`^/worker/api/v1/workers(/[\S/]+)?$`)
   164  
   165  type listServerStubBehavior struct {
   166  	waitFor         time.Duration
   167  	responseStatus  int
   168  	wantBearerToken string
   169  	wantAction      string
   170  	existingWorkers []*model.WorkerDetails
   171  }
   172  
   173  type listServerStub struct {
   174  	t        *testing.T
   175  	ctx      context.Context
   176  	behavior *listServerStubBehavior
   177  }
   178  
   179  func newListServerStub(t *testing.T, ctx context.Context, behavior *listServerStubBehavior) string {
   180  	stub := listServerStub{t: t, behavior: behavior, ctx: ctx}
   181  	server := httptest.NewUnstartedServer(&stub)
   182  	t.Cleanup(server.Close)
   183  	server.Start()
   184  	return "http:" + "//" + server.Listener.Addr().String()
   185  }
   186  
   187  func (s *listServerStub) ServeHTTP(res http.ResponseWriter, req *http.Request) {
   188  	urlMatch := listUrlPattern.FindAllStringSubmatch(req.URL.Path, -1)
   189  	if len(urlMatch) == 0 {
   190  		res.WriteHeader(http.StatusNotFound)
   191  		return
   192  	}
   193  
   194  	if s.behavior.waitFor > 0 {
   195  		select {
   196  		case <-s.ctx.Done():
   197  			return
   198  		case <-time.After(s.behavior.waitFor):
   199  		}
   200  	}
   201  
   202  	// Validate method
   203  	if http.MethodGet != req.Method {
   204  		res.WriteHeader(http.StatusMethodNotAllowed)
   205  		return
   206  	}
   207  
   208  	// Validate token
   209  	if req.Header.Get("authorization") != "Bearer "+s.behavior.wantBearerToken {
   210  		res.WriteHeader(http.StatusForbidden)
   211  		return
   212  	}
   213  
   214  	if s.behavior.responseStatus > 0 {
   215  		res.WriteHeader(s.behavior.responseStatus)
   216  		return
   217  	}
   218  
   219  	var workers []*model.WorkerDetails
   220  
   221  	if s.behavior.wantAction == "" {
   222  		workers = s.behavior.existingWorkers
   223  	} else {
   224  		for _, wk := range s.behavior.existingWorkers {
   225  			if wk.Action == s.behavior.wantAction {
   226  				workers = append(workers, wk)
   227  			}
   228  		}
   229  	}
   230  
   231  	res.WriteHeader(http.StatusOK)
   232  	_, err := res.Write([]byte(mustJsonMarshal(s.t, getAllResponse{Workers: workers})))
   233  	require.NoError(s.t, err)
   234  }