...

Source file src/edge-infra.dev/pkg/sds/interlock/topic/cluster/cluster_int_test.go

Documentation: edge-infra.dev/pkg/sds/interlock/topic/cluster

     1  package cluster_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"os"
    10  	"testing"
    11  
    12  	"github.com/gin-gonic/gin"
    13  	"github.com/go-logr/logr/testr"
    14  	"github.com/spf13/afero"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  	kruntime "k8s.io/apimachinery/pkg/runtime"
    18  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    19  	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
    20  	ctrl "sigs.k8s.io/controller-runtime"
    21  	"sigs.k8s.io/controller-runtime/pkg/cache/informertest"
    22  	"sigs.k8s.io/controller-runtime/pkg/client"
    23  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    24  
    25  	"edge-infra.dev/pkg/edge/info"
    26  	"edge-infra.dev/pkg/sds/interlock/internal/config"
    27  	"edge-infra.dev/pkg/sds/interlock/internal/errors"
    28  	"edge-infra.dev/pkg/sds/interlock/topic/cluster"
    29  	"edge-infra.dev/pkg/sds/interlock/websocket"
    30  	"edge-infra.dev/pkg/sds/lib/k8s/retryclient"
    31  )
    32  
    33  var testClusterName = "test-cluster"
    34  
    35  func init() {
    36  	gin.SetMode(gin.TestMode)
    37  	// required to prevent errors around getting InCluster configuration for k8s
    38  	// client
    39  	os.Setenv("KUBERNETES_SERVICE_HOST", "1")
    40  	os.Setenv("KUBERNETES_SERVICE_PORT", "1")
    41  
    42  	// required to prevent hostname required error
    43  	os.Setenv("NODE_NAME", "test-node")
    44  }
    45  
    46  func SetupTestCtx(t *testing.T) context.Context {
    47  	logOptions := testr.Options{
    48  		LogTimestamp: true,
    49  		Verbosity:    -1,
    50  	}
    51  
    52  	ctx := ctrl.LoggerInto(context.Background(), testr.NewWithOptions(t, logOptions))
    53  	return ctx
    54  }
    55  
    56  func TestGetState(t *testing.T) {
    57  	r, err := initializeClusterRouter(SetupTestCtx(t))
    58  	require.NoError(t, err)
    59  
    60  	w := httptest.NewRecorder()
    61  	req, _ := http.NewRequest("GET", cluster.Path, nil)
    62  	r.ServeHTTP(w, req)
    63  
    64  	assert.Equal(t, http.StatusOK, w.Code)
    65  
    66  	actual := &cluster.State{}
    67  	require.NoError(t, json.Unmarshal(w.Body.Bytes(), actual))
    68  
    69  	expected := &cluster.State{
    70  		Name: testClusterName,
    71  	}
    72  	assert.Equal(t, expected, actual)
    73  }
    74  
    75  type NameEditor struct {
    76  	Name string `json:"name"`
    77  }
    78  
    79  type Errors struct {
    80  	Errors []*errors.Error `json:"errors"`
    81  }
    82  
    83  func TestPatchState(t *testing.T) {
    84  	r, err := initializeClusterRouter(SetupTestCtx(t))
    85  	require.NoError(t, err)
    86  
    87  	testCases := map[string]struct {
    88  		input            interface{}
    89  		expectedStatus   int
    90  		response         interface{}
    91  		expectedResponse interface{}
    92  	}{
    93  		"Success": {
    94  			input:          struct{}{},
    95  			expectedStatus: http.StatusAccepted,
    96  			response:       &cluster.State{},
    97  			expectedResponse: &cluster.State{
    98  				Name: testClusterName,
    99  			},
   100  		},
   101  		"Faillure_ReadOnlyName": {
   102  			input: NameEditor{
   103  				Name: "any-name",
   104  			},
   105  			expectedStatus: http.StatusBadRequest,
   106  			response:       &Errors{},
   107  			expectedResponse: &Errors{
   108  				Errors: []*errors.Error{
   109  					{
   110  						Detail: errors.NewReadOnlyFieldMessage("Name"),
   111  					},
   112  				},
   113  			},
   114  		},
   115  	}
   116  
   117  	for name, tc := range testCases {
   118  		t.Run(name, func(t *testing.T) {
   119  			out, err := json.Marshal(tc.input)
   120  			require.NoError(t, err)
   121  
   122  			w := httptest.NewRecorder()
   123  			req, _ := http.NewRequest("PATCH", cluster.Path, bytes.NewBuffer(out))
   124  			r.ServeHTTP(w, req)
   125  
   126  			assert.Equal(t, tc.expectedStatus, w.Code)
   127  
   128  			require.NoError(t, json.Unmarshal(w.Body.Bytes(), tc.response))
   129  			assert.Equal(t, tc.expectedResponse, tc.response)
   130  		})
   131  	}
   132  }
   133  
   134  func initializeClusterRouter(ctx context.Context) (*gin.Engine, error) {
   135  	ei := &info.EdgeInfo{
   136  		BannerName:       "example",
   137  		ProjectID:        "example",
   138  		Store:            testClusterName,
   139  		Fleet:            "example",
   140  		ClusterType:      "example",
   141  		Location:         "example",
   142  		ForemanProjectID: "example",
   143  		BannerEdgeID:     "example",
   144  		ClusterEdgeID:    "example",
   145  		EdgeAPIEndpoint:  "example",
   146  	}
   147  	cm := ei.ToConfigMap()
   148  
   149  	cli := getFakeKubeClient(cm)
   150  
   151  	cfg := &config.Config{
   152  		Fs:              afero.NewMemMapFs(),
   153  		KubeRetryClient: retryclient.New(cli, cli, retryclient.Config{}),
   154  		Cache:           &informertest.FakeInformers{},
   155  	}
   156  
   157  	wm := websocket.NewManager()
   158  
   159  	c, err := cluster.New(ctx, cfg, wm)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	r := gin.Default()
   165  	c.RegisterEndpoints(r)
   166  
   167  	return r, nil
   168  }
   169  
   170  func getFakeKubeClient(initObjs ...client.Object) client.Client {
   171  	return fake.NewClientBuilder().WithScheme(createScheme()).WithObjects(initObjs...).Build()
   172  }
   173  
   174  func createScheme() *kruntime.Scheme {
   175  	scheme := kruntime.NewScheme()
   176  	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
   177  	return scheme
   178  }
   179  

View as plain text