hunk.code_window:   return (
       <Widget>
         <WidgetTitle
           icon="fa-laptop"
           title="Automatic Edge Environment Creation"
         />
         <WidgetBody>
           <AutoEnvCreationSettingsForm settings={settingsQuery.data} />
  
hunk.labels: keep keep keep replace keep keep keep keep
hunk.after_edit:         icon="svg-laptop"
  
hunk.file_path: app/portainer/settings/edge-compute/AutomaticEdgeEnvCreation/AutomaticEdgeEnvCreation.tsx
hunk.type: replace
file: package endpointedge
  
  import (
  	"context"
  	"encoding/json"
  	"fmt"
  	"net/http"
  	"net/http/httptest"
  	"os"
  	"testing"
  	"time"
  
  	portainer "github.com/portainer/portainer/api"
  	"github.com/portainer/portainer/api/apikey"
  	"github.com/portainer/portainer/api/chisel"
  	"github.com/portainer/portainer/api/datastore"
  	"github.com/portainer/portainer/api/filesystem"
  	"github.com/portainer/portainer/api/http/security"
  	"github.com/portainer/portainer/api/jwt"
  
  	"github.com/stretchr/testify/assert"
  )
  
  type endpointTestCase struct {
  	endpoint           portainer.Endpoint
  	endpointRelation   portainer.EndpointRelation
  	expectedStatusCode int
  }
  
  var endpointTestCases = []endpointTestCase{
  	{
  		portainer.Endpoint{},
  		portainer.EndpointRelation{},
  		http.StatusNotFound,
  	},
  	{
  		portainer.Endpoint{
  			ID:     -1,
  			Name:   "endpoint-id--1",
  			Type:   portainer.EdgeAgentOnDockerEnvironment,
  			URL:    "https://portainer.io:9443",
  			EdgeID: "edge-id",
  		},
  		portainer.EndpointRelation{
  			EndpointID: -1,
  		},
  		http.StatusNotFound,
  	},
  	{
  		portainer.Endpoint{
  			ID:     2,
  			Name:   "endpoint-id-2",
  			Type:   portainer.EdgeAgentOnDockerEnvironment,
  			URL:    "https://portainer.io:9443",
  			EdgeID: "",
  		},
  		portainer.EndpointRelation{
  			EndpointID: 2,
  		},
  		http.StatusBadRequest,
  	},
  	{
  		portainer.Endpoint{
  			ID:     4,
  			Name:   "endpoint-id-4",
  			Type:   portainer.EdgeAgentOnDockerEnvironment,
  			URL:    "https://portainer.io:9443",
  			EdgeID: "edge-id",
  		},
  		portainer.EndpointRelation{
  			EndpointID: 4,
  		},
  		http.StatusOK,
  	},
  }
  
  func setupHandler() (*Handler, func(), error) {
  	tmpDir, err := os.MkdirTemp(os.TempDir(), "portainer-test")
  	if err != nil {
  		return nil, nil, fmt.Errorf("could not create a tmp dir: %w", err)
  	}
  
  	fs, err := filesystem.NewService(tmpDir, "")
  	if err != nil {
  		return nil, nil, fmt.Errorf("could not start a new filesystem service: %w", err)
  	}
  
  	_, store, storeTeardown := datastore.MustNewTestStore(true, true)
  
  	ctx := context.Background()
  	shutdownCtx, cancelFn := context.WithCancel(ctx)
  
  	teardown := func() {
  		cancelFn()
  		storeTeardown()
  	}
  
  	jwtService, err := jwt.NewService("1h", store)
  	if err != nil {
  		teardown()
  		return nil, nil, fmt.Errorf("could not start a new jwt service: %w", err)
  	}
  
  	apiKeyService := apikey.NewAPIKeyService(nil, nil)
  
  	settings, err := store.Settings().Settings()
  	if err != nil {
  		teardown()
  		return nil, nil, fmt.Errorf("could not create new settings: %w", err)
  	}
  	settings.TrustOnFirstConnect = true
  
  	err = store.Settings().UpdateSettings(settings)
  	if err != nil {
  		teardown()
  		return nil, nil, fmt.Errorf("could not update settings: %w", err)
  	}
  
  	handler := NewHandler(
  		security.NewRequestBouncer(store, jwtService, apiKeyService),
  		store,
  		fs,
  		chisel.NewService(store, shutdownCtx),
  	)
  
  	handler.ReverseTunnelService = chisel.NewService(store, shutdownCtx)
  
  	return handler, teardown, nil
  }
  
  func createEndpoint(handler *Handler, endpoint portainer.Endpoint, endpointRelation portainer.EndpointRelation) (err error) {
  	// Avoid setting ID below 0 to generate invalid test cases
  	if endpoint.ID <= 0 {
  		return nil
  	}
  
  	err = handler.DataStore.Endpoint().Create(&endpoint)
  	if err != nil {
  		return err
  	}
  
  	return handler.DataStore.EndpointRelation().Create(&endpointRelation)
  }
  
  func TestMissingEdgeIdentifier(t *testing.T) {
  	handler, teardown, err := setupHandler()
  	defer teardown()
  
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	endpointID := portainer.EndpointID(45)
  	err = createEndpoint(handler, portainer.Endpoint{
  		ID:     endpointID,
  		Name:   "endpoint-id-45",
  		Type:   portainer.EdgeAgentOnDockerEnvironment,
  		URL:    "https://portainer.io:9443",
  		EdgeID: "edge-id",
  	}, portainer.EndpointRelation{EndpointID: endpointID})
  
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%d/edge/status", endpointID), nil)
  	if err != nil {
  		t.Fatal("request error:", err)
  	}
  
  	rec := httptest.NewRecorder()
  	handler.ServeHTTP(rec, req)
  
  	if rec.Code != http.StatusForbidden {
  		t.Fatalf(fmt.Sprintf("expected a %d response, found: %d without Edge identifier", http.StatusForbidden, rec.Code))
  	}
  }
  
  func TestWithEndpoints(t *testing.T) {
  	handler, teardown, err := setupHandler()
  	defer teardown()
  
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	for _, test := range endpointTestCases {
  		err = createEndpoint(handler, test.endpoint, test.endpointRelation)
  		if err != nil {
  			t.Fatal(err)
  		}
  
  		req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%d/edge/status", test.endpoint.ID), nil)
  		if err != nil {
  			t.Fatal("request error:", err)
  		}
  		req.Header.Set(portainer.PortainerAgentEdgeIDHeader, "edge-id")
  
  		rec := httptest.NewRecorder()
  		handler.ServeHTTP(rec, req)
  
  		if rec.Code != test.expectedStatusCode {
  			t.Fatalf(fmt.Sprintf("expected a %d response, found: %d for endpoint ID: %d", test.expectedStatusCode, rec.Code, test.endpoint.ID))
  		}
  	}
  }
  
  func TestLastCheckInDateIncreases(t *testing.T) {
  	handler, teardown, err := setupHandler()
  	defer teardown()
  
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	endpointID := portainer.EndpointID(56)
  	endpoint := portainer.Endpoint{
  		ID:              endpointID,
  		Name:            "test-endpoint-56",
  		Type:            portainer.EdgeAgentOnDockerEnvironment,
  		URL:             "https://portainer.io:9443",
  		EdgeID:          "edge-id",
  		LastCheckInDate: time.Now().Unix(),
  	}
  
  	endpointRelation := portainer.EndpointRelation{
  		EndpointID: endpoint.ID,
  	}
  
  	err = createEndpoint(handler, endpoint, endpointRelation)
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	time.Sleep(1 * time.Second)
  
  	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%d/edge/status", endpoint.ID), nil)
  	if err != nil {
  		t.Fatal("request error:", err)
  	}
  	req.Header.Set(portainer.PortainerAgentEdgeIDHeader, "edge-id")
  
  	rec := httptest.NewRecorder()
  	handler.ServeHTTP(rec, req)
  
  	if rec.Code != http.StatusOK {
  		t.Fatalf(fmt.Sprintf("expected a %d response, found: %d", http.StatusOK, rec.Code))
  	}
  
  	updatedEndpoint, err := handler.DataStore.Endpoint().Endpoint(endpoint.ID)
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	assert.Greater(t, updatedEndpoint.LastCheckInDate, endpoint.LastCheckInDate)
  }
  
  func TestEmptyEdgeIdWithAgentPlatformHeader(t *testing.T) {
  	handler, teardown, err := setupHandler()
  	defer teardown()
  
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	endpointID := portainer.EndpointID(44)
  	edgeId := "edge-id"
  	endpoint := portainer.Endpoint{
  		ID:     endpointID,
  		Name:   "test-endpoint-44",
  		Type:   portainer.EdgeAgentOnDockerEnvironment,
  		URL:    "https://portainer.io:9443",
  		EdgeID: "",
  	}
  	endpointRelation := portainer.EndpointRelation{
  		EndpointID: endpoint.ID,
  	}
  
  	err = createEndpoint(handler, endpoint, endpointRelation)
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%d/edge/status", endpoint.ID), nil)
  	if err != nil {
  		t.Fatal("request error:", err)
  	}
  	req.Header.Set(portainer.PortainerAgentEdgeIDHeader, edgeId)
  	req.Header.Set(portainer.HTTPResponseAgentPlatform, "1")
  
  	rec := httptest.NewRecorder()
  	handler.ServeHTTP(rec, req)
  
  	if rec.Code != http.StatusOK {
  		t.Fatalf(fmt.Sprintf("expected a %d response, found: %d with empty edge ID", http.StatusOK, rec.Code))
  	}
  
  	updatedEndpoint, err := handler.DataStore.Endpoint().Endpoint(endpoint.ID)
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	assert.Equal(t, updatedEndpoint.EdgeID, edgeId)
  }
  
  func TestEdgeStackStatus(t *testing.T) {
  	handler, teardown, err := setupHandler()
  	defer teardown()
  
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	endpointID := portainer.EndpointID(7)
  	endpoint := portainer.Endpoint{
  		ID:              endpointID,
  		Name:            "test-endpoint-7",
  		Type:            portainer.EdgeAgentOnDockerEnvironment,
  		URL:             "https://portainer.io:9443",
  		EdgeID:          "edge-id",
  		LastCheckInDate: time.Now().Unix(),
  	}
  
  	edgeStackID := portainer.EdgeStackID(17)
  	edgeStack := portainer.EdgeStack{
  		ID:   edgeStackID,
  		Name: "test-edge-stack-17",
  		Status: map[portainer.EndpointID]portainer.EdgeStackStatus{
  			endpointID: {Type: portainer.StatusOk, Error: "", EndpointID: endpoint.ID},
  		},
  		CreationDate:   time.Now().Unix(),
  		EdgeGroups:     []portainer.EdgeGroupID{1, 2},
  		ProjectPath:    "/project/path",
  		EntryPoint:     "entrypoint",
  		Version:        237,
  		ManifestPath:   "/manifest/path",
  		DeploymentType: 1,
  	}
  
  	endpointRelation := portainer.EndpointRelation{
  		EndpointID: endpoint.ID,
  		EdgeStacks: map[portainer.EdgeStackID]bool{
  			edgeStack.ID: true,
  		},
  	}
  	handler.DataStore.EdgeStack().Create(edgeStack.ID, &edgeStack)
  
  	err = createEndpoint(handler, endpoint, endpointRelation)
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%d/edge/status", endpoint.ID), nil)
  	if err != nil {
  		t.Fatal("request error:", err)
  	}
  	req.Header.Set(portainer.PortainerAgentEdgeIDHeader, "edge-id")
  
  	rec := httptest.NewRecorder()
  	handler.ServeHTTP(rec, req)
  
  	if rec.Code != http.StatusOK {
  		t.Fatalf(fmt.Sprintf("expected a %d response, found: %d", http.StatusOK, rec.Code))
  	}
  
  	var data endpointEdgeStatusInspectResponse
  	err = json.NewDecoder(rec.Body).Decode(&data)
  	if err != nil {
  		t.Fatal("error decoding response:", err)
  	}
  
  	assert.Len(t, data.Stacks, 1)
  	assert.Equal(t, edgeStack.ID, data.Stacks[0].ID)
  	assert.Equal(t, edgeStack.Version, data.Stacks[0].Version)
  }
  
  func TestEdgeJobsResponse(t *testing.T) {
  	handler, teardown, err := setupHandler()
  	defer teardown()
  
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	endpointID := portainer.EndpointID(77)
  	endpoint := portainer.Endpoint{
  		ID:              endpointID,
  		Name:            "test-endpoint-77",
  		Type:            portainer.EdgeAgentOnDockerEnvironment,
  		URL:             "https://portainer.io:9443",
  		EdgeID:          "edge-id",
  		LastCheckInDate: time.Now().Unix(),
  	}
  
  	endpointRelation := portainer.EndpointRelation{
  		EndpointID: endpoint.ID,
  	}
  
  	err = createEndpoint(handler, endpoint, endpointRelation)
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	path, err := handler.FileService.StoreEdgeJobFileFromBytes("test-script", []byte("pwd"))
  	if err != nil {
  		t.Fatal(err)
  	}
  
  	edgeJobID := portainer.EdgeJobID(35)
  	edgeJob := portainer.EdgeJob{
  		ID:             edgeJobID,
  		Created:        time.Now().Unix(),
  		CronExpression: "* * * * *",
  		Name:           "test-edge-job",
  		ScriptPath:     path,
  		Recurring:      true,
  		Version:        57,
  	}
  
  	handler.ReverseTunnelService.AddEdgeJob(endpoint.ID, &edgeJob)
  
  	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%d/edge/status", endpoint.ID), nil)
  	if err != nil {
  		t.Fatal("request error:", err)
  	}
  	req.Header.Set(portainer.PortainerAgentEdgeIDHeader, "edge-id")
  
  	rec := httptest.NewRecorder()
  	handler.ServeHTTP(rec, req)
  
  	if rec.Code != http.StatusOK {
  		t.Fatalf(fmt.Sprintf("expected a %d response, found: %d", http.StatusOK, rec.Code))
  	}
  
  	var data endpointEdgeStatusInspectResponse
  	err = json.NewDecoder(rec.Body).Decode(&data)
  	if err != nil {
  		t.Fatal("error decoding response:", err)
  	}
  
  	assert.Len(t, data.Schedules, 1)
  	assert.Equal(t, edgeJob.ID, data.Schedules[0].ID)
  	assert.Equal(t, edgeJob.CronExpression, data.Schedules[0].CronExpression)
  	assert.Equal(t, edgeJob.Version, data.Schedules[0].Version)
  }
  