github.com/google/go-github/v70@v70.0.0/github/enterprise_actions_hosted_runners_test.go (about)

     1  // Copyright 2025 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package github
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"net/http"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/google/go-cmp/cmp"
    16  )
    17  
    18  func TestEnterpriseService_ListHostedRunners(t *testing.T) {
    19  	t.Parallel()
    20  	client, mux, _ := setup(t)
    21  
    22  	mux.HandleFunc("/enterprises/o/actions/hosted-runners", func(w http.ResponseWriter, r *http.Request) {
    23  		testMethod(t, r, "GET")
    24  		fmt.Fprint(w, `{
    25  			"total_count": 2,
    26  			"runners": [
    27  				{
    28  					"id": 5,
    29  					"name": "My hosted ubuntu runner",
    30  					"runner_group_id": 2,
    31  					"platform": "linux-x64",
    32  					"image_details": {
    33  						"id": "ubuntu-20.04",
    34  						"size_gb": 86
    35  					},
    36  					"machine_size_details": {
    37  						"id": "4-core",
    38  						"cpu_cores": 4,
    39  						"memory_gb": 16,
    40  						"storage_gb": 150
    41  					},
    42  					"status": "Ready",
    43  					"maximum_runners": 10,
    44  					"public_ip_enabled": true,
    45  					"public_ips": [
    46  						{
    47  							"enabled": true,
    48  							"prefix": "20.80.208.150",
    49  							"length": 31
    50  						}
    51  					],
    52  					"last_active_on": "2023-04-26T15:23:37Z"
    53  				},
    54  				{
    55  					"id": 7,
    56  					"name": "My hosted Windows runner",
    57  					"runner_group_id": 2,
    58  					"platform": "win-x64",
    59  					"image_details": {
    60  						"id": "windows-latest",
    61  						"size_gb": 256
    62  					},
    63  					"machine_size_details": {
    64  						"id": "8-core",
    65  						"cpu_cores": 8,
    66  						"memory_gb": 32,
    67  						"storage_gb": 300
    68  					},
    69  					"status": "Ready",
    70  					"maximum_runners": 20,
    71  					"public_ip_enabled": false,
    72  					"public_ips": [],
    73  					"last_active_on": "2023-04-26T15:23:37Z"
    74  				}
    75  			]
    76  		}`)
    77  	})
    78  	opts := &ListOptions{Page: 1, PerPage: 1}
    79  	ctx := context.Background()
    80  	hostedRunners, _, err := client.Enterprise.ListHostedRunners(ctx, "o", opts)
    81  	if err != nil {
    82  		t.Errorf("Enterprise.ListHostedRunners returned error: %v", err)
    83  	}
    84  
    85  	lastActiveOn := Timestamp{time.Date(2023, 4, 26, 15, 23, 37, 0, time.UTC)}
    86  
    87  	want := &HostedRunners{
    88  		TotalCount: 2,
    89  		Runners: []*HostedRunner{
    90  			{
    91  				ID:            Ptr(int64(5)),
    92  				Name:          Ptr("My hosted ubuntu runner"),
    93  				RunnerGroupID: Ptr(int64(2)),
    94  				Platform:      Ptr("linux-x64"),
    95  				ImageDetails: &HostedRunnerImageDetail{
    96  					ID:     Ptr("ubuntu-20.04"),
    97  					SizeGB: Ptr(int64(86)),
    98  				},
    99  				MachineSizeDetails: &HostedRunnerMachineSpec{
   100  					ID:        "4-core",
   101  					CPUCores:  4,
   102  					MemoryGB:  16,
   103  					StorageGB: 150,
   104  				},
   105  				Status:          Ptr("Ready"),
   106  				MaximumRunners:  Ptr(int64(10)),
   107  				PublicIPEnabled: Ptr(true),
   108  				PublicIPs: []*HostedRunnerPublicIP{
   109  					{
   110  						Enabled: true,
   111  						Prefix:  "20.80.208.150",
   112  						Length:  31,
   113  					},
   114  				},
   115  				LastActiveOn: Ptr(lastActiveOn),
   116  			},
   117  			{
   118  				ID:            Ptr(int64(7)),
   119  				Name:          Ptr("My hosted Windows runner"),
   120  				RunnerGroupID: Ptr(int64(2)),
   121  				Platform:      Ptr("win-x64"),
   122  				ImageDetails: &HostedRunnerImageDetail{
   123  					ID:     Ptr("windows-latest"),
   124  					SizeGB: Ptr(int64(256)),
   125  				},
   126  				MachineSizeDetails: &HostedRunnerMachineSpec{
   127  					ID:        "8-core",
   128  					CPUCores:  8,
   129  					MemoryGB:  32,
   130  					StorageGB: 300,
   131  				},
   132  				Status:          Ptr("Ready"),
   133  				MaximumRunners:  Ptr(int64(20)),
   134  				PublicIPEnabled: Ptr(false),
   135  				PublicIPs:       []*HostedRunnerPublicIP{},
   136  				LastActiveOn:    Ptr(lastActiveOn),
   137  			},
   138  		},
   139  	}
   140  	if !cmp.Equal(hostedRunners, want) {
   141  		t.Errorf("Enterprise.ListHostedRunners returned %+v, want %+v", hostedRunners, want)
   142  	}
   143  
   144  	const methodName = "ListHostedRunners"
   145  	testBadOptions(t, methodName, func() (err error) {
   146  		_, _, err = client.Enterprise.ListHostedRunners(ctx, "\n", opts)
   147  		return err
   148  	})
   149  
   150  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   151  		got, resp, err := client.Enterprise.ListHostedRunners(ctx, "o", opts)
   152  		if got != nil {
   153  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   154  		}
   155  		return resp, err
   156  	})
   157  }
   158  
   159  func TestEnterpriseService_CreateHostedRunner(t *testing.T) {
   160  	t.Parallel()
   161  	client, mux, _ := setup(t)
   162  
   163  	mux.HandleFunc("/enterprises/o/actions/hosted-runners", func(w http.ResponseWriter, r *http.Request) {
   164  		testMethod(t, r, "POST")
   165  		fmt.Fprint(w, `{
   166  			"id": 5,
   167  			"name": "My hosted ubuntu runner",
   168  			"runner_group_id": 2,
   169  			"platform": "linux-x64",
   170  			"image_details": {
   171  				"id": "ubuntu-20.04",
   172  				"size_gb": 86
   173  			},
   174  			"machine_size_details": {
   175  				"id": "4-core",
   176  				"cpu_cores": 4,
   177  				"memory_gb": 16,
   178  				"storage_gb": 150
   179  			},
   180  			"status": "Ready",
   181  			"maximum_runners": 10,
   182  			"public_ip_enabled": true,
   183  			"public_ips": [
   184  				{
   185  					"enabled": true,
   186  					"prefix": "20.80.208.150",
   187  					"length": 31
   188  				}
   189  			],
   190  			"last_active_on": "2023-04-26T15:23:37Z"
   191  			}`)
   192  	})
   193  
   194  	ctx := context.Background()
   195  	req := &HostedRunnerRequest{
   196  		Name: "My Hosted runner",
   197  		Image: HostedRunnerImage{
   198  			ID:      "ubuntu-latest",
   199  			Source:  "github",
   200  			Version: "latest",
   201  		},
   202  		RunnerGroupID:  1,
   203  		Size:           "4-core",
   204  		MaximumRunners: 50,
   205  		EnableStaticIP: false,
   206  	}
   207  	hostedRunner, _, err := client.Enterprise.CreateHostedRunner(ctx, "o", req)
   208  	if err != nil {
   209  		t.Errorf("Enterprise.CreateHostedRunner returned error: %v", err)
   210  	}
   211  
   212  	lastActiveOn := Timestamp{time.Date(2023, 4, 26, 15, 23, 37, 0, time.UTC)}
   213  	want := &HostedRunner{
   214  		ID:            Ptr(int64(5)),
   215  		Name:          Ptr("My hosted ubuntu runner"),
   216  		RunnerGroupID: Ptr(int64(2)),
   217  		Platform:      Ptr("linux-x64"),
   218  		ImageDetails: &HostedRunnerImageDetail{
   219  			ID:     Ptr("ubuntu-20.04"),
   220  			SizeGB: Ptr(int64(86)),
   221  		},
   222  		MachineSizeDetails: &HostedRunnerMachineSpec{
   223  			ID:        "4-core",
   224  			CPUCores:  4,
   225  			MemoryGB:  16,
   226  			StorageGB: 150,
   227  		},
   228  		Status:          Ptr("Ready"),
   229  		MaximumRunners:  Ptr(int64(10)),
   230  		PublicIPEnabled: Ptr(true),
   231  		PublicIPs: []*HostedRunnerPublicIP{
   232  			{
   233  				Enabled: true,
   234  				Prefix:  "20.80.208.150",
   235  				Length:  31,
   236  			},
   237  		},
   238  		LastActiveOn: Ptr(lastActiveOn),
   239  	}
   240  
   241  	if !cmp.Equal(hostedRunner, want) {
   242  		t.Errorf("Enterprise.CreateHostedRunner returned %+v, want %+v", hostedRunner, want)
   243  	}
   244  
   245  	// Validation tests
   246  	testCases := []struct {
   247  		name          string
   248  		request       *HostedRunnerRequest
   249  		expectedError string
   250  	}{
   251  		{
   252  			name: "Missing Size",
   253  			request: &HostedRunnerRequest{
   254  				Name: "My Hosted runner",
   255  				Image: HostedRunnerImage{
   256  					ID:      "ubuntu-latest",
   257  					Source:  "github",
   258  					Version: "latest",
   259  				},
   260  				RunnerGroupID: 1,
   261  			},
   262  			expectedError: "validation failed: size is required for creating a hosted runner",
   263  		},
   264  		{
   265  			name: "Missing Image",
   266  			request: &HostedRunnerRequest{
   267  				Name:          "My Hosted runner",
   268  				RunnerGroupID: 1,
   269  				Size:          "4-core",
   270  			},
   271  			expectedError: "validation failed: image is required for creating a hosted runner",
   272  		},
   273  		{
   274  			name: "Missing Name",
   275  			request: &HostedRunnerRequest{
   276  				Image: HostedRunnerImage{
   277  					ID:      "ubuntu-latest",
   278  					Source:  "github",
   279  					Version: "latest",
   280  				},
   281  				RunnerGroupID: 1,
   282  				Size:          "4-core",
   283  			},
   284  			expectedError: "validation failed: name is required for creating a hosted runner",
   285  		},
   286  		{
   287  			name: "Missing RunnerGroupID",
   288  			request: &HostedRunnerRequest{
   289  				Name: "My Hosted runner",
   290  				Image: HostedRunnerImage{
   291  					ID:      "ubuntu-latest",
   292  					Source:  "github",
   293  					Version: "latest",
   294  				},
   295  				Size: "4-core",
   296  			},
   297  			expectedError: "validation failed: runner group ID is required for creating a hosted runner",
   298  		},
   299  		{
   300  			name: "ImageVersion Set Instead of Image Struct",
   301  			request: &HostedRunnerRequest{
   302  				Name: "My Hosted runner",
   303  				Image: HostedRunnerImage{
   304  					ID:      "ubuntu-latest",
   305  					Source:  "github",
   306  					Version: "latest",
   307  				},
   308  				RunnerGroupID:  1,
   309  				Size:           "4-core",
   310  				ImageVersion:   "1.0.0",
   311  				MaximumRunners: 50,
   312  				EnableStaticIP: false,
   313  			},
   314  			expectedError: "validation failed: imageVersion should not be set directly; use the Image struct to specify image details",
   315  		},
   316  	}
   317  
   318  	for _, tt := range testCases {
   319  		_, _, err := client.Enterprise.CreateHostedRunner(ctx, "o", tt.request)
   320  		if err == nil || err.Error() != tt.expectedError {
   321  			t.Errorf("expected error: %v, got: %v", tt.expectedError, err)
   322  		}
   323  	}
   324  
   325  	const methodName = "CreateHostedRunner"
   326  	testBadOptions(t, methodName, func() (err error) {
   327  		_, _, err = client.Enterprise.CreateHostedRunner(ctx, "\n", req)
   328  		return err
   329  	})
   330  
   331  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   332  		got, resp, err := client.Enterprise.CreateHostedRunner(ctx, "o", req)
   333  		if got != nil {
   334  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   335  		}
   336  		return resp, err
   337  	})
   338  }
   339  
   340  func TestEnterpriseService_GetHostedRunnerGitHubOwnedImages(t *testing.T) {
   341  	t.Parallel()
   342  	client, mux, _ := setup(t)
   343  
   344  	mux.HandleFunc("/enterprises/o/actions/hosted-runners/images/github-owned", func(w http.ResponseWriter, r *http.Request) {
   345  		testMethod(t, r, "GET")
   346  		fmt.Fprint(w, `{
   347  			"total_count": 1,
   348  			"images": [
   349  				{
   350  					"id": "ubuntu-20.04",
   351  					"platform": "linux-x64",
   352  					"size_gb": 86,
   353  					"display_name": "20.04",
   354  					"source": "github"
   355  				}
   356  			]
   357  			}`)
   358  	})
   359  
   360  	ctx := context.Background()
   361  	hostedRunnerImages, _, err := client.Enterprise.GetHostedRunnerGitHubOwnedImages(ctx, "o")
   362  	if err != nil {
   363  		t.Errorf("Enterprise.GetHostedRunnerGitHubOwnedImages returned error: %v", err)
   364  	}
   365  
   366  	want := &HostedRunnerImages{
   367  		TotalCount: 1,
   368  		Images: []*HostedRunnerImageSpecs{
   369  			{
   370  				ID:          "ubuntu-20.04",
   371  				Platform:    "linux-x64",
   372  				SizeGB:      86,
   373  				DisplayName: "20.04",
   374  				Source:      "github",
   375  			},
   376  		},
   377  	}
   378  
   379  	if !cmp.Equal(hostedRunnerImages, want) {
   380  		t.Errorf("Enterprise.GetHostedRunnerGitHubOwnedImages returned %+v, want %+v", hostedRunnerImages, want)
   381  	}
   382  
   383  	const methodName = "GetHostedRunnerGitHubOwnedImages"
   384  	testBadOptions(t, methodName, func() (err error) {
   385  		_, _, err = client.Enterprise.GetHostedRunnerGitHubOwnedImages(ctx, "\n")
   386  		return err
   387  	})
   388  
   389  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   390  		got, resp, err := client.Enterprise.GetHostedRunnerGitHubOwnedImages(ctx, "o")
   391  		if got != nil {
   392  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   393  		}
   394  		return resp, err
   395  	})
   396  }
   397  
   398  func TestEnterpriseService_GetHostedRunnerPartnerImages(t *testing.T) {
   399  	t.Parallel()
   400  	client, mux, _ := setup(t)
   401  
   402  	mux.HandleFunc("/enterprises/o/actions/hosted-runners/images/partner", func(w http.ResponseWriter, r *http.Request) {
   403  		testMethod(t, r, "GET")
   404  		fmt.Fprint(w, `{
   405  			"total_count": 1,
   406  			"images": [
   407  				{
   408  					"id": "ubuntu-20.04",
   409  					"platform": "linux-x64",
   410  					"size_gb": 86,
   411  					"display_name": "20.04",
   412  					"source": "partner"
   413  				}
   414  			]
   415  			}`)
   416  	})
   417  
   418  	ctx := context.Background()
   419  	hostedRunnerImages, _, err := client.Enterprise.GetHostedRunnerPartnerImages(ctx, "o")
   420  	if err != nil {
   421  		t.Errorf("Enterprise.GetHostedRunnerPartnerImages returned error: %v", err)
   422  	}
   423  
   424  	want := &HostedRunnerImages{
   425  		TotalCount: 1,
   426  		Images: []*HostedRunnerImageSpecs{
   427  			{
   428  				ID:          "ubuntu-20.04",
   429  				Platform:    "linux-x64",
   430  				SizeGB:      86,
   431  				DisplayName: "20.04",
   432  				Source:      "partner",
   433  			},
   434  		},
   435  	}
   436  
   437  	if !cmp.Equal(hostedRunnerImages, want) {
   438  		t.Errorf("Enterprise.GetHostedRunnerPartnerImages returned %+v, want %+v", hostedRunnerImages, want)
   439  	}
   440  
   441  	const methodName = "GetHostedRunnerPartnerImages"
   442  	testBadOptions(t, methodName, func() (err error) {
   443  		_, _, err = client.Enterprise.GetHostedRunnerPartnerImages(ctx, "\n")
   444  		return err
   445  	})
   446  
   447  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   448  		got, resp, err := client.Enterprise.GetHostedRunnerPartnerImages(ctx, "o")
   449  		if got != nil {
   450  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   451  		}
   452  		return resp, err
   453  	})
   454  }
   455  
   456  func TestEnterpriseService_GetHostedRunnerLimits(t *testing.T) {
   457  	t.Parallel()
   458  	client, mux, _ := setup(t)
   459  	mux.HandleFunc("/enterprises/o/actions/hosted-runners/limits", func(w http.ResponseWriter, r *http.Request) {
   460  		testMethod(t, r, "GET")
   461  		fmt.Fprint(w, `{
   462  			"public_ips": {
   463  				"current_usage": 17,
   464  				"maximum": 50
   465  			}
   466  		}`)
   467  	})
   468  
   469  	ctx := context.Background()
   470  	publicIPLimits, _, err := client.Enterprise.GetHostedRunnerLimits(ctx, "o")
   471  	if err != nil {
   472  		t.Errorf("Enterprise.GetPartnerImages returned error: %v", err)
   473  	}
   474  
   475  	want := &HostedRunnerPublicIPLimits{
   476  		PublicIPs: &PublicIPUsage{
   477  			CurrentUsage: 17,
   478  			Maximum:      50,
   479  		},
   480  	}
   481  
   482  	if !cmp.Equal(publicIPLimits, want) {
   483  		t.Errorf("Enterprise.GetHostedRunnerLimits returned %+v, want %+v", publicIPLimits, want)
   484  	}
   485  
   486  	const methodName = "GetHostedRunnerLimits"
   487  	testBadOptions(t, methodName, func() (err error) {
   488  		_, _, err = client.Enterprise.GetHostedRunnerLimits(ctx, "\n")
   489  		return err
   490  	})
   491  
   492  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   493  		got, resp, err := client.Enterprise.GetHostedRunnerLimits(ctx, "o")
   494  		if got != nil {
   495  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   496  		}
   497  		return resp, err
   498  	})
   499  }
   500  
   501  func TestEnterpriseService_GetHostedRunnerMachineSpecs(t *testing.T) {
   502  	t.Parallel()
   503  	client, mux, _ := setup(t)
   504  
   505  	mux.HandleFunc("/enterprises/o/actions/hosted-runners/machine-sizes", func(w http.ResponseWriter, r *http.Request) {
   506  		testMethod(t, r, "GET")
   507  		fmt.Fprint(w, `{
   508  			"total_count": 1,
   509  			"machine_specs": [
   510  				{
   511  					"id": "4-core",
   512   	 				"cpu_cores": 4,
   513    					"memory_gb": 16,
   514    					"storage_gb": 150
   515  				}
   516  			]
   517  			}`)
   518  	})
   519  
   520  	ctx := context.Background()
   521  	machineSpecs, _, err := client.Enterprise.GetHostedRunnerMachineSpecs(ctx, "o")
   522  	if err != nil {
   523  		t.Errorf("Enterprise.GetHostedRunnerMachineSpecs returned error: %v", err)
   524  	}
   525  	want := &HostedRunnerMachineSpecs{
   526  		TotalCount: 1,
   527  		MachineSpecs: []*HostedRunnerMachineSpec{
   528  			{
   529  				ID:        "4-core",
   530  				CPUCores:  4,
   531  				MemoryGB:  16,
   532  				StorageGB: 150,
   533  			},
   534  		},
   535  	}
   536  
   537  	if !cmp.Equal(machineSpecs, want) {
   538  		t.Errorf("Enterprise.GetHostedRunnerMachineSpecs returned %+v, want %+v", machineSpecs, want)
   539  	}
   540  
   541  	const methodName = "GetHostedRunnerMachineSpecs"
   542  	testBadOptions(t, methodName, func() (err error) {
   543  		_, _, err = client.Enterprise.GetHostedRunnerMachineSpecs(ctx, "\n")
   544  		return err
   545  	})
   546  
   547  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   548  		got, resp, err := client.Enterprise.GetHostedRunnerMachineSpecs(ctx, "o")
   549  		if got != nil {
   550  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   551  		}
   552  		return resp, err
   553  	})
   554  }
   555  
   556  func TestEnterpriseService_GetHostedRunnerPlatforms(t *testing.T) {
   557  	t.Parallel()
   558  	client, mux, _ := setup(t)
   559  
   560  	mux.HandleFunc("/enterprises/o/actions/hosted-runners/platforms", func(w http.ResponseWriter, r *http.Request) {
   561  		testMethod(t, r, "GET")
   562  		fmt.Fprint(w, `{
   563  			"total_count": 1,
   564    			"platforms": [
   565      			"linux-x64",
   566      			"win-x64"
   567    			]
   568  		}`)
   569  	})
   570  
   571  	ctx := context.Background()
   572  	platforms, _, err := client.Enterprise.GetHostedRunnerPlatforms(ctx, "o")
   573  	if err != nil {
   574  		t.Errorf("Enterprise.GetHostedRunnerPlatforms returned error: %v", err)
   575  	}
   576  	want := &HostedRunnerPlatforms{
   577  		TotalCount: 1,
   578  		Platforms: []string{
   579  			"linux-x64",
   580  			"win-x64",
   581  		},
   582  	}
   583  
   584  	if !cmp.Equal(platforms, want) {
   585  		t.Errorf("Enterprise.GetHostedRunnerPlatforms returned %+v, want %+v", platforms, want)
   586  	}
   587  
   588  	const methodName = "GetHostedRunnerPlatforms"
   589  	testBadOptions(t, methodName, func() (err error) {
   590  		_, _, err = client.Enterprise.GetHostedRunnerPlatforms(ctx, "\n")
   591  		return err
   592  	})
   593  
   594  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   595  		got, resp, err := client.Enterprise.GetHostedRunnerPlatforms(ctx, "o")
   596  		if got != nil {
   597  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   598  		}
   599  		return resp, err
   600  	})
   601  }
   602  
   603  func TestEnterpriseService_GetHostedRunner(t *testing.T) {
   604  	t.Parallel()
   605  	client, mux, _ := setup(t)
   606  
   607  	mux.HandleFunc("/enterprises/o/actions/hosted-runners/23", func(w http.ResponseWriter, r *http.Request) {
   608  		testMethod(t, r, "GET")
   609  		fmt.Fprint(w, `{
   610  			"id": 5,
   611  			"name": "My hosted ubuntu runner",
   612  			"runner_group_id": 2,
   613  			"platform": "linux-x64",
   614  			"image_details": {
   615  				"id": "ubuntu-20.04",
   616  				"size_gb": 86
   617  			},
   618  			"machine_size_details": {
   619  				"id": "4-core",
   620  				"cpu_cores": 4,
   621  				"memory_gb": 16,
   622  				"storage_gb": 150
   623  			},
   624  			"status": "Ready",
   625  			"maximum_runners": 10,
   626  			"public_ip_enabled": true,
   627  			"public_ips": [
   628  				{
   629  				"enabled": true,
   630  				"prefix": "20.80.208.150",
   631  				"length": 31
   632  				}
   633  			],
   634  			"last_active_on": "2023-04-26T15:23:37Z"
   635  		}`)
   636  	})
   637  
   638  	ctx := context.Background()
   639  	hostedRunner, _, err := client.Enterprise.GetHostedRunner(ctx, "o", 23)
   640  	if err != nil {
   641  		t.Errorf("Enterprise.GetHostedRunner returned error: %v", err)
   642  	}
   643  
   644  	lastActiveOn := Timestamp{time.Date(2023, 4, 26, 15, 23, 37, 0, time.UTC)}
   645  	want := &HostedRunner{
   646  		ID:            Ptr(int64(5)),
   647  		Name:          Ptr("My hosted ubuntu runner"),
   648  		RunnerGroupID: Ptr(int64(2)),
   649  		Platform:      Ptr("linux-x64"),
   650  		ImageDetails: &HostedRunnerImageDetail{
   651  			ID:     Ptr("ubuntu-20.04"),
   652  			SizeGB: Ptr(int64(86)),
   653  		},
   654  		MachineSizeDetails: &HostedRunnerMachineSpec{
   655  			ID:        "4-core",
   656  			CPUCores:  4,
   657  			MemoryGB:  16,
   658  			StorageGB: 150,
   659  		},
   660  		Status:          Ptr("Ready"),
   661  		MaximumRunners:  Ptr(int64(10)),
   662  		PublicIPEnabled: Ptr(true),
   663  		PublicIPs: []*HostedRunnerPublicIP{
   664  			{
   665  				Enabled: true,
   666  				Prefix:  "20.80.208.150",
   667  				Length:  31,
   668  			},
   669  		},
   670  		LastActiveOn: Ptr(lastActiveOn),
   671  	}
   672  
   673  	if !cmp.Equal(hostedRunner, want) {
   674  		t.Errorf("Enterprise.GetHostedRunner returned %+v, want %+v", hostedRunner, want)
   675  	}
   676  
   677  	const methodName = "GetHostedRunner"
   678  	testBadOptions(t, methodName, func() (err error) {
   679  		_, _, err = client.Enterprise.GetHostedRunner(ctx, "\n", 23)
   680  		return err
   681  	})
   682  
   683  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   684  		got, resp, err := client.Enterprise.GetHostedRunner(ctx, "o", 23)
   685  		if got != nil {
   686  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   687  		}
   688  		return resp, err
   689  	})
   690  }
   691  
   692  func TestEnterpriseService_UpdateHostedRunner(t *testing.T) {
   693  	t.Parallel()
   694  	client, mux, _ := setup(t)
   695  
   696  	mux.HandleFunc("/enterprises/o/actions/hosted-runners/23", func(w http.ResponseWriter, r *http.Request) {
   697  		testMethod(t, r, "PATCH")
   698  		fmt.Fprint(w, `{
   699  			"id": 5,
   700  			"name": "My hosted ubuntu runner",
   701  			"runner_group_id": 2,
   702  			"platform": "linux-x64",
   703  			"image_details": {
   704  				"id": "ubuntu-20.04",
   705  				"size_gb": 86
   706  			},
   707  			"machine_size_details": {
   708  				"id": "4-core",
   709  				"cpu_cores": 4,
   710  				"memory_gb": 16,
   711  				"storage_gb": 150
   712  			},
   713  			"status": "Ready",
   714  			"maximum_runners": 10,
   715  			"public_ip_enabled": true,
   716  			"public_ips": [
   717  				{
   718  					"enabled": true,
   719  					"prefix": "20.80.208.150",
   720  					"length": 31
   721  				}
   722  			],
   723  			"last_active_on": "2023-04-26T15:23:37Z"
   724  		}`)
   725  	})
   726  
   727  	// Test for a valid update without `Size`
   728  	ctx := context.Background()
   729  	validReq := HostedRunnerRequest{
   730  		Name:           "My larger runner",
   731  		RunnerGroupID:  1,
   732  		MaximumRunners: 50,
   733  		EnableStaticIP: false,
   734  		ImageVersion:   "1.0.0",
   735  	}
   736  	hostedRunner, _, err := client.Enterprise.UpdateHostedRunner(ctx, "o", 23, validReq)
   737  	if err != nil {
   738  		t.Errorf("Enterprise.UpdateHostedRunner returned error: %v", err)
   739  	}
   740  
   741  	lastActiveOn := Timestamp{time.Date(2023, 4, 26, 15, 23, 37, 0, time.UTC)}
   742  	want := &HostedRunner{
   743  		ID:            Ptr(int64(5)),
   744  		Name:          Ptr("My hosted ubuntu runner"),
   745  		RunnerGroupID: Ptr(int64(2)),
   746  		Platform:      Ptr("linux-x64"),
   747  		ImageDetails: &HostedRunnerImageDetail{
   748  			ID:     Ptr("ubuntu-20.04"),
   749  			SizeGB: Ptr(int64(86)),
   750  		},
   751  		MachineSizeDetails: &HostedRunnerMachineSpec{
   752  			ID:        "4-core",
   753  			CPUCores:  4,
   754  			MemoryGB:  16,
   755  			StorageGB: 150,
   756  		},
   757  		Status:          Ptr("Ready"),
   758  		MaximumRunners:  Ptr(int64(10)),
   759  		PublicIPEnabled: Ptr(true),
   760  		PublicIPs: []*HostedRunnerPublicIP{
   761  			{
   762  				Enabled: true,
   763  				Prefix:  "20.80.208.150",
   764  				Length:  31,
   765  			},
   766  		},
   767  		LastActiveOn: Ptr(lastActiveOn),
   768  	}
   769  
   770  	if !cmp.Equal(hostedRunner, want) {
   771  		t.Errorf("Enterprise.UpdateHostedRunner returned %+v, want %+v", hostedRunner, want)
   772  	}
   773  
   774  	testCases := []struct {
   775  		name          string
   776  		request       HostedRunnerRequest
   777  		expectedError string
   778  	}{
   779  		{
   780  			name: "Size Set in Update Request",
   781  			request: HostedRunnerRequest{
   782  				Name:           "My larger runner",
   783  				RunnerGroupID:  1,
   784  				MaximumRunners: 50,
   785  				EnableStaticIP: false,
   786  				ImageVersion:   "1.0.0",
   787  				Size:           "4-core", // Should cause validation error
   788  			},
   789  			expectedError: "validation failed: size cannot be updated, API does not support updating size",
   790  		},
   791  		{
   792  			name: "Image Set in Update Request",
   793  			request: HostedRunnerRequest{
   794  				Name:           "My larger runner",
   795  				RunnerGroupID:  1,
   796  				MaximumRunners: 50,
   797  				EnableStaticIP: false,
   798  				ImageVersion:   "1.0.0",
   799  				Image: HostedRunnerImage{ // Should cause validation error
   800  					ID:      "ubuntu-latest",
   801  					Source:  "github",
   802  					Version: "latest",
   803  				},
   804  			},
   805  			expectedError: "validation failed: image struct should not be set directly; use the ImageVersion to specify version details",
   806  		},
   807  	}
   808  	for _, tt := range testCases {
   809  		_, _, err := client.Enterprise.UpdateHostedRunner(ctx, "o", 23, tt.request)
   810  		if err == nil || err.Error() != tt.expectedError {
   811  			t.Errorf("expected error: %v, got: %v", tt.expectedError, err)
   812  		}
   813  	}
   814  
   815  	const methodName = "UpdateHostedRunner"
   816  	testBadOptions(t, methodName, func() (err error) {
   817  		_, _, err = client.Enterprise.UpdateHostedRunner(ctx, "\n", 23, validReq)
   818  		return err
   819  	})
   820  
   821  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   822  		got, resp, err := client.Enterprise.UpdateHostedRunner(ctx, "o", 23, validReq)
   823  		if got != nil {
   824  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   825  		}
   826  		return resp, err
   827  	})
   828  }
   829  
   830  func TestEnterpriseService_DeleteHostedRunner(t *testing.T) {
   831  	t.Parallel()
   832  	client, mux, _ := setup(t)
   833  
   834  	mux.HandleFunc("/enterprises/o/actions/hosted-runners/23", func(w http.ResponseWriter, r *http.Request) {
   835  		testMethod(t, r, "DELETE")
   836  		fmt.Fprint(w, `{
   837  			"id": 5,
   838  			"name": "My hosted ubuntu runner",
   839  			"runner_group_id": 2,
   840  			"platform": "linux-x64",
   841  			"image_details": {
   842  				"id": "ubuntu-20.04",
   843  				"size_gb": 86
   844  			},
   845  			"machine_size_details": {
   846  				"id": "4-core",
   847  				"cpu_cores": 4,
   848  				"memory_gb": 16,
   849  				"storage_gb": 150
   850  			},
   851  			"status": "Ready",
   852  			"maximum_runners": 10,
   853  			"public_ip_enabled": true,
   854  			"public_ips": [
   855  				{
   856  				"enabled": true,
   857  				"prefix": "20.80.208.150",
   858  				"length": 31
   859  				}
   860  			],
   861  			"last_active_on": "2023-04-26T15:23:37Z"
   862  		}`)
   863  	})
   864  
   865  	ctx := context.Background()
   866  	hostedRunner, _, err := client.Enterprise.DeleteHostedRunner(ctx, "o", 23)
   867  	if err != nil {
   868  		t.Errorf("Enterprise.GetHostedRunner returned error: %v", err)
   869  	}
   870  
   871  	lastActiveOn := Timestamp{time.Date(2023, 4, 26, 15, 23, 37, 0, time.UTC)}
   872  	want := &HostedRunner{
   873  		ID:            Ptr(int64(5)),
   874  		Name:          Ptr("My hosted ubuntu runner"),
   875  		RunnerGroupID: Ptr(int64(2)),
   876  		Platform:      Ptr("linux-x64"),
   877  		ImageDetails: &HostedRunnerImageDetail{
   878  			ID:     Ptr("ubuntu-20.04"),
   879  			SizeGB: Ptr(int64(86)),
   880  		},
   881  		MachineSizeDetails: &HostedRunnerMachineSpec{
   882  			ID:        "4-core",
   883  			CPUCores:  4,
   884  			MemoryGB:  16,
   885  			StorageGB: 150,
   886  		},
   887  		Status:          Ptr("Ready"),
   888  		MaximumRunners:  Ptr(int64(10)),
   889  		PublicIPEnabled: Ptr(true),
   890  		PublicIPs: []*HostedRunnerPublicIP{
   891  			{
   892  				Enabled: true,
   893  				Prefix:  "20.80.208.150",
   894  				Length:  31,
   895  			},
   896  		},
   897  		LastActiveOn: Ptr(lastActiveOn),
   898  	}
   899  
   900  	if !cmp.Equal(hostedRunner, want) {
   901  		t.Errorf("Enterprise.DeleteHostedRunner returned %+v, want %+v", hostedRunner, want)
   902  	}
   903  
   904  	const methodName = "DeleteHostedRunner"
   905  	testBadOptions(t, methodName, func() (err error) {
   906  		_, _, err = client.Enterprise.DeleteHostedRunner(ctx, "\n", 23)
   907  		return err
   908  	})
   909  
   910  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   911  		got, resp, err := client.Enterprise.DeleteHostedRunner(ctx, "o", 23)
   912  		if got != nil {
   913  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   914  		}
   915  		return resp, err
   916  	})
   917  }