package instances_test import ( "context" "encoding/json" "net/http" "net/http/httptest" "os" "strings" "testing" "github.com/gin-gonic/gin" "github.com/go-logr/logr/testr" "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache/informertest" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "edge-infra.dev/pkg/sds/interlock/internal/config" "edge-infra.dev/pkg/sds/interlock/topic/instances" "edge-infra.dev/pkg/sds/interlock/websocket" "edge-infra.dev/pkg/sds/lib/k8s/retryclient" ) func init() { gin.SetMode(gin.TestMode) // required to prevent errors around getting InCluster configuration for k8s // client os.Setenv("KUBERNETES_SERVICE_HOST", "1") os.Setenv("KUBERNETES_SERVICE_PORT", "1") } func SetupTestCtx(t *testing.T) context.Context { logOptions := testr.Options{ LogTimestamp: true, Verbosity: -1, } ctx := ctrl.LoggerInto(context.Background(), testr.NewWithOptions(t, logOptions)) return ctx } // CreateScheme creates a new scheme, adds all types of the automatically generated // clientset, and returns it. func CreateScheme() *kruntime.Scheme { scheme := kruntime.NewScheme() utilruntime.Must(clientgoscheme.AddToScheme(scheme)) return scheme } // GetFakeKubeClient returns a fake client initialised with a slice of // Kubernets objects. To be used for testing purposes. func GetFakeKubeClient(initObjs ...client.Object) client.Client { return fake.NewClientBuilder().WithScheme(CreateScheme()).WithObjects(initObjs...).Build() } // GetTestHostNode returns a Node object initialised with name and uid fields // To be used for testing purposes. func GetTestPod(name, nodeName, ip string) *corev1.Pod { return &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: "interlock", }, Spec: corev1.PodSpec{ NodeName: nodeName, }, Status: corev1.PodStatus{ PodIP: ip, }, } } // setupTestInstancesTopic returns an initialised instances topic func setupTestInstancesTopic(t *testing.T) (*instances.Instances, error) { pod1 := GetTestPod("interlock-11111", "node1", "1.1.1.1") pod2 := GetTestPod("interlock-22222", "node2", "2.2.2.2") cli := GetFakeKubeClient(pod1, pod2) cfg := &config.Config{ Fs: afero.NewMemMapFs(), KubeRetryClient: retryclient.New(cli, cli, retryclient.Config{}), Cache: &informertest.FakeInformers{}, } wm := websocket.NewManager() return instances.New(SetupTestCtx(t), cfg, wm) } func TestGetState(t *testing.T) { h, err := setupTestInstancesTopic(t) require.NoError(t, err) r := gin.Default() h.RegisterEndpoints(r) w := httptest.NewRecorder() req, err := http.NewRequest(http.MethodGet, instances.Path, nil) require.NoError(t, err) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) actual := &instances.State{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), actual)) expected := &instances.State{ Instances: map[string]instances.Instance{ "node1": {URL: "http://1.1.1.1:80"}, "node2": {URL: "http://2.2.2.2:80"}, }, } assert.Equal(t, expected, actual) } func TestGetURL(t *testing.T) { h, err := setupTestInstancesTopic(t) require.NoError(t, err) r := gin.Default() h.RegisterEndpoints(r) path := strings.Join([]string{instances.Path, "node1"}, "/") w := httptest.NewRecorder() req, err := http.NewRequest(http.MethodGet, path, nil) require.NoError(t, err) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) actual := &instances.Instance{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), actual)) expected := &instances.Instance{ URL: "http://1.1.1.1:80", } assert.Equal(t, expected, actual) }