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

     1  package service_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"regexp"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/fastly/go-fastly/v9/fastly"
    15  
    16  	"github.com/fastly/cli/pkg/app"
    17  	"github.com/fastly/cli/pkg/global"
    18  	"github.com/fastly/cli/pkg/manifest"
    19  	"github.com/fastly/cli/pkg/mock"
    20  	"github.com/fastly/cli/pkg/testutil"
    21  )
    22  
    23  func TestServiceCreate(t *testing.T) {
    24  	args := testutil.Args
    25  	scenarios := []struct {
    26  		args       []string
    27  		api        mock.API
    28  		wantError  string
    29  		wantOutput string
    30  	}{
    31  		{
    32  			args:       args("service create --name Foo"),
    33  			api:        mock.API{CreateServiceFn: createServiceOK},
    34  			wantOutput: "Created service 12345",
    35  		},
    36  		{
    37  			args:       args("service create -n=Foo"),
    38  			api:        mock.API{CreateServiceFn: createServiceOK},
    39  			wantOutput: "Created service 12345",
    40  		},
    41  		{
    42  			args:       args("service create --name Foo --type wasm"),
    43  			api:        mock.API{CreateServiceFn: createServiceOK},
    44  			wantOutput: "Created service 12345",
    45  		},
    46  		{
    47  			args:       args("service create --name Foo --type wasm --comment Hello"),
    48  			api:        mock.API{CreateServiceFn: createServiceOK},
    49  			wantOutput: "Created service 12345",
    50  		},
    51  		{
    52  			args:       args("service create -n Foo --comment Hello"),
    53  			api:        mock.API{CreateServiceFn: createServiceOK},
    54  			wantOutput: "Created service 12345",
    55  		},
    56  		{
    57  			args:      args("service create -n Foo"),
    58  			api:       mock.API{CreateServiceFn: createServiceError},
    59  			wantError: errTest.Error(),
    60  		},
    61  	}
    62  	for testcaseIdx := range scenarios {
    63  		testcase := &scenarios[testcaseIdx]
    64  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
    65  			var stdout bytes.Buffer
    66  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
    67  				opts := testutil.MockGlobalData(testcase.args, &stdout)
    68  				opts.APIClientFactory = mock.APIClient(testcase.api)
    69  				return opts, nil
    70  			}
    71  			err := app.Run(testcase.args, nil)
    72  			testutil.AssertErrorContains(t, err, testcase.wantError)
    73  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
    74  		})
    75  	}
    76  }
    77  
    78  func TestServiceList(t *testing.T) {
    79  	args := testutil.Args
    80  	scenarios := []struct {
    81  		args       []string
    82  		api        mock.API
    83  		wantError  string
    84  		wantOutput string
    85  	}{
    86  		{
    87  			api: mock.API{
    88  				GetServicesFn: func(i *fastly.GetServicesInput) *fastly.ListPaginator[fastly.Service] {
    89  					return fastly.NewPaginator[fastly.Service](mock.HTTPClient{
    90  						Errors: []error{
    91  							testutil.Err,
    92  						},
    93  						Responses: []*http.Response{nil},
    94  					}, fastly.ListOpts{}, "/example")
    95  				},
    96  			},
    97  			args:      args("service list"),
    98  			wantError: testutil.Err.Error(),
    99  		},
   100  		{
   101  			api: mock.API{
   102  				GetServicesFn: func(i *fastly.GetServicesInput) *fastly.ListPaginator[fastly.Service] {
   103  					return fastly.NewPaginator[fastly.Service](mock.HTTPClient{
   104  						Errors: []error{nil},
   105  						Responses: []*http.Response{
   106  							{
   107  								Body: io.NopCloser(strings.NewReader(`[
   108                    {
   109                      "name": "Foo",
   110                      "id": "123",
   111                      "type": "wasm",
   112                      "version": 2,
   113                      "updated_at": "2021-06-15T23:00:00Z"
   114                    },
   115                    {
   116                      "name": "Bar",
   117                      "id": "456",
   118                      "type": "wasm",
   119                      "version": 1,
   120                      "updated_at": "2021-06-15T23:00:00Z"
   121                    },
   122                    {
   123                      "name": "Baz",
   124                      "id": "789",
   125                      "type": "vcl",
   126                      "version": 1
   127                    }
   128                  ]`)),
   129  							},
   130  						},
   131  					}, fastly.ListOpts{}, "/example")
   132  				},
   133  			},
   134  			args:       args("service list --per-page 1"),
   135  			wantOutput: listServicesShortOutput,
   136  		},
   137  		{
   138  			api: mock.API{
   139  				GetServicesFn: func(i *fastly.GetServicesInput) *fastly.ListPaginator[fastly.Service] {
   140  					return fastly.NewPaginator[fastly.Service](mock.HTTPClient{
   141  						Errors: []error{nil},
   142  						Responses: []*http.Response{
   143  							{
   144  								Body: io.NopCloser(strings.NewReader(`[
   145                    {
   146                      "name": "Foo",
   147                      "id": "123",
   148                      "type": "wasm",
   149                      "version": 2,
   150                      "updated_at": "2021-06-15T23:00:00Z",
   151                      "customer_id": "mycustomerid",
   152                      "versions": [
   153                        {
   154                          "number": 1,
   155                          "comment": "a",
   156                          "service_id": "b",
   157                          "active": false,
   158                          "locked": false,
   159                          "deployed": false,
   160                          "staging": false,
   161                          "testing": false,
   162                          "created_at": "2021-06-15T23:00:00Z",
   163                          "deleted_at": "2021-06-15T23:00:00Z",
   164                          "updated_at": "2021-06-15T23:00:00Z"
   165                        },
   166                        {
   167                          "number": 2,
   168                          "comment": "c",
   169                          "service_id": "d",
   170                          "active": true,
   171                          "locked": false,
   172                          "deployed": true,
   173                          "staging": false,
   174                          "testing": false,
   175                          "created_at": "2021-06-15T23:00:00Z",
   176                          "updated_at": "2021-06-15T23:00:00Z"
   177                        }
   178                      ]
   179                    },
   180                    {
   181                      "name": "Bar",
   182                      "id": "456",
   183                      "type": "wasm",
   184                      "version": 1,
   185                      "updated_at": "2021-06-15T23:00:00Z",
   186                      "customer_id": "mycustomerid"
   187                    },
   188                    {
   189                      "name": "Baz",
   190                      "id": "789",
   191                      "type": "vcl",
   192                      "version": 1,
   193                      "customer_id": "mycustomerid"
   194                    }
   195                  ]`)),
   196  							},
   197  						},
   198  					}, fastly.ListOpts{}, "/example")
   199  				},
   200  			},
   201  			args:       args("service list --verbose"),
   202  			wantOutput: listServicesVerboseOutput,
   203  		},
   204  	}
   205  	for testcaseIdx := range scenarios {
   206  		testcase := &scenarios[testcaseIdx]
   207  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   208  			var stdout bytes.Buffer
   209  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   210  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   211  				opts.APIClientFactory = mock.APIClient(testcase.api)
   212  				return opts, nil
   213  			}
   214  			err := app.Run(testcase.args, nil)
   215  			testutil.AssertErrorContains(t, err, testcase.wantError)
   216  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   217  		})
   218  	}
   219  }
   220  
   221  func TestServiceDescribe(t *testing.T) {
   222  	args := testutil.Args
   223  	scenarios := []struct {
   224  		args       []string
   225  		api        mock.API
   226  		wantError  string
   227  		wantOutput string
   228  	}{
   229  		{
   230  			args:      args("service describe"),
   231  			api:       mock.API{GetServiceDetailsFn: describeServiceOK},
   232  			wantError: "error reading service: no service ID found",
   233  		},
   234  		{
   235  			args:       args("service describe --service-id 123"),
   236  			api:        mock.API{GetServiceDetailsFn: describeServiceOK},
   237  			wantOutput: describeServiceShortOutput,
   238  		},
   239  		{
   240  			args:       args("service describe --service-id 123 --verbose"),
   241  			api:        mock.API{GetServiceDetailsFn: describeServiceOK},
   242  			wantOutput: describeServiceVerboseOutput,
   243  		},
   244  		{
   245  			args:       args("service describe --service-id 123 -v"),
   246  			api:        mock.API{GetServiceDetailsFn: describeServiceOK},
   247  			wantOutput: describeServiceVerboseOutput,
   248  		},
   249  		{
   250  			args:       args("service --verbose describe --service-id 123"),
   251  			api:        mock.API{GetServiceDetailsFn: describeServiceOK},
   252  			wantOutput: describeServiceVerboseOutput,
   253  		},
   254  		{
   255  			args:       args("-v service describe --service-id 123"),
   256  			api:        mock.API{GetServiceDetailsFn: describeServiceOK},
   257  			wantOutput: describeServiceVerboseOutput,
   258  		},
   259  		{
   260  			args:      args("service describe --service-id 123"),
   261  			api:       mock.API{GetServiceDetailsFn: describeServiceError},
   262  			wantError: errTest.Error(),
   263  		},
   264  	}
   265  	for testcaseIdx := range scenarios {
   266  		testcase := &scenarios[testcaseIdx]
   267  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   268  			var stdout bytes.Buffer
   269  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   270  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   271  				opts.APIClientFactory = mock.APIClient(testcase.api)
   272  				return opts, nil
   273  			}
   274  			err := app.Run(testcase.args, nil)
   275  			testutil.AssertErrorContains(t, err, testcase.wantError)
   276  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   277  		})
   278  	}
   279  }
   280  
   281  func TestServiceSearch(t *testing.T) {
   282  	args := testutil.Args
   283  	scenarios := []struct {
   284  		args       []string
   285  		api        mock.API
   286  		wantError  string
   287  		wantOutput string
   288  	}{
   289  		{
   290  			args:      args("service search"),
   291  			wantError: "error parsing arguments: required flag --name not provided",
   292  		},
   293  		{
   294  			args:       args("service search --name Foo"),
   295  			api:        mock.API{SearchServiceFn: searchServiceOK},
   296  			wantOutput: searchServiceShortOutput,
   297  		},
   298  		{
   299  			args:       args("service search --name Foo -v"),
   300  			api:        mock.API{SearchServiceFn: searchServiceOK},
   301  			wantOutput: searchServiceVerboseOutput,
   302  		},
   303  		{
   304  			args:      args("service search --name"),
   305  			api:       mock.API{SearchServiceFn: searchServiceOK},
   306  			wantError: "error parsing arguments: expected argument for flag '--name'",
   307  		},
   308  	}
   309  	for testcaseIdx := range scenarios {
   310  		testcase := &scenarios[testcaseIdx]
   311  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   312  			var stdout bytes.Buffer
   313  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   314  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   315  				opts.APIClientFactory = mock.APIClient(testcase.api)
   316  				return opts, nil
   317  			}
   318  			err := app.Run(testcase.args, nil)
   319  			testutil.AssertErrorContains(t, err, testcase.wantError)
   320  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   321  		})
   322  	}
   323  }
   324  
   325  func TestServiceUpdate(t *testing.T) {
   326  	args := testutil.Args
   327  	scenarios := []struct {
   328  		args       []string
   329  		api        mock.API
   330  		wantError  string
   331  		wantOutput string
   332  	}{
   333  		{
   334  			args: args("service update"),
   335  			api: mock.API{
   336  				GetServiceFn:    getServiceOK,
   337  				UpdateServiceFn: updateServiceOK,
   338  			},
   339  			wantError: "error reading service: no service ID found",
   340  		},
   341  		{
   342  			args:      args("service update --service-id 12345"),
   343  			api:       mock.API{UpdateServiceFn: updateServiceOK},
   344  			wantError: "error parsing arguments: must provide either --name or --comment to update service",
   345  		},
   346  		{
   347  			args:       args("service update --service-id 12345 --name Foo"),
   348  			api:        mock.API{UpdateServiceFn: updateServiceOK},
   349  			wantOutput: "Updated service 12345",
   350  		},
   351  		{
   352  			args:       args("service update --service-id 12345 -n=Foo"),
   353  			api:        mock.API{UpdateServiceFn: updateServiceOK},
   354  			wantOutput: "Updated service 12345",
   355  		},
   356  		{
   357  			args:       args("service update --service-id 12345 --name Foo"),
   358  			api:        mock.API{UpdateServiceFn: updateServiceOK},
   359  			wantOutput: "Updated service 12345",
   360  		},
   361  		{
   362  			args:       args("service update --service-id 12345 --name Foo --comment Hello"),
   363  			api:        mock.API{UpdateServiceFn: updateServiceOK},
   364  			wantOutput: "Updated service 12345",
   365  		},
   366  		{
   367  			args:       args("service update --service-id 12345 -n Foo --comment Hello"),
   368  			api:        mock.API{UpdateServiceFn: updateServiceOK},
   369  			wantOutput: "Updated service 12345",
   370  		},
   371  		{
   372  			args:      args("service update --service-id 12345 -n Foo"),
   373  			api:       mock.API{UpdateServiceFn: updateServiceError},
   374  			wantError: errTest.Error(),
   375  		},
   376  	}
   377  	for testcaseIdx := range scenarios {
   378  		testcase := &scenarios[testcaseIdx]
   379  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   380  			var stdout bytes.Buffer
   381  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   382  				opts := testutil.MockGlobalData(testcase.args, &stdout)
   383  				opts.APIClientFactory = mock.APIClient(testcase.api)
   384  				return opts, nil
   385  			}
   386  			err := app.Run(testcase.args, nil)
   387  			testutil.AssertErrorContains(t, err, testcase.wantError)
   388  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   389  		})
   390  	}
   391  }
   392  
   393  func TestServiceDelete(t *testing.T) {
   394  	args := testutil.Args
   395  	nonEmptyServiceID := regexp.MustCompile(`service_id = "[^"]+"`)
   396  
   397  	scenarios := []struct {
   398  		args                 []string
   399  		api                  mock.API
   400  		manifest             string
   401  		wantError            string
   402  		wantOutput           string
   403  		expectEmptyServiceID bool
   404  	}{
   405  		{
   406  			args:      args("service delete"),
   407  			api:       mock.API{DeleteServiceFn: deleteServiceOK},
   408  			manifest:  "fastly-no-serviceid.toml",
   409  			wantError: "error reading service: no service ID found",
   410  		},
   411  		{
   412  			args:                 args("service delete"),
   413  			api:                  mock.API{DeleteServiceFn: deleteServiceOK},
   414  			manifest:             "fastly-valid.toml",
   415  			wantOutput:           "Deleted service ID 123",
   416  			expectEmptyServiceID: true,
   417  		},
   418  		{
   419  			args:       args("service delete --service-id 001"),
   420  			api:        mock.API{DeleteServiceFn: deleteServiceOK},
   421  			wantOutput: "Deleted service ID 001",
   422  		},
   423  		{
   424  			args:                 args("service delete --service-id 001"),
   425  			api:                  mock.API{DeleteServiceFn: deleteServiceOK},
   426  			manifest:             "fastly-valid.toml",
   427  			wantOutput:           "Deleted service ID 001",
   428  			expectEmptyServiceID: false,
   429  		},
   430  		{
   431  			args:      args("service delete --service-id 001"),
   432  			api:       mock.API{DeleteServiceFn: deleteServiceError},
   433  			manifest:  "fastly-valid.toml",
   434  			wantError: errTest.Error(),
   435  		},
   436  	}
   437  	for testcaseIdx := range scenarios {
   438  		testcase := &scenarios[testcaseIdx]
   439  		t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
   440  			// We're going to chdir to an temp environment,
   441  			// so save the PWD to return to, afterwards.
   442  			pwd, err := os.Getwd()
   443  			if err != nil {
   444  				t.Fatal(err)
   445  			}
   446  
   447  			// Create test environment
   448  			opts := testutil.EnvOpts{T: t}
   449  			if testcase.manifest != "" {
   450  				b, err := os.ReadFile(filepath.Join("testdata", testcase.manifest))
   451  				if err != nil {
   452  					t.Fatal(err)
   453  				}
   454  				opts.Write = []testutil.FileIO{
   455  					{Src: string(b), Dst: manifest.Filename},
   456  				}
   457  			}
   458  			rootdir := testutil.NewEnv(opts)
   459  			defer os.RemoveAll(rootdir)
   460  
   461  			// Before running the test, chdir into the temp environment.
   462  			// When we're done, chdir back to our original location.
   463  			// This is so we can reliably assert file structure.
   464  			if err := os.Chdir(rootdir); err != nil {
   465  				t.Fatal(err)
   466  			}
   467  			defer func() {
   468  				_ = os.Chdir(pwd)
   469  			}()
   470  
   471  			var stdout bytes.Buffer
   472  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   473  				runOpts := testutil.MockGlobalData(testcase.args, &stdout)
   474  				runOpts.APIClientFactory = mock.APIClient(testcase.api)
   475  				return runOpts, nil
   476  			}
   477  			runErr := app.Run(testcase.args, nil)
   478  			testutil.AssertErrorContains(t, runErr, testcase.wantError)
   479  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   480  
   481  			if testcase.manifest != "" {
   482  				m := filepath.Join(rootdir, manifest.Filename)
   483  				b, err := os.ReadFile(m)
   484  				if err != nil {
   485  					t.Fatal(err)
   486  				}
   487  
   488  				if testcase.expectEmptyServiceID {
   489  					testutil.AssertStringContains(t, string(b), `service_id = ""`)
   490  				} else if !nonEmptyServiceID.Match(b) && runErr == nil {
   491  					// The runErr check is to prevent the first test case from causing an
   492  					// accidental failure. As the fastly.toml doesn't have a service_id
   493  					// set, while marshalling back and forth it'll get converted to an
   494  					// empty string in the manifest file which will accidentally trigger
   495  					// the following test error otherwise if we don't check for the nil
   496  					// error value. Because that first test case expects an error to be
   497  					// raised we know that we can safely check for `runErr == nil` here.
   498  					t.Fatal("expected service_id to contain a value")
   499  				}
   500  			}
   501  		})
   502  	}
   503  }
   504  
   505  var errTest = errors.New("fixture error")
   506  
   507  func createServiceOK(i *fastly.CreateServiceInput) (*fastly.Service, error) {
   508  	return &fastly.Service{
   509  		ServiceID: fastly.ToPointer("12345"),
   510  		Name:      i.Name,
   511  	}, nil
   512  }
   513  
   514  func createServiceError(*fastly.CreateServiceInput) (*fastly.Service, error) {
   515  	return nil, errTest
   516  }
   517  
   518  var listServicesShortOutput = strings.TrimSpace(`
   519  NAME  ID   TYPE  ACTIVE VERSION  LAST EDITED (UTC)
   520  Foo   123  wasm  2               2021-06-15 23:00
   521  Bar   456  wasm  1               2021-06-15 23:00
   522  Baz   789  vcl   1               n/a
   523  `) + "\n"
   524  
   525  var listServicesVerboseOutput = strings.TrimSpace(`
   526  Fastly API endpoint: https://api.fastly.com
   527  Fastly API token provided via config file (profile: user)
   528  
   529  Service 1/3
   530  	ID: 123
   531  	Name: Foo
   532  	Type: wasm
   533  	Customer ID: mycustomerid
   534  	Last edited (UTC): 2021-06-15 23:00
   535  	Active version: 2
   536  	Versions: 2
   537  		Version 1/2
   538  			Number: 1
   539  			Comment: a
   540  			Service ID: b
   541  			Active: false
   542  			Locked: false
   543  			Deployed: false
   544  			Staging: false
   545  			Testing: false
   546  			Created (UTC): 2021-06-15 23:00
   547  			Last edited (UTC): 2021-06-15 23:00
   548  			Deleted (UTC): 2021-06-15 23:00
   549  		Version 2/2
   550  			Number: 2
   551  			Comment: c
   552  			Service ID: d
   553  			Active: true
   554  			Locked: false
   555  			Deployed: true
   556  			Staging: false
   557  			Testing: false
   558  			Created (UTC): 2021-06-15 23:00
   559  			Last edited (UTC): 2021-06-15 23:00
   560  
   561  Service 2/3
   562  	ID: 456
   563  	Name: Bar
   564  	Type: wasm
   565  	Customer ID: mycustomerid
   566  	Last edited (UTC): 2021-06-15 23:00
   567  	Active version: 1
   568  	Versions: 0
   569  
   570  Service 3/3
   571  	ID: 789
   572  	Name: Baz
   573  	Type: vcl
   574  	Customer ID: mycustomerid
   575  	Active version: 1
   576  	Versions: 0
   577  `) + "\n\n"
   578  
   579  func getServiceOK(_ *fastly.GetServiceInput) (*fastly.Service, error) {
   580  	return &fastly.Service{
   581  		ServiceID: fastly.ToPointer("12345"),
   582  		Name:      fastly.ToPointer("Foo"),
   583  		Comment:   fastly.ToPointer("Bar"),
   584  	}, nil
   585  }
   586  
   587  func describeServiceOK(_ *fastly.GetServiceInput) (*fastly.ServiceDetail, error) {
   588  	return &fastly.ServiceDetail{
   589  		ServiceID:  fastly.ToPointer("123"),
   590  		Name:       fastly.ToPointer("Foo"),
   591  		Type:       fastly.ToPointer("wasm"),
   592  		Comment:    fastly.ToPointer("example"),
   593  		CustomerID: fastly.ToPointer("mycustomerid"),
   594  		ActiveVersion: &fastly.Version{
   595  			Number:    fastly.ToPointer(2),
   596  			Comment:   fastly.ToPointer("c"),
   597  			ServiceID: fastly.ToPointer("d"),
   598  			Active:    fastly.ToPointer(true),
   599  			Deployed:  fastly.ToPointer(true),
   600  			CreatedAt: testutil.MustParseTimeRFC3339("2001-03-03T04:05:06Z"),
   601  			UpdatedAt: testutil.MustParseTimeRFC3339("2001-03-04T04:05:06Z"),
   602  		},
   603  		UpdatedAt: testutil.MustParseTimeRFC3339("2010-11-15T19:01:02Z"),
   604  		Versions: []*fastly.Version{
   605  			{
   606  				Number:    fastly.ToPointer(1),
   607  				Comment:   fastly.ToPointer("a"),
   608  				ServiceID: fastly.ToPointer("b"),
   609  				CreatedAt: testutil.MustParseTimeRFC3339("2001-02-03T04:05:06Z"),
   610  				UpdatedAt: testutil.MustParseTimeRFC3339("2001-02-04T04:05:06Z"),
   611  				DeletedAt: testutil.MustParseTimeRFC3339("2001-02-05T04:05:06Z"),
   612  			},
   613  			{
   614  				Number:    fastly.ToPointer(2),
   615  				Comment:   fastly.ToPointer("c"),
   616  				ServiceID: fastly.ToPointer("d"),
   617  				Active:    fastly.ToPointer(true),
   618  				Deployed:  fastly.ToPointer(true),
   619  				CreatedAt: testutil.MustParseTimeRFC3339("2001-03-03T04:05:06Z"),
   620  				UpdatedAt: testutil.MustParseTimeRFC3339("2001-03-04T04:05:06Z"),
   621  			},
   622  		},
   623  	}, nil
   624  }
   625  
   626  func describeServiceError(_ *fastly.GetServiceInput) (*fastly.ServiceDetail, error) {
   627  	return nil, errTest
   628  }
   629  
   630  var describeServiceShortOutput = strings.TrimSpace(`
   631  ID: 123
   632  Name: Foo
   633  Type: wasm
   634  Comment: example
   635  Customer ID: mycustomerid
   636  Last edited (UTC): 2010-11-15 19:01
   637  Active version:
   638  	Number: 2
   639  	Comment: c
   640  	Service ID: d
   641  	Active: true
   642  	Deployed: true
   643  	Created (UTC): 2001-03-03 04:05
   644  	Last edited (UTC): 2001-03-04 04:05
   645  Versions: 2
   646  	Version 1/2
   647  		Number: 1
   648  		Comment: a
   649  		Service ID: b
   650  		Created (UTC): 2001-02-03 04:05
   651  		Last edited (UTC): 2001-02-04 04:05
   652  		Deleted (UTC): 2001-02-05 04:05
   653  	Version 2/2
   654  		Number: 2
   655  		Comment: c
   656  		Service ID: d
   657  		Active: true
   658  		Deployed: true
   659  		Created (UTC): 2001-03-03 04:05
   660  		Last edited (UTC): 2001-03-04 04:05
   661  `) + "\n"
   662  
   663  var describeServiceVerboseOutput = strings.TrimSpace(`
   664  Fastly API endpoint: https://api.fastly.com
   665  Fastly API token provided via config file (profile: user)
   666  
   667  Service ID (via --service-id): 123
   668  
   669  ID: 123
   670  Name: Foo
   671  Type: wasm
   672  Comment: example
   673  Customer ID: mycustomerid
   674  Last edited (UTC): 2010-11-15 19:01
   675  Active version:
   676  	Number: 2
   677  	Comment: c
   678  	Service ID: d
   679  	Active: true
   680  	Deployed: true
   681  	Created (UTC): 2001-03-03 04:05
   682  	Last edited (UTC): 2001-03-04 04:05
   683  Versions: 2
   684  	Version 1/2
   685  		Number: 1
   686  		Comment: a
   687  		Service ID: b
   688  		Created (UTC): 2001-02-03 04:05
   689  		Last edited (UTC): 2001-02-04 04:05
   690  		Deleted (UTC): 2001-02-05 04:05
   691  	Version 2/2
   692  		Number: 2
   693  		Comment: c
   694  		Service ID: d
   695  		Active: true
   696  		Deployed: true
   697  		Created (UTC): 2001-03-03 04:05
   698  		Last edited (UTC): 2001-03-04 04:05
   699  `) + "\n"
   700  
   701  func searchServiceOK(_ *fastly.SearchServiceInput) (*fastly.Service, error) {
   702  	return &fastly.Service{
   703  		ServiceID:  fastly.ToPointer("123"),
   704  		Name:       fastly.ToPointer("Foo"),
   705  		Type:       fastly.ToPointer("wasm"),
   706  		CustomerID: fastly.ToPointer("mycustomerid"),
   707  		UpdatedAt:  testutil.MustParseTimeRFC3339("2010-11-15T19:01:02Z"),
   708  		Versions: []*fastly.Version{
   709  			{
   710  				Number:    fastly.ToPointer(1),
   711  				Comment:   fastly.ToPointer("a"),
   712  				ServiceID: fastly.ToPointer("b"),
   713  				CreatedAt: testutil.MustParseTimeRFC3339("2001-02-03T04:05:06Z"),
   714  				UpdatedAt: testutil.MustParseTimeRFC3339("2001-02-04T04:05:06Z"),
   715  				DeletedAt: testutil.MustParseTimeRFC3339("2001-02-05T04:05:06Z"),
   716  			},
   717  			{
   718  				Number:    fastly.ToPointer(2),
   719  				Comment:   fastly.ToPointer("c"),
   720  				ServiceID: fastly.ToPointer("d"),
   721  				Active:    fastly.ToPointer(true),
   722  				Deployed:  fastly.ToPointer(true),
   723  				CreatedAt: testutil.MustParseTimeRFC3339("2001-03-03T04:05:06Z"),
   724  				UpdatedAt: testutil.MustParseTimeRFC3339("2001-03-04T04:05:06Z"),
   725  			},
   726  		},
   727  	}, nil
   728  }
   729  
   730  var searchServiceShortOutput = strings.TrimSpace(`
   731  ID: 123
   732  Name: Foo
   733  Type: wasm
   734  Customer ID: mycustomerid
   735  Last edited (UTC): 2010-11-15 19:01
   736  Versions: 2
   737  	Version 1/2
   738  		Number: 1
   739  		Comment: a
   740  		Service ID: b
   741  		Created (UTC): 2001-02-03 04:05
   742  		Last edited (UTC): 2001-02-04 04:05
   743  		Deleted (UTC): 2001-02-05 04:05
   744  	Version 2/2
   745  		Number: 2
   746  		Comment: c
   747  		Service ID: d
   748  		Active: true
   749  		Deployed: true
   750  		Created (UTC): 2001-03-03 04:05
   751  		Last edited (UTC): 2001-03-04 04:05
   752  `) + "\n"
   753  
   754  var searchServiceVerboseOutput = strings.TrimSpace(`
   755  Fastly API endpoint: https://api.fastly.com
   756  Fastly API token provided via config file (profile: user)
   757  
   758  ID: 123
   759  Name: Foo
   760  Type: wasm
   761  Customer ID: mycustomerid
   762  Last edited (UTC): 2010-11-15 19:01
   763  Versions: 2
   764  	Version 1/2
   765  		Number: 1
   766  		Comment: a
   767  		Service ID: b
   768  		Created (UTC): 2001-02-03 04:05
   769  		Last edited (UTC): 2001-02-04 04:05
   770  		Deleted (UTC): 2001-02-05 04:05
   771  	Version 2/2
   772  		Number: 2
   773  		Comment: c
   774  		Service ID: d
   775  		Active: true
   776  		Deployed: true
   777  		Created (UTC): 2001-03-03 04:05
   778  		Last edited (UTC): 2001-03-04 04:05
   779  `) + "\n"
   780  
   781  func updateServiceOK(_ *fastly.UpdateServiceInput) (*fastly.Service, error) {
   782  	return &fastly.Service{
   783  		ServiceID: fastly.ToPointer("12345"),
   784  	}, nil
   785  }
   786  
   787  func updateServiceError(*fastly.UpdateServiceInput) (*fastly.Service, error) {
   788  	return nil, errTest
   789  }
   790  
   791  func deleteServiceOK(*fastly.DeleteServiceInput) error {
   792  	return nil
   793  }
   794  
   795  func deleteServiceError(*fastly.DeleteServiceInput) error {
   796  	return errTest
   797  }