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