github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/healthcheck/healthcheck_test.go (about)

     1  package healthcheck_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/fastly/go-fastly/v9/fastly"
    11  
    12  	"github.com/fastly/cli/pkg/app"
    13  	"github.com/fastly/cli/pkg/global"
    14  	"github.com/fastly/cli/pkg/mock"
    15  	"github.com/fastly/cli/pkg/testutil"
    16  )
    17  
    18  func TestHealthCheckCreate(t *testing.T) {
    19  	args := testutil.Args
    20  	scenarios := []struct {
    21  		args       []string
    22  		api        mock.API
    23  		wantError  string
    24  		wantOutput string
    25  	}{
    26  		{
    27  			args:      args("healthcheck create --version 1"),
    28  			wantError: "error reading service: no service ID found",
    29  		},
    30  		{
    31  			args: args("healthcheck create --service-id 123 --version 1 --name www.test.com --autoclone"),
    32  			api: mock.API{
    33  				ListVersionsFn:      testutil.ListVersions,
    34  				CloneVersionFn:      testutil.CloneVersionResult(4),
    35  				CreateHealthCheckFn: createHealthCheckError,
    36  			},
    37  			wantError: errTest.Error(),
    38  		},
    39  		// NOTE: Added --timeout flag to validate that a nil pointer dereference is
    40  		// not triggered at runtime when parsing the arguments.
    41  		{
    42  			args: args("healthcheck create --service-id 123 --version 1 --name www.test.com --autoclone --timeout 10"),
    43  			api: mock.API{
    44  				ListVersionsFn:      testutil.ListVersions,
    45  				CloneVersionFn:      testutil.CloneVersionResult(4),
    46  				CreateHealthCheckFn: createHealthCheckOK,
    47  			},
    48  			wantOutput: "Created healthcheck www.test.com (service 123 version 4)",
    49  		},
    50  	}
    51  	for testcaseIdx := range scenarios {
    52  		testcase := &scenarios[testcaseIdx]
    53  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
    54  			var stdout bytes.Buffer
    55  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
    56  				opts := testutil.MockGlobalData(testcase.args, &stdout)
    57  				opts.APIClientFactory = mock.APIClient(testcase.api)
    58  				return opts, nil
    59  			}
    60  			err := app.Run(testcase.args, nil)
    61  			testutil.AssertErrorContains(t, err, testcase.wantError)
    62  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
    63  		})
    64  	}
    65  }
    66  
    67  func TestHealthCheckList(t *testing.T) {
    68  	args := testutil.Args
    69  	scenarios := []struct {
    70  		args       []string
    71  		api        mock.API
    72  		wantError  string
    73  		wantOutput string
    74  	}{
    75  		{
    76  			args: args("healthcheck list --service-id 123 --version 1"),
    77  			api: mock.API{
    78  				ListVersionsFn:     testutil.ListVersions,
    79  				ListHealthChecksFn: listHealthChecksOK,
    80  			},
    81  			wantOutput: listHealthChecksShortOutput,
    82  		},
    83  		{
    84  			args: args("healthcheck list --service-id 123 --version 1 --verbose"),
    85  			api: mock.API{
    86  				ListVersionsFn:     testutil.ListVersions,
    87  				ListHealthChecksFn: listHealthChecksOK,
    88  			},
    89  			wantOutput: listHealthChecksVerboseOutput,
    90  		},
    91  		{
    92  			args: args("healthcheck list --service-id 123 --version 1 -v"),
    93  			api: mock.API{
    94  				ListVersionsFn:     testutil.ListVersions,
    95  				ListHealthChecksFn: listHealthChecksOK,
    96  			},
    97  			wantOutput: listHealthChecksVerboseOutput,
    98  		},
    99  		{
   100  			args: args("healthcheck --verbose list --service-id 123 --version 1"),
   101  			api: mock.API{
   102  				ListVersionsFn:     testutil.ListVersions,
   103  				ListHealthChecksFn: listHealthChecksOK,
   104  			},
   105  			wantOutput: listHealthChecksVerboseOutput,
   106  		},
   107  		{
   108  			args: args("-v healthcheck list --service-id 123 --version 1"),
   109  			api: mock.API{
   110  				ListVersionsFn:     testutil.ListVersions,
   111  				ListHealthChecksFn: listHealthChecksOK,
   112  			},
   113  			wantOutput: listHealthChecksVerboseOutput,
   114  		},
   115  		{
   116  			args: args("healthcheck list --service-id 123 --version 1"),
   117  			api: mock.API{
   118  				ListVersionsFn:     testutil.ListVersions,
   119  				ListHealthChecksFn: listHealthChecksError,
   120  			},
   121  			wantError: errTest.Error(),
   122  		},
   123  	}
   124  	for testcaseIdx := range scenarios {
   125  		testcase := &scenarios[testcaseIdx]
   126  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   127  			var stdout bytes.Buffer
   128  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   129  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   130  				opts.APIClientFactory = mock.APIClient(testcase.api)
   131  				return opts, nil
   132  			}
   133  			err := app.Run(testcase.args, nil)
   134  			testutil.AssertErrorContains(t, err, testcase.wantError)
   135  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   136  		})
   137  	}
   138  }
   139  
   140  func TestHealthCheckDescribe(t *testing.T) {
   141  	args := testutil.Args
   142  	scenarios := []struct {
   143  		args       []string
   144  		api        mock.API
   145  		wantError  string
   146  		wantOutput string
   147  	}{
   148  		{
   149  			args:      args("healthcheck describe --service-id 123 --version 1"),
   150  			wantError: "error parsing arguments: required flag --name not provided",
   151  		},
   152  		{
   153  			args: args("healthcheck describe --service-id 123 --version 1 --name www.test.com"),
   154  			api: mock.API{
   155  				ListVersionsFn:   testutil.ListVersions,
   156  				GetHealthCheckFn: getHealthCheckError,
   157  			},
   158  			wantError: errTest.Error(),
   159  		},
   160  		{
   161  			args: args("healthcheck describe --service-id 123 --version 1 --name www.test.com"),
   162  			api: mock.API{
   163  				ListVersionsFn:   testutil.ListVersions,
   164  				GetHealthCheckFn: getHealthCheckOK,
   165  			},
   166  			wantOutput: describeHealthCheckOutput,
   167  		},
   168  	}
   169  	for testcaseIdx := range scenarios {
   170  		testcase := &scenarios[testcaseIdx]
   171  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   172  			var stdout bytes.Buffer
   173  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   174  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   175  				opts.APIClientFactory = mock.APIClient(testcase.api)
   176  				return opts, nil
   177  			}
   178  			err := app.Run(testcase.args, nil)
   179  			testutil.AssertErrorContains(t, err, testcase.wantError)
   180  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   181  		})
   182  	}
   183  }
   184  
   185  func TestHealthCheckUpdate(t *testing.T) {
   186  	args := testutil.Args
   187  	scenarios := []struct {
   188  		args       []string
   189  		api        mock.API
   190  		wantError  string
   191  		wantOutput string
   192  	}{
   193  		{
   194  			args:      args("healthcheck update --service-id 123 --version 1 --new-name www.test.com --comment "),
   195  			wantError: "error parsing arguments: required flag --name not provided",
   196  		},
   197  		{
   198  			args: args("healthcheck update --service-id 123 --version 1 --name www.test.com --new-name www.example.com --autoclone"),
   199  			api: mock.API{
   200  				ListVersionsFn:      testutil.ListVersions,
   201  				CloneVersionFn:      testutil.CloneVersionResult(4),
   202  				UpdateHealthCheckFn: updateHealthCheckOK,
   203  			},
   204  		},
   205  		{
   206  			args: args("healthcheck update --service-id 123 --version 1 --name www.test.com --new-name www.example.com --autoclone"),
   207  			api: mock.API{
   208  				ListVersionsFn:      testutil.ListVersions,
   209  				CloneVersionFn:      testutil.CloneVersionResult(4),
   210  				UpdateHealthCheckFn: updateHealthCheckError,
   211  			},
   212  			wantError: errTest.Error(),
   213  		},
   214  		{
   215  			args: args("healthcheck update --service-id 123 --version 1 --name www.test.com --new-name www.example.com --autoclone"),
   216  			api: mock.API{
   217  				ListVersionsFn:      testutil.ListVersions,
   218  				CloneVersionFn:      testutil.CloneVersionResult(4),
   219  				UpdateHealthCheckFn: updateHealthCheckOK,
   220  			},
   221  			wantOutput: "Updated healthcheck www.example.com (service 123 version 4)",
   222  		},
   223  	}
   224  	for testcaseIdx := range scenarios {
   225  		testcase := &scenarios[testcaseIdx]
   226  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   227  			var stdout bytes.Buffer
   228  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   229  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   230  				opts.APIClientFactory = mock.APIClient(testcase.api)
   231  				return opts, nil
   232  			}
   233  			err := app.Run(testcase.args, nil)
   234  			testutil.AssertErrorContains(t, err, testcase.wantError)
   235  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   236  		})
   237  	}
   238  }
   239  
   240  func TestHealthCheckDelete(t *testing.T) {
   241  	args := testutil.Args
   242  	scenarios := []struct {
   243  		args       []string
   244  		api        mock.API
   245  		wantError  string
   246  		wantOutput string
   247  	}{
   248  		{
   249  			args:      args("healthcheck delete --service-id 123 --version 1"),
   250  			wantError: "error parsing arguments: required flag --name not provided",
   251  		},
   252  		{
   253  			args: args("healthcheck delete --service-id 123 --version 1 --name www.test.com --autoclone"),
   254  			api: mock.API{
   255  				ListVersionsFn:      testutil.ListVersions,
   256  				CloneVersionFn:      testutil.CloneVersionResult(4),
   257  				DeleteHealthCheckFn: deleteHealthCheckError,
   258  			},
   259  			wantError: errTest.Error(),
   260  		},
   261  		{
   262  			args: args("healthcheck delete --service-id 123 --version 1 --name www.test.com --autoclone"),
   263  			api: mock.API{
   264  				ListVersionsFn:      testutil.ListVersions,
   265  				CloneVersionFn:      testutil.CloneVersionResult(4),
   266  				DeleteHealthCheckFn: deleteHealthCheckOK,
   267  			},
   268  			wantOutput: "Deleted healthcheck www.test.com (service 123 version 4)",
   269  		},
   270  	}
   271  	for testcaseIdx := range scenarios {
   272  		testcase := &scenarios[testcaseIdx]
   273  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   274  			var stdout bytes.Buffer
   275  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   276  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   277  				opts.APIClientFactory = mock.APIClient(testcase.api)
   278  				return opts, nil
   279  			}
   280  			err := app.Run(testcase.args, nil)
   281  			testutil.AssertErrorContains(t, err, testcase.wantError)
   282  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   283  		})
   284  	}
   285  }
   286  
   287  var errTest = errors.New("fixture error")
   288  
   289  func createHealthCheckOK(i *fastly.CreateHealthCheckInput) (*fastly.HealthCheck, error) {
   290  	return &fastly.HealthCheck{
   291  		ServiceID:      fastly.ToPointer(i.ServiceID),
   292  		ServiceVersion: fastly.ToPointer(i.ServiceVersion),
   293  		Name:           i.Name,
   294  		Host:           fastly.ToPointer("www.test.com"),
   295  		Path:           fastly.ToPointer("/health"),
   296  	}, nil
   297  }
   298  
   299  func createHealthCheckError(_ *fastly.CreateHealthCheckInput) (*fastly.HealthCheck, error) {
   300  	return nil, errTest
   301  }
   302  
   303  func listHealthChecksOK(i *fastly.ListHealthChecksInput) ([]*fastly.HealthCheck, error) {
   304  	return []*fastly.HealthCheck{
   305  		{
   306  			ServiceID:      fastly.ToPointer(i.ServiceID),
   307  			ServiceVersion: fastly.ToPointer(i.ServiceVersion),
   308  			Name:           fastly.ToPointer("test"),
   309  			Comment:        fastly.ToPointer("test"),
   310  			Method:         fastly.ToPointer("HEAD"),
   311  			Host:           fastly.ToPointer("www.test.com"),
   312  			Path:           fastly.ToPointer("/health"),
   313  		},
   314  		{
   315  			ServiceID:      fastly.ToPointer(i.ServiceID),
   316  			ServiceVersion: fastly.ToPointer(i.ServiceVersion),
   317  			Name:           fastly.ToPointer("example"),
   318  			Comment:        fastly.ToPointer("example"),
   319  			Method:         fastly.ToPointer("HEAD"),
   320  			Host:           fastly.ToPointer("www.example.com"),
   321  			Path:           fastly.ToPointer("/health"),
   322  		},
   323  	}, nil
   324  }
   325  
   326  func listHealthChecksError(_ *fastly.ListHealthChecksInput) ([]*fastly.HealthCheck, error) {
   327  	return nil, errTest
   328  }
   329  
   330  var listHealthChecksShortOutput = strings.TrimSpace(`
   331  SERVICE  VERSION  NAME     METHOD  HOST             PATH
   332  123      1        test     HEAD    www.test.com     /health
   333  123      1        example  HEAD    www.example.com  /health
   334  `) + "\n"
   335  
   336  var listHealthChecksVerboseOutput = strings.Join([]string{
   337  	"Fastly API endpoint: https://api.fastly.com",
   338  	"Fastly API token provided via config file (profile: user)",
   339  	"",
   340  	"Service ID (via --service-id): 123",
   341  	"",
   342  	"Version: 1",
   343  	"	Healthcheck 1/2",
   344  	"		Name: test",
   345  	"		Comment: test",
   346  	"		Method: HEAD",
   347  	"		Host: www.test.com",
   348  	"		Path: /health",
   349  	"		HTTP version: ",
   350  	"		Timeout: 0",
   351  	"		Check interval: 0",
   352  	"		Expected response: 0",
   353  	"		Window: 0",
   354  	"		Threshold: 0",
   355  	"		Initial: 0",
   356  	"	Healthcheck 2/2",
   357  	"		Name: example",
   358  	"		Comment: example",
   359  	"		Method: HEAD",
   360  	"		Host: www.example.com",
   361  	"		Path: /health",
   362  	"		HTTP version: ",
   363  	"		Timeout: 0",
   364  	"		Check interval: 0",
   365  	"		Expected response: 0",
   366  	"		Window: 0",
   367  	"		Threshold: 0",
   368  	"		Initial: 0",
   369  }, "\n") + "\n\n"
   370  
   371  func getHealthCheckOK(i *fastly.GetHealthCheckInput) (*fastly.HealthCheck, error) {
   372  	return &fastly.HealthCheck{
   373  		ServiceID:      fastly.ToPointer(i.ServiceID),
   374  		ServiceVersion: fastly.ToPointer(i.ServiceVersion),
   375  		Name:           fastly.ToPointer("test"),
   376  		Method:         fastly.ToPointer("HEAD"),
   377  		Host:           fastly.ToPointer("www.test.com"),
   378  		Path:           fastly.ToPointer("/healthcheck"),
   379  		Comment:        fastly.ToPointer("test"),
   380  	}, nil
   381  }
   382  
   383  func getHealthCheckError(_ *fastly.GetHealthCheckInput) (*fastly.HealthCheck, error) {
   384  	return nil, errTest
   385  }
   386  
   387  var describeHealthCheckOutput = "\n" + strings.Join([]string{
   388  	"Service ID: 123",
   389  	"Version: 1",
   390  	"Name: test",
   391  	"Comment: test",
   392  	"Method: HEAD",
   393  	"Host: www.test.com",
   394  	"Path: /healthcheck",
   395  	"HTTP version: ",
   396  	"Timeout: 0",
   397  	"Check interval: 0",
   398  	"Expected response: 0",
   399  	"Window: 0",
   400  	"Threshold: 0",
   401  	"Initial: 0",
   402  }, "\n") + "\n"
   403  
   404  func updateHealthCheckOK(i *fastly.UpdateHealthCheckInput) (*fastly.HealthCheck, error) {
   405  	return &fastly.HealthCheck{
   406  		ServiceID:      fastly.ToPointer(i.ServiceID),
   407  		ServiceVersion: fastly.ToPointer(i.ServiceVersion),
   408  		Name:           i.NewName,
   409  	}, nil
   410  }
   411  
   412  func updateHealthCheckError(_ *fastly.UpdateHealthCheckInput) (*fastly.HealthCheck, error) {
   413  	return nil, errTest
   414  }
   415  
   416  func deleteHealthCheckOK(_ *fastly.DeleteHealthCheckInput) error {
   417  	return nil
   418  }
   419  
   420  func deleteHealthCheckError(_ *fastly.DeleteHealthCheckInput) error {
   421  	return errTest
   422  }