package services import ( "context" "database/sql" "encoding/json" "errors" "fmt" "testing" "time" "github.com/DATA-DOG/go-sqlmock" kustomizeApi "github.com/fluxcd/kustomize-controller/api/v1" sourceApi "github.com/fluxcd/source-controller/api/v1" "github.com/lib/pq" "github.com/stretchr/testify/assert" "edge-infra.dev/pkg/edge/api/graph/model" "edge-infra.dev/pkg/edge/api/services/artifacts" sqlquery "edge-infra.dev/pkg/edge/api/sql" "edge-infra.dev/pkg/edge/api/status" "edge-infra.dev/pkg/edge/constants" "edge-infra.dev/pkg/edge/constants/api/fleet" "edge-infra.dev/pkg/edge/ctlfish/monitor" whv1 "edge-infra.dev/pkg/f8n/warehouse/k8s/apis/v1alpha1" "edge-infra.dev/pkg/lib/runtime/version" ) var ( testStoreClusterEdgeID = "test-store-cluster-edge-id" testNetworkID = "dfsdgjskgsad" testNetworkID2 = "omeromgserga" testIP = "8.8.8.8" testIP2 = "0.0.0.0" testFamily = "inet" testDNSServiceType = "dns" testNTPServiceType = "ntp" testPriority = 100 testPriority2 = 99 testNetworkIDmap = map[string]string{testNetworkID: testDNSServiceType, testNetworkID2: testNTPServiceType} ) const ( supportVersionResource = `["{\"metadata\":{\"name\":\"test\",\"labels\":{\"feature.node.kubernetes.io/ien-version\":\"v1.15.0\"}}}","test-support-status-cluster"]` unsupportedVersionResource = `["{\"metadata\":{\"name\":\"test\",\"labels\":{\"feature.node.kubernetes.io/ien-version\":\"v1.2.0\"}}}","test-unsupported-status-cluster"]` noVersionResource = `["{\"metadata\":{\"name\":\"test\",\"labels\":{\"feature.node.kubernetes.io/ien-version\":\"\"}}}","test-no-terminal-version-status-cluster"]` ) func TestDeleteClusterSQLEntry(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() testStoreClusterEdgeID := "mv8wx3jwjgtg" mock.ExpectExec(sqlquery.ClusterDeleteQuery).WithArgs(testStoreClusterEdgeID). WillReturnResult(sqlmock.NewResult(1, 1)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) if err := service.DeleteStoreEntry(context.Background(), testStoreClusterEdgeID); err != nil { t.Errorf("error was not expected while deleting cluster: %s", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) } } func TestGetClusterFromSQL(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() mock.ExpectQuery(sqlquery.GetClusterByEdgeID). WithArgs("uuid-1"). WillReturnRows(mock.NewRows([]string{"cluster_edge_id", "cluster_name", "project_id", "registered", "active", "banner_edge_id", "bsl_site_id", "fleet_version"}). AddRow("uuid-1", "test-store", "test_project_id", true, true, "banner_edge_id", "bsl_site_id", "latest")) mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster). WithArgs("uuid-1"). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "test-label-key", "color", true, true, nil, true, "description", "label_type")) mock.ExpectQuery(sqlquery.GetArtifactVersionsAndCompatibility).WithArgs(fleet.Store, version.New().SemVer). WillReturnRows(mock.NewRows([]string{"artifact_name", "artifact_version", "nth_index", "compatible_artifact_name", "compatible_artifact_version"}). AddRow(fleet.Store, version.New().SemVer, 2, compatibleArtifactVersionA.Name, compatibleArtifactVersionA.Version). AddRow(fleet.Store, version.New().SemVer, 2, compatibleArtifactVersionB.Name, compatibleArtifactVersionB.Version)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) c, err := service.GetCluster(context.Background(), "uuid-1") assert.Nil(t, err) assert.Equal(t, "test-store", c.Name) assert.Equal(t, "test-label-key", c.Labels[0].Key) } func TestGetClustersFromSQL(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() mock.ExpectQuery(sqlquery.GetClustersWithLabelQuery). WithArgs(projectID, pq.Array([]string{"test-label-key"})). WillReturnRows(mock.NewRows([]string{"uuid-1", "cluster_name", "project_id", "registered", "active", "banner_edge_id", "bsl_site_id", "fleet_version"}). AddRow("uuid-2", "test-store", "test_project_id", true, true, "banner_edge_id", "bsl_site_id", "latest")) mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster). WithArgs("uuid-2"). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "test-label-key", "color", true, true, nil, true, "description", "label_type")) mock.ExpectQuery(sqlquery.GetClustersQuery). WithArgs(projectID). WillReturnRows(mock.NewRows([]string{"uuid", "cluster_name", "project_id", "registered", "active", "banner_edge_id", "bsl_site_id", "fleet_version"}). AddRow("uuid-1", "test-store-1", "test_project_id", true, true, "banner_edge_id", "bsl_site_id", "latest"). AddRow("uuid-2", "test-store-2", "test_project_id", true, true, "banner_edge_id", "bsl_site_id", "latest")) mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster). WithArgs("uuid-1"). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "test-label-key-1", "color", true, true, "banner_edge_id", true, "description", "label_type"). AddRow("label_edge_id", "test-label-key-2", "color", true, true, "banner_edge_id", true, "description", "label_type")) mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster). WithArgs("uuid-2"). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "test-label-key-2", "color", true, true, "banner_edge_id", true, "description", "label_type")) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) clusters, err := service.GetClusters(context.Background(), projectID, []string{"test-label-key"}) assert.Nil(t, err) assert.Equal(t, 1, len(clusters)) assert.Equal(t, "test-store", clusters[0].Name) assert.Equal(t, "test-label-key", clusters[0].Labels[0].Key) clusters, err = service.GetClusters(context.Background(), projectID, nil) assert.Nil(t, err) assert.Equal(t, 2, len(clusters)) assert.Equal(t, "test-store-1", clusters[0].Name) assert.Equal(t, 2, len(clusters[0].Labels)) assert.Equal(t, "test-label-key-1", clusters[0].Labels[0].Key) assert.Equal(t, "test-label-key-2", clusters[0].Labels[1].Key) assert.Equal(t, "test-store-2", clusters[1].Name) assert.Equal(t, 1, len(clusters[1].Labels)) assert.Equal(t, "test-label-key-2", clusters[1].Labels[0].Key) } func TestGetClustersFromSQLEmptyResponse(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() mock.ExpectQuery(sqlquery.GetClustersWithLabelQuery). WithArgs(projectID, pq.Array([]string{"test-label-key"})). WillReturnRows(mock.NewRows([]string{"uuid", "cluster_name", "project_id", "registered", "active", "banner_edge_id", "bsl_site_id", "labels"})) mock.ExpectQuery(sqlquery.GetClustersQuery). WithArgs(projectID). WillReturnRows(mock.NewRows([]string{"uuid", "cluster_name", "project_id", "registered", "active", "banner_edge_id", "bsl_site_id", "labels"})) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) clusters, err := service.GetClusters(context.Background(), projectID, []string{"test-label-key"}) assert.Nil(t, err) assert.Equal(t, []*model.Cluster{}, clusters) clusters, err = service.GetClusters(context.Background(), projectID, nil) assert.Nil(t, err) assert.Equal(t, []*model.Cluster{}, clusters) } func TestToBucketStatusInformation(t *testing.T) { testSyncInfo := monitor.SyncInfo{ LastUpdated: "test-last-updated", Revision: "test-revision", StatusMessage: "test-status-message", Error: false, Suspended: true, } bucketInfo := map[string]monitor.BucketInfo{ "test-bucket-1": { Excludes: "test-excludes-1", BucketName: "test-bucket-name-1", FluxStatus: testSyncInfo, }, } buckets := toBucketStatusInformation(bucketInfo) assert.Equal(t, 1, len(buckets)) assert.Equal(t, "test-excludes-1", buckets[0].Excludes) assert.Equal(t, "test-bucket-name-1", buckets[0].BucketName) assert.Equal(t, "test-bucket-1", buckets[0].FluxStatus.Name) } func TestToKustomizationStatusInformation(t *testing.T) { testSyncInfo := monitor.SyncInfo{ LastUpdated: "test-last-updated", Revision: "test-revision", StatusMessage: "test-status-message", Error: false, Suspended: true, } kustomizationInfo := map[string]monitor.KustomizationInfo{ "test-kustomization-1": {Path: "test-path-1", Source: "test-source-1", FluxStatus: testSyncInfo}, "test-kustomization-2": {Path: "test-path-2", Source: "test-source-2", FluxStatus: testSyncInfo}, } kustomizations := toKustomizationStatusInformation(kustomizationInfo) assert.Equal(t, 2, len(kustomizations)) assert.Contains(t, "test-path-1, test-path-2", kustomizations[0].Path) assert.Contains(t, "test-source-1, test-source-2", kustomizations[0].Source) assert.Contains(t, "test-path-1, test-path-2", kustomizations[1].Path) assert.Contains(t, "test-source-1, test-source-2", kustomizations[1].Source) assert.Contains(t, "test-kustomization-1 test-kustomization-2", kustomizations[0].FluxStatus.Name) assert.Contains(t, "test-kustomization-1 test-kustomization-2", kustomizations[1].FluxStatus.Name) } func TestGetFluxStatusInfo(t *testing.T) { testSyncInfo := monitor.SyncInfo{ LastUpdated: "test-last-updated", Revision: "test-revision", StatusMessage: "test-status-message", Error: false, Suspended: true, } fluxStatus := getFluxStatusInfo("test-flux-name", &testSyncInfo) assert.Equal(t, "test-flux-name", fluxStatus.Name) assert.Equal(t, "test-last-updated", fluxStatus.LastUpdated) assert.Equal(t, "test-revision", fluxStatus.Revision) assert.Equal(t, "test-status-message", fluxStatus.StatusMessage) assert.Equal(t, false, fluxStatus.Error) assert.Equal(t, true, fluxStatus.Suspended) } func TestCreateClusterNetworkServiceSQLEntry(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() newClusterNetworkService := model.CreateNetworkServiceInfo{ ServiceType: testDNSServiceType, IP: testIP2, Family: testFamily, Priority: &testPriority, } newClusterNetworkServices := []*model.CreateNetworkServiceInfo{&newClusterNetworkService} mock.ExpectQuery(sqlquery.GetClusterNetworkServices).WithArgs(testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID, testIP, testFamily, testDNSServiceType, testPriority)) mock.ExpectQuery(sqlquery.CreateClusterNetworkServices).WithArgs(testStoreClusterEdgeID, testIP2, testFamily, testDNSServiceType, testPriority).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID2, testIP2, testFamily, testDNSServiceType, testPriority)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) clusterNetworkServices, err := service.CreateClusterNetworkServices(context.Background(), testStoreClusterEdgeID, newClusterNetworkServices) if err != nil { t.Errorf("error was not expected while creating cluster network services: %s", err) } expected := model.ClusterNetworkServiceInfo{ NetworkServiceID: testNetworkID2, ServiceType: testDNSServiceType, IP: testIP2, Family: testFamily, Priority: &testPriority, } returnedEntry := *clusterNetworkServices[0] assert.Equal(t, returnedEntry, expected) } func TestGetClusterNetworkServiceSQLEntry(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() cluster := model.Cluster{ ClusterEdgeID: testStoreClusterEdgeID, } mock.ExpectQuery(sqlquery.GetClusterNetworkServices).WithArgs(testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID, testIP, testFamily, testDNSServiceType, testPriority)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) clusterNetworkServices, err := service.GetClusterNetworkServices(context.Background(), cluster.ClusterEdgeID) if err != nil { t.Errorf("error was not expected while getting cluster network services: %s", err) } expected := model.ClusterNetworkServiceInfo{ NetworkServiceID: testNetworkID, ServiceType: testDNSServiceType, IP: testIP, Family: testFamily, Priority: &testPriority, } returnedEntry := *clusterNetworkServices[0] assert.Equal(t, returnedEntry, expected) } func TestDeleteClusterNetworkServiceSQLEntry(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() mock.ExpectQuery(sqlquery.GetClusterNetworkServiceByNetworkID).WithArgs(testStoreClusterEdgeID, testNetworkID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID, testIP, testFamily, testDNSServiceType, testPriority)) mock.ExpectExec(sqlquery.DeleteClusterNetworkService).WithArgs(testStoreClusterEdgeID, testNetworkID). WillReturnResult(sqlmock.NewResult(1, 1)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) deleted, err := service.DeleteClusterNetworkService(context.Background(), testStoreClusterEdgeID, testNetworkID) if err != nil { t.Errorf("error was not expected while deleting cluster network services: %s", err) } assert.True(t, deleted) } func TestUpdateClusterNetworkServiceSQLEntry(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() newClusterNetworkService := model.UpdateNetworkServiceInfo{ NetworkServiceID: testNetworkID, IP: testIP, Family: testFamily, Priority: &testPriority2, } newClusterNetworkServices := []*model.UpdateNetworkServiceInfo{&newClusterNetworkService} mock.ExpectQuery(sqlquery.GetClusterNetworkServices).WithArgs(testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID, testIP, testFamily, testDNSServiceType, testPriority)) mock.ExpectQuery(sqlquery.UpdateClusterNetworkServices).WithArgs(testIP, testFamily, testPriority2, testNetworkID, testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID, testIP, testFamily, testDNSServiceType, testPriority2)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) clusterNetworkServices, err := service.UpdateClusterNetworkServices(context.Background(), testStoreClusterEdgeID, newClusterNetworkServices, testNetworkIDmap) if err != nil { t.Errorf("error was not expected while updating cluster network services: %s", err) } expected := model.ClusterNetworkServiceInfo{ NetworkServiceID: testNetworkID, ServiceType: testDNSServiceType, IP: testIP, Family: testFamily, Priority: &testPriority2, } returnedEntry := *clusterNetworkServices[0] assert.Equal(t, returnedEntry, expected) } func TestCreateClusterNetworkServiceSQLEntryInvalidNTP(t *testing.T) { db, _, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() newClusterNetworkService := model.CreateNetworkServiceInfo{ ServiceType: "ntp", IP: "domain!.test", Family: testFamily, Priority: &testPriority, } newClusterNetworkServices := []*model.CreateNetworkServiceInfo{&newClusterNetworkService} service := NewStoreClusterService(nil, nil, db, nil, nil, nil) _, err = service.CreateClusterNetworkServices(context.Background(), testStoreClusterEdgeID, newClusterNetworkServices) assert.Error(t, err) } func TestUpdateClusterNetworkServiceSQLEntryInvalidNTP(t *testing.T) { db, _, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() newClusterNetworkService := model.UpdateNetworkServiceInfo{ NetworkServiceID: testNetworkID, IP: "8.8.8.", Family: testFamily, Priority: &testPriority, } newClusterNetworkServices := []*model.UpdateNetworkServiceInfo{&newClusterNetworkService} service := NewStoreClusterService(nil, nil, db, nil, nil, nil) _, err = service.UpdateClusterNetworkServices(context.Background(), testStoreClusterEdgeID, newClusterNetworkServices, testNetworkIDmap) assert.Error(t, err) } func TestUpdateClusterNetworkServiceSQLEntryValidNTP(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() newClusterNetworkService := model.UpdateNetworkServiceInfo{ NetworkServiceID: testNetworkID2, IP: "time.google.com", Family: testFamily, Priority: &testPriority, } newClusterNetworkServices := []*model.UpdateNetworkServiceInfo{&newClusterNetworkService} mock.ExpectQuery(sqlquery.GetClusterNetworkServices).WithArgs(testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID2, testIP, testFamily, testNTPServiceType, testPriority)) mock.ExpectQuery(sqlquery.UpdateClusterNetworkServices).WithArgs("time.google.com", testFamily, testPriority, testNetworkID2, testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID2, "time.google.com", testFamily, testNTPServiceType, testPriority)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) clusterNetworkServices, err := service.UpdateClusterNetworkServices(context.Background(), testStoreClusterEdgeID, newClusterNetworkServices, testNetworkIDmap) if err != nil { t.Errorf("error was not expected while updating cluster network services: %s", err) } expected := model.ClusterNetworkServiceInfo{ NetworkServiceID: testNetworkID2, ServiceType: "ntp", IP: "time.google.com", Family: testFamily, Priority: &testPriority, } returnedEntry := *clusterNetworkServices[0] assert.Equal(t, returnedEntry, expected) } func TestCreateClusterNetworkServiceSQLEntryNoPrioritySet(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() newClusterNetworkService := model.CreateNetworkServiceInfo{ ServiceType: testDNSServiceType, IP: testIP2, Family: testFamily, } newClusterNetworkServices := []*model.CreateNetworkServiceInfo{&newClusterNetworkService} mock.ExpectQuery(sqlquery.GetClusterNetworkServices).WithArgs(testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID, testIP, testFamily, testDNSServiceType, testPriority)) mock.ExpectQuery(sqlquery.CreateClusterNetworkServices).WithArgs(testStoreClusterEdgeID, testIP2, testFamily, testDNSServiceType, testPriority).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID2, testIP2, testFamily, testDNSServiceType, testPriority)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) clusterNetworkServices, err := service.CreateClusterNetworkServices(context.Background(), testStoreClusterEdgeID, newClusterNetworkServices) if err != nil { t.Errorf("error was not expected while creating cluster network services: %s", err) } expected := model.ClusterNetworkServiceInfo{ NetworkServiceID: testNetworkID2, ServiceType: testDNSServiceType, IP: testIP2, Family: testFamily, Priority: &testPriority, } returnedEntry := *clusterNetworkServices[0] assert.Equal(t, returnedEntry, expected) } func TestCreateClusterNetworkServiceSQLEntryPrioritySetToNegativeInt(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() negativePriority := -10 newClusterNetworkService := model.CreateNetworkServiceInfo{ ServiceType: testDNSServiceType, IP: testIP2, Family: testFamily, Priority: &negativePriority, } newClusterNetworkServices := []*model.CreateNetworkServiceInfo{&newClusterNetworkService} mock.ExpectQuery(sqlquery.GetClusterNetworkServices).WithArgs(testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID, testIP, testFamily, testDNSServiceType, testPriority)) mock.ExpectQuery(sqlquery.CreateClusterNetworkServices).WithArgs(testStoreClusterEdgeID, testIP2, testFamily, testDNSServiceType, testPriority).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID2, testIP2, testFamily, testDNSServiceType, testPriority)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) clusterNetworkServices, err := service.CreateClusterNetworkServices(context.Background(), testStoreClusterEdgeID, newClusterNetworkServices) if err != nil { t.Errorf("error was not expected while creating cluster network services: %s", err) } expected := model.ClusterNetworkServiceInfo{ NetworkServiceID: testNetworkID2, ServiceType: testDNSServiceType, IP: testIP2, Family: testFamily, Priority: &testPriority, } returnedEntry := *clusterNetworkServices[0] assert.Equal(t, returnedEntry, expected) } func TestCreateClusterNetworkServiceSQLEntryDuplicateIP(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() newClusterNetworkServiceWithDuplicateIP := &model.CreateNetworkServiceInfo{ ServiceType: testDNSServiceType, IP: testIP, Family: testFamily, Priority: &testPriority, } newClusterNetworkServices := []*model.CreateNetworkServiceInfo{newClusterNetworkServiceWithDuplicateIP} mock.ExpectQuery(sqlquery.GetClusterNetworkServices).WithArgs(testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID, testIP, testFamily, testDNSServiceType, testPriority)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) _, err = service.CreateClusterNetworkServices(context.Background(), testStoreClusterEdgeID, newClusterNetworkServices) assert.Error(t, err) } func TestUpdateClusterNetworkServiceSQLEntryDuplicateIP(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() clusterNetworkServiceWithDuplicateIP := &model.UpdateNetworkServiceInfo{ NetworkServiceID: testNetworkID, IP: testIP2, Family: testFamily, Priority: &testPriority, } newClusterNetworkServices := []*model.UpdateNetworkServiceInfo{clusterNetworkServiceWithDuplicateIP} mock.ExpectQuery(sqlquery.GetClusterNetworkServices).WithArgs(testStoreClusterEdgeID).WillReturnRows(mock.NewRows([]string{"network_service_id", "ip", "family", "service_type", "priority"}). AddRow(testNetworkID, testIP, testFamily, testNTPServiceType, testPriority). AddRow(testNetworkID2, testIP2, testFamily, testNTPServiceType, testPriority)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) testNetworkIDmapNTP := map[string]string{testNetworkID: testNTPServiceType, testNetworkID2: testNTPServiceType} _, err = service.UpdateClusterNetworkServices(context.Background(), testStoreClusterEdgeID, newClusterNetworkServices, testNetworkIDmapNTP) assert.Error(t, err) } func TestUpdateStoreSiteID(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() mock.ExpectExec(sqlquery.UpdateStoreSiteIDQuery).WithArgs("testSiteID", testStoreClusterEdgeID). WillReturnResult(sqlmock.NewResult(1, 1)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) err = service.UpdateStoreSiteID(context.Background(), testStoreClusterEdgeID, "testSiteID") if err != nil { t.Errorf("error was not expected while updating siteID: %s", err) } assert.NoError(t, err) } func TestGetEventsForCluster(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() shipmentMessage := "Applied 11 pallets: [...]" kustoMessage := "Reconciliation finished in 4s, next run in 1m0s" createdAt := time.Now().Format(time.RFC3339) annotations := map[string]string{ "pallet.edge.ncr.com/created": createdAt, "pallet.edge.ncr.com/name": "fluxcd-syncing-config", "pallet.edge.ncr.com/version": "0.18.0-rc.0123456789+commit.a01234b", } jsonData, err := json.Marshal(annotations) assert.NoError(t, err) mock.ExpectQuery(sqlquery.GetClusterEvents). WithArgs("uuid-1"). WillReturnRows(mock.NewRows([]string{"event_edge_id", "event_name", "involved_kind", "involved_namespace", "involved_name", "reason", "message", "status", "source_component", "annotations", "terminal_id", "cluster_edge_id", "created_at"}). AddRow("event_edge_id", "test-event-name", "Shipment", "", "test-shipment-name", "Succeeded", shipmentMessage, "Normal", "lumperctl", "", "terminal_id", "uuid-1", createdAt). AddRow("event_edge_id2", "test-event-name2", "Kustomization", "flux-config", "test-kusto-name", "ReconciliationSucceeded", kustoMessage, "Normal", "kustomize-controller", string(jsonData), "terminal_id", "uuid-1", createdAt)) service := NewStoreClusterService(nil, nil, db, nil, nil, nil) events, err := service.GetEventsForCluster(context.Background(), "uuid-1") assert.NoError(t, err) assert.NotNil(t, events) assert.Equal(t, 2, len(events)) for _, event := range events { if event.EventEdgeID == "event_edge_id2" { assert.Equal(t, "test-event-name2", event.Name) assert.Equal(t, "Kustomization", event.InvolvedObject.Kind) assert.Equal(t, "flux-config", *event.InvolvedObject.Namespace) assert.Equal(t, "test-kusto-name", event.InvolvedObject.Name) assert.Equal(t, "ReconciliationSucceeded", event.Reason) assert.Equal(t, kustoMessage, event.Message) assert.Equal(t, "kustomize-controller", event.Source) assert.Equal(t, string(jsonData), *event.Annotations) assert.Equal(t, "Normal", event.Status) assert.Equal(t, "terminal_id", *event.TerminalID) assert.Equal(t, "uuid-1", event.ClusterEdgeID) assert.Equal(t, createdAt, event.CreatedAt) } } } func TestGetInfraStatusForCluster(t *testing.T) { clusterEdgeID := "test-infra-status" db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() expectedCombinedStatus := &model.ClusterStatus{ Status: "HEALTHY", Message: "SYNCED", } service := NewStoreClusterService(nil, nil, db, nil, nil, nil) type test struct { testName string clusterEdgeID string expected *model.ClusterStatus mockSQLQuery func() *sqlmock.ExpectedQuery } tests := []test{{ testName: "ClusterInfraReadyStatus", clusterEdgeID: clusterEdgeID, expected: expectedCombinedStatus, mockSQLQuery: func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterInfraStatusByID). WithArgs(clusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"cluster_edge_id", "cluster_name", "infra_status", "infra_status_details"}). AddRow(clusterEdgeID, "test-cluster", "READY", "SYNCED")) }, }, { testName: "ClusterInfraStatusNotReady", clusterEdgeID: clusterEdgeID, expected: &model.ClusterStatus{ Status: "UNHEALTHY", Message: "ReconcileFailed", }, mockSQLQuery: func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterInfraStatusByID). WithArgs(clusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"cluster_edge_id", "cluster_name", "infra_status", "infra_status_details"}). AddRow(clusterEdgeID, "test-cluster", "ERROR", "ReconcileFailed")) }, }, { testName: "ClusterInfraStatusProvisioning", clusterEdgeID: clusterEdgeID, expected: &model.ClusterStatus{ Status: "PROVISIONING", Message: "", }, mockSQLQuery: func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterInfraStatusByID). WithArgs(clusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"cluster_edge_id", "cluster_name", "infra_status", "infra_status_details"}). AddRow(clusterEdgeID, "test-cluster", "PROVISIONING", "")) }, }, } for _, tc := range tests { tc.mockSQLQuery() t.Run(tc.testName, func(t *testing.T) { clusterInfraStatus, err := service.GetInfraStatus(context.Background(), clusterEdgeID) assert.NoError(t, err) assert.Equal(t, tc.expected.Status, clusterInfraStatus.Status) }) } } func TestReplicationStatus(t *testing.T) { clusterEdgeID := "test-replication-status" db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() service := NewStoreClusterService(nil, nil, db, nil, nil, nil) type test struct { testName string clusterEdgeID string expected []*model.ReplicationStatus mockSQLQuery func() *sqlmock.ExpectedQuery } tests := []test{{ testName: "ReplicationStatusFound", clusterEdgeID: clusterEdgeID, expected: []*model.ReplicationStatus{{Name: "test-db", Status: "True"}}, mockSQLQuery: func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetReplicationStatus). WithArgs(clusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"name", "value"}). AddRow("test-db", "True")) }, }, { testName: "ReplicationStatusEmpty", clusterEdgeID: clusterEdgeID, expected: []*model.ReplicationStatus{}, mockSQLQuery: func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetReplicationStatus). WithArgs(clusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"name", "value"})) }, }, { testName: "ReplicationStatusNotFound", clusterEdgeID: clusterEdgeID, expected: []*model.ReplicationStatus{{Name: "test-db", Status: "False"}}, mockSQLQuery: func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetReplicationStatus). WithArgs(clusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"name", "value"}). AddRow("test-db", "False")) }, }, } for _, tc := range tests { tc.mockSQLQuery() t.Run(tc.testName, func(t *testing.T) { replicationStatus, err := service.GetReplicationStatus(context.Background(), clusterEdgeID) assert.NoError(t, err) assert.Equal(t, tc.expected, replicationStatus) }) } } func TestGetCombinedClusterStatus(t *testing.T) { clusterEdgeID := "11fd4df6-db4c-422b-ac9d-2b43dae86d7e" db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() service := NewStoreClusterService(nil, nil, db, nil, nil, nil) testCases := []struct { testName string cluster *model.CombinedStatus expected *model.ClusterStatus mockSQLQueries []func() *sqlmock.ExpectedQuery err error }{ { testName: "missing-cluster", cluster: nil, err: ErrClusterMissing, expected: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{}, }, { testName: "cluster-not-active", cluster: &model.CombinedStatus{ ClusterEdgeID: clusterEdgeID, Active: false, }, err: nil, expected: &model.ClusterStatus{ Status: status.Registered, Message: status.RegisteredMessage, }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{}, }, { testName: "ready-status", cluster: &model.CombinedStatus{ ClusterEdgeID: clusterEdgeID, Active: true, }, err: nil, expected: &model.ClusterStatus{ Status: status.Ready, Message: status.ReadyMessage, }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatuses). WithArgs(clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"kind", "jsonpath", "value"}). AddRow("Shipment", `$.status.conditions[?(@.type == "Ready")].status`, "True"). AddRow("Kustomization", `$.status.conditions[?(@.type == "Ready")].status`, "True"). AddRow("Bucket", `$.status.conditions[?(@.type == "Ready")].status`, "True"), ) }, }, }, { testName: "error-status-one-error-resource", cluster: &model.CombinedStatus{ ClusterEdgeID: clusterEdgeID, Active: true, }, err: nil, expected: &model.ClusterStatus{ Status: status.Error, Message: "timeout waiting for: [UnpackedPallet/vncserver-76d26410 status: 'InProgress', UnpackedPallet/display-76d26410 status: 'InProgress']", }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatuses). WithArgs(clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"kind", "jsonpath", "value"}). AddRow("Shipment", `$.status.conditions[?(@.type == "Ready")].status`, "False"). AddRow("Kustomization", `$.status.conditions[?(@.type == "Ready")].status`, "True"). AddRow("Bucket", `$.status.conditions[?(@.type == "Ready")].status`, "True"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs("Shipment", clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("timeout waiting for: [UnpackedPallet/vncserver-76d26410 status: 'InProgress', UnpackedPallet/display-76d26410 status: 'InProgress']"), ) }, }, }, { testName: "error-status-two-not-ready-resources-one-not-reported", cluster: &model.CombinedStatus{ ClusterEdgeID: clusterEdgeID, Active: true, }, err: nil, expected: &model.ClusterStatus{ Status: status.Installing, Message: "timeout waiting for: [UnpackedPallet/vncserver-76d26410 status: 'InProgress', UnpackedPallet/display-76d26410 status: 'InProgress'] and Waiting for Kustomization status", }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatuses). WithArgs(clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"kind", "jsonpath", "value"}). AddRow("Shipment", `$.status.conditions[?(@.type == "Ready")].status`, "False"). AddRow("Kustomization", `$.status.conditions[?(@.type == "Ready")].status`, "False"). AddRow("Bucket", `$.status.conditions[?(@.type == "Ready")].status`, "True"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs("Shipment", clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("timeout waiting for: [UnpackedPallet/vncserver-76d26410 status: 'InProgress', UnpackedPallet/display-76d26410 status: 'InProgress']"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs("Kustomization", clusterEdgeID, false). WillReturnError(sql.ErrNoRows) }, }, }, { testName: "error-status-three-not-ready-resources-three-errors", cluster: &model.CombinedStatus{ ClusterEdgeID: clusterEdgeID, Active: true, }, err: nil, expected: &model.ClusterStatus{ Status: status.Error, Message: `timeout waiting for: [UnpackedPallet/vncserver-76d26410 status: 'InProgress', UnpackedPallet/display-76d26410 status: 'InProgress'], kustomization path not found: stat /tmp/kustomization-2906021254/e0da4d18-0786-440e-acc7-b37ea6198c09/shipments: no such file or directory, and failed to confirm existence of 'ret-edge-y95i8vtpeyfqmv99fx0i4' bucket: Get "https://storage.googleapis.com/storage/v1/b/ret-edge-y95i8vtpeyfqmv99fx0i4?alt=json&prettyPrint=false&projection=full": oauth2: cannot fetch token: Post "https://oauth2.googleapis.com/token": dial tcp: lookup oauth2.googleapis.com on 10.63.0.18:53: read udp 100.137.192.153:57882->10.66.0.10:53: i/o timeout`, }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatuses). WithArgs(clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"kind", "jsonpath", "value"}). AddRow("Shipment", `$.status.conditions[?(@.type == "Ready")].status`, "False"). AddRow("Kustomization", `$.status.conditions[?(@.type == "Ready")].status`, "False"). AddRow("Bucket", `$.status.conditions[?(@.type == "Ready")].status`, "False"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs("Shipment", clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow(`timeout waiting for: [UnpackedPallet/vncserver-76d26410 status: 'InProgress', UnpackedPallet/display-76d26410 status: 'InProgress']`), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs("Kustomization", clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow(`kustomization path not found: stat /tmp/kustomization-2906021254/e0da4d18-0786-440e-acc7-b37ea6198c09/shipments: no such file or directory`), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs("Bucket", clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow(`failed to confirm existence of 'ret-edge-y95i8vtpeyfqmv99fx0i4' bucket: Get "https://storage.googleapis.com/storage/v1/b/ret-edge-y95i8vtpeyfqmv99fx0i4?alt=json&prettyPrint=false&projection=full": oauth2: cannot fetch token: Post "https://oauth2.googleapis.com/token": dial tcp: lookup oauth2.googleapis.com on 10.63.0.18:53: read udp 100.137.192.153:57882->10.66.0.10:53: i/o timeout`), ) }, }, }, } for _, tc := range testCases { for _, mockSQLFn := range tc.mockSQLQueries { mockSQLFn() } t.Run(tc.testName, func(t *testing.T) { clusterStatus, err := service.GetCombinedClusterStatus(context.Background(), tc.cluster) if tc.err != nil { assert.True(t, errors.Is(err, tc.err)) } else { assert.NoError(t, err) assert.Equal(t, tc.expected.Status, clusterStatus.Status) assert.Equal(t, tc.expected.Message, clusterStatus.Message) } }) } } func TestGetComponentStatus(t *testing.T) { clusterEdgeID := "11fd4df6-db4c-422b-ac9d-2b43dae86d7e" db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() service := NewStoreClusterService(nil, nil, db, nil, nil, nil) testCases := []struct { title string clusterEdgeID string kind string active bool expected *model.ClusterStatus mockSQLQueries []func() *sqlmock.ExpectedQuery err error }{ { title: "Test Case 1 - InActive Cluster", clusterEdgeID: clusterEdgeID, kind: whv1.ShipmentGVK.Kind, active: false, expected: &model.ClusterStatus{ Status: status.NotAvailable, Message: status.NotAvailableStatusMessage, }, err: nil, }, { title: "Test Case 2 - Shipment Status, Active Cluster", clusterEdgeID: clusterEdgeID, kind: whv1.ShipmentGVK.Kind, active: true, expected: &model.ClusterStatus{ Status: status.Ready, Message: fmt.Sprintf(status.KindReadyMessage, whv1.ShipmentGVK.Kind), }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(whv1.ShipmentGVK.Kind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("True"), ) }, }, }, { title: "Test Case 3 - Shipment Status, Active Cluster, Missing Cluster Status", clusterEdgeID: clusterEdgeID, kind: whv1.ShipmentGVK.Kind, active: true, expected: &model.ClusterStatus{ Status: status.NotAvailable, Message: status.NotAvailableStatusMessage, }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(whv1.ShipmentGVK.Kind, clusterEdgeID, false). WillReturnError(sql.ErrNoRows) }, }, }, { title: "Test Case 4 - Shipment Status, Active Cluster, Not Reported", clusterEdgeID: clusterEdgeID, kind: whv1.ShipmentGVK.Kind, active: true, expected: &model.ClusterStatus{ Status: status.Installing, Message: fmt.Sprintf("%s %s status", status.NotReportedFormat, whv1.ShipmentGVK.Kind), }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(whv1.ShipmentGVK.Kind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("False"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs(whv1.ShipmentGVK.Kind, clusterEdgeID, false). WillReturnError(sql.ErrNoRows) }, }, }, { title: "Test Case 5 - Shipment Status, Active Cluster, Error", clusterEdgeID: clusterEdgeID, kind: whv1.ShipmentGVK.Kind, active: true, expected: &model.ClusterStatus{ Status: status.Error, Message: "timeout waiting for: [UnpackedPallet/display-823f6029 status: 'InProgress', UnpackedPallet/vncserver-823f6029 status: 'InProgress']", }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(whv1.ShipmentGVK.Kind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("False"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs(whv1.ShipmentGVK.Kind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("timeout waiting for: [UnpackedPallet/display-823f6029 status: 'InProgress', UnpackedPallet/vncserver-823f6029 status: 'InProgress']"), ) }, }, }, { title: "Test Case 6 - Kustomization Status, Active Cluster, Ready", clusterEdgeID: clusterEdgeID, kind: kustomizeApi.KustomizationKind, active: true, expected: &model.ClusterStatus{ Status: status.Ready, Message: fmt.Sprintf(status.KindReadyMessage, kustomizeApi.KustomizationKind), }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(kustomizeApi.KustomizationKind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("True"), ) }, }, }, { title: "Test Case 7 - Kustomization Status, Active Cluster, Missing Cluster Status", clusterEdgeID: clusterEdgeID, kind: kustomizeApi.KustomizationKind, active: true, expected: &model.ClusterStatus{ Status: status.NotAvailable, Message: status.NotAvailableStatusMessage, }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(kustomizeApi.KustomizationKind, clusterEdgeID, false). WillReturnError(sql.ErrNoRows) }, }, }, { title: "Test Case 8 - Kustomization Status, Active Cluster, Not Reported", clusterEdgeID: clusterEdgeID, kind: kustomizeApi.KustomizationKind, active: true, expected: &model.ClusterStatus{ Status: status.Installing, Message: fmt.Sprintf("%s %s status", status.NotReportedFormat, kustomizeApi.KustomizationKind), }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(kustomizeApi.KustomizationKind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("False"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs(kustomizeApi.KustomizationKind, clusterEdgeID, false). WillReturnError(sql.ErrNoRows) }, }, }, { title: "Test Case 9 - Kustomization Status, Active Cluster, Error", clusterEdgeID: clusterEdgeID, kind: kustomizeApi.KustomizationKind, active: true, expected: &model.ClusterStatus{ Status: status.Error, Message: "path /tmp/abc123 not found", }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(kustomizeApi.KustomizationKind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("False"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs(kustomizeApi.KustomizationKind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("path /tmp/abc123 not found"), ) }, }, }, { title: "Test Case 10 - Bucket Status, Active Cluster, Ready", clusterEdgeID: clusterEdgeID, kind: sourceApi.BucketKind, active: true, expected: &model.ClusterStatus{ Status: status.Ready, Message: fmt.Sprintf(status.KindReadyMessage, sourceApi.BucketKind), }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(sourceApi.BucketKind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("True"), ) }, }, }, { title: "Test Case 11 - Bucket Status, Active Cluster, Missing Cluster Status", clusterEdgeID: clusterEdgeID, kind: sourceApi.BucketKind, active: true, expected: &model.ClusterStatus{ Status: status.NotAvailable, Message: status.NotAvailableStatusMessage, }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(sourceApi.BucketKind, clusterEdgeID, false). WillReturnError(sql.ErrNoRows) }, }, }, { title: "Test Case 12 - Bucket Status, Active Cluster, Not Reported", clusterEdgeID: clusterEdgeID, kind: sourceApi.BucketKind, active: true, expected: &model.ClusterStatus{ Status: status.Installing, Message: fmt.Sprintf("%s %s status", status.NotReportedFormat, sourceApi.BucketKind), }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(sourceApi.BucketKind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("False"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs(sourceApi.BucketKind, clusterEdgeID, false). WillReturnError(sql.ErrNoRows) }, }, }, { title: "Test Case 13 - Bucket Status, Active Cluster, Error", clusterEdgeID: clusterEdgeID, kind: sourceApi.BucketKind, active: true, expected: &model.ClusterStatus{ Status: status.Error, Message: "some cool source bucket error here", }, err: nil, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterStatus). WithArgs(sourceApi.BucketKind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("False"), ) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetClusterErrorStatusMessage). WithArgs(sourceApi.BucketKind, clusterEdgeID, false). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("some cool source bucket error here"), ) }, }, }, { title: "Test Case 14 - No Cluster Edge ID Error", clusterEdgeID: "", kind: sourceApi.BucketKind, active: true, expected: nil, err: ErrClusterMissing, }, { title: "Test Case 15 - No Kind Error", clusterEdgeID: clusterEdgeID, kind: "", active: true, expected: nil, err: ErrKindMissing, }, } for _, tc := range testCases { for _, mockSQLFn := range tc.mockSQLQueries { mockSQLFn() } t.Run(tc.title, func(t *testing.T) { res, err := service.GetComponentStatus(context.Background(), tc.clusterEdgeID, tc.kind, tc.active) if tc.err != nil { assert.True(t, errors.Is(tc.err, err)) } if tc.expected != nil { assert.Equal(t, tc.expected.Status, res.Status) assert.Equal(t, tc.expected.Message, res.Message) } else { assert.Equal(t, tc.expected, res) } }) } } func TestGetActiveVersion(t *testing.T) { clusterEdgeID := "test-active-version-cluster" db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() service := NewStoreClusterService(nil, nil, db, nil, nil, nil) activeVersion := "0.18.0-rc.1715271464+commit.a8854d8" type test struct { testName string clusterEdgeID string expected string mockSQLQuery func() *sqlmock.ExpectedQuery } tests := []test{ { testName: "GetActiveEdgeVersion", clusterEdgeID: clusterEdgeID, expected: activeVersion, mockSQLQuery: func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetActiveEdgeVersionFromWatchedField). WithArgs(clusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("\"0.18.0-rc.1715271464+commit.a8854d8\""). AddRow("\"0.18.0-rc.1715271464+commit.a8854d8\""). AddRow("True")) }, }, { testName: "GetActiveEdgeVersionEmpty", clusterEdgeID: clusterEdgeID, expected: "", mockSQLQuery: func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetActiveEdgeVersionFromWatchedField). WithArgs(clusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("\"\"").AddRow("\"\"").AddRow("True")) }, }, } for _, tc := range tests { tc.mockSQLQuery() t.Run(tc.testName, func(t *testing.T) { clusterInfraStatus, err := service.GetActiveEdgeVersion(context.Background(), clusterEdgeID) assert.NoError(t, err) assert.Equal(t, tc.expected, clusterInfraStatus) }) } } // nolint: dupl func TestGetSupportStatus(t *testing.T) { supportedClusterEdgeID := "test-support-status-cluster" unsupportedClusterEdgeID := "test-unsupported-status-cluster" noTerminalVersionClusterEdgeID := "test-no-terminal-version-status-cluster" latestVersion := version.New().SemVer db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() getKubeResource := func() GetKubeResourceFunc { return func(_ context.Context, _projectID string, cluster *model.Cluster, _ model.LoqRequest) ([]string, error) { assert.Equal(t, projectID, _projectID) assert.Nil(t, cluster) return []string{supportVersionResource, unsupportedVersionResource, noVersionResource}, nil } } bqClientMock := createMockBQClient(t, getKubeResource()) artifactService := artifacts.NewArtifactsService(db, nil) labelService := NewLabelService(artifactService, db) terminalService := NewTerminalServiceBQ(db, bqClientMock, labelService) compatibilityService := NewCompatibilityService(db) service := NewStoreClusterService(nil, bqClientMock, db, nil, terminalService, compatibilityService) testCases := []struct { testName string cluster *model.CombinedStatus expected *model.SupportStatus mockSQLQueries []func() *sqlmock.ExpectedQuery err error }{ { testName: "supported-gke-cluster", cluster: &model.CombinedStatus{ ClusterEdgeID: supportedClusterEdgeID, }, expected: &model.SupportStatus{ InfraSupportStatus: &model.InfraSupportStatus{ Status: status.Supported, Message: fmt.Sprintf("Edge Infra version %s supported", latestVersion), }, EdgeOsSupportStatus: &model.EdgeOsSupportStatus{}, }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster).WithArgs(supportedClusterEdgeID). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "test-label-key-1", "color", true, true, "banner_edge_id", true, "description", "label_type")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetArtifactVersionsAndCompatibilityForArtifactName).WithArgs(fleet.Store, latestVersion, constants.EdgeOSArtifact). WillReturnRows(mock.NewRows([]string{"artifact_name", "artifact_version", "nth_index", "compatible_artifact_name", "compatible_artifact_version"}). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.15"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.14"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.13")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetActiveEdgeVersionFromWatchedField). WithArgs(supportedClusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow(fmt.Sprintf("\"%s-rc.1715271464+commit.a8854d8\"", latestVersion)). AddRow(fmt.Sprintf("\"%s-rc.1715271464+commit.a8854d8\"", latestVersion)). AddRow("True")) }, }, err: nil, }, { testName: "unsupported-gke-cluster", cluster: &model.CombinedStatus{ ClusterEdgeID: unsupportedClusterEdgeID, }, expected: &model.SupportStatus{ InfraSupportStatus: &model.InfraSupportStatus{ Status: status.ClusterOutOfSupport, Message: fmt.Sprintf("Edge Infra version %s is out of support with current version %s", "0.17.0", latestVersion), }, EdgeOsSupportStatus: &model.EdgeOsSupportStatus{}, }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster).WithArgs(unsupportedClusterEdgeID). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "test-label-key-1", "color", true, true, "banner_edge_id", true, "description", "label_type")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetArtifactVersionsAndCompatibilityForArtifactName).WithArgs(fleet.Store, latestVersion, constants.EdgeOSArtifact). WillReturnRows(mock.NewRows([]string{"artifact_name", "artifact_version", "nth_index", "compatible_artifact_name", "compatible_artifact_version"}). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.15"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.14"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.13")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetActiveEdgeVersionFromWatchedField). WithArgs(unsupportedClusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow("\"0.17.0-rc.1715271464+commit.a8854d8\""). AddRow("\"0.17.0-rc.1715271464+commit.a8854d8\""). AddRow("True")) }, }, err: nil, }, { testName: "supported-dsds-cluster", cluster: &model.CombinedStatus{ ClusterEdgeID: supportedClusterEdgeID, }, expected: &model.SupportStatus{ InfraSupportStatus: &model.InfraSupportStatus{ Status: status.Supported, Message: fmt.Sprintf("Edge Infra version %s supported", latestVersion), }, EdgeOsSupportStatus: &model.EdgeOsSupportStatus{ Status: status.Supported, Message: fmt.Sprintf("EdgeOS version %s supported", "1.15.0"), }, }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster).WithArgs(supportedClusterEdgeID). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "dsds", "color", true, true, "banner_edge_id", true, "description", "label_type")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetArtifactVersionsAndCompatibilityForArtifactName).WithArgs(fleet.Store, latestVersion, constants.EdgeOSArtifact). WillReturnRows(mock.NewRows([]string{"artifact_name", "artifact_version", "nth_index", "compatible_artifact_name", "compatible_artifact_version"}). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.15"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.14"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.13")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetActiveEdgeVersionFromWatchedField). WithArgs(supportedClusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow(fmt.Sprintf("\"%s-rc.1715271464+commit.a8854d8\"", latestVersion)). AddRow(fmt.Sprintf("\"%s-rc.1715271464+commit.a8854d8\"", latestVersion)). AddRow("True")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalByClusterEdgeIDQuery).WithArgs(supportedClusterEdgeID). WillReturnRows(mock.NewRows(terminalColumns). AddRow("t001", "lane1", "controlplane", supportedClusterEdgeID, "test-cluster", "server", "none", "", "", "", false, "test")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalDiskByTerminalIDQuery).WithArgs(sqlmock.AnyArg()). WillReturnRows(mock.NewRows(terminalDiskColumns). AddRow("", "", false, false, "", false)) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalInterfaceByTerminalIDQuery).WithArgs(sqlmock.AnyArg()). WillReturnRows(mock.NewRows(terminalIfaceColumns). AddRow("", "", false, false, "", "", "")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalAddressByInterfaceIDQuery).WithArgs(sqlmock.AnyArg()). WillReturnRows(mock.NewRows(terminalAddrColumns). AddRow("", "", 1, "", "")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalLabels). WithArgs(sqlmock.AnyArg(), sql.NullString{}). WillReturnRows(mock.NewRows([]string{"terminal_id", "terminal_label_edge_id", "label_edge_id", "labelkey", "color", "visible", "editable", "banner", "unique", "description", "label_type"}). AddRow("t001", "388d1144-27c5-44e2-856a-e69a3d4f859f", testLabelEdgeID, label.Key, label.Color, label.Visible, label.Editable, label.BannerEdgeID, label.Unique, label.Description, label.Type)) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetProjectIDByClusterEdgeID). WithArgs(supportedClusterEdgeID). WillReturnRows(mock.NewRows([]string{"project_id"}). AddRow("test-org")) }, }, err: nil, }, { testName: "unsupported-dsds-cluster", cluster: &model.CombinedStatus{ ClusterEdgeID: unsupportedClusterEdgeID, }, expected: &model.SupportStatus{ InfraSupportStatus: &model.InfraSupportStatus{ Status: status.Supported, Message: fmt.Sprintf("Edge Infra version %s supported", latestVersion), }, EdgeOsSupportStatus: &model.EdgeOsSupportStatus{ Status: status.ClusterOutOfSupport, Message: fmt.Sprintf("One or more terminals on EdgeOS version %s is out of support", "1.2.0"), }, }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster).WithArgs(unsupportedClusterEdgeID). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "dsds", "color", true, true, "banner_edge_id", true, "description", "label_type")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetArtifactVersionsAndCompatibilityForArtifactName).WithArgs(fleet.Store, latestVersion, constants.EdgeOSArtifact). WillReturnRows(mock.NewRows([]string{"artifact_name", "artifact_version", "nth_index", "compatible_artifact_name", "compatible_artifact_version"}). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.15"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.14"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.13")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetActiveEdgeVersionFromWatchedField). WithArgs(unsupportedClusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow(fmt.Sprintf("\"%s-rc.1715271464+commit.a8854d8\"", latestVersion)). AddRow(fmt.Sprintf("\"%s-rc.1715271464+commit.a8854d8\"", latestVersion)). AddRow("True")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalByClusterEdgeIDQuery).WithArgs(unsupportedClusterEdgeID). WillReturnRows(mock.NewRows(terminalColumns). AddRow("t001", "lane1", "controlplane", unsupportedClusterEdgeID, "test-cluster", "server", "none", "", "", "", false, "test")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalDiskByTerminalIDQuery).WithArgs(sqlmock.AnyArg()). WillReturnRows(mock.NewRows(terminalDiskColumns). AddRow("", "", false, false, "", false)) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalInterfaceByTerminalIDQuery).WithArgs(sqlmock.AnyArg()). WillReturnRows(mock.NewRows(terminalIfaceColumns). AddRow("", "", false, false, "", "", "")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalAddressByInterfaceIDQuery).WithArgs(sqlmock.AnyArg()). WillReturnRows(mock.NewRows(terminalAddrColumns). AddRow("", "", 1, "", "")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalLabels). WithArgs(sqlmock.AnyArg(), sql.NullString{}). WillReturnRows(mock.NewRows([]string{"terminal_id", "terminal_label_edge_id", "label_edge_id", "labelkey", "color", "visible", "editable", "banner", "unique", "description", "label_type"}). AddRow("t001", "388d1144-27c5-44e2-856a-e69a3d4f859f", testLabelEdgeID, label.Key, label.Color, label.Visible, label.Editable, label.BannerEdgeID, label.Unique, label.Description, label.Type)) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetProjectIDByClusterEdgeID). WithArgs(unsupportedClusterEdgeID). WillReturnRows(mock.NewRows([]string{"project_id"}). AddRow("test-org")) }, }, err: nil, }, { testName: "no-active-version-cluster", cluster: &model.CombinedStatus{ ClusterEdgeID: unsupportedClusterEdgeID, }, expected: &model.SupportStatus{ InfraSupportStatus: &model.InfraSupportStatus{ Status: status.ErrGettingVersion, Message: status.NoActiveVersion, }, EdgeOsSupportStatus: &model.EdgeOsSupportStatus{}, }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster).WithArgs(unsupportedClusterEdgeID). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "dsds", "color", true, true, "banner_edge_id", true, "description", "label_type")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetArtifactVersionsAndCompatibilityForArtifactName).WithArgs(fleet.Store, latestVersion, constants.EdgeOSArtifact). WillReturnRows(mock.NewRows([]string{"artifact_name", "artifact_version", "nth_index", "compatible_artifact_name", "compatible_artifact_version"}). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.15"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.14"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.13")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetActiveEdgeVersionFromWatchedField). WithArgs(unsupportedClusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow(""). AddRow(""). AddRow("True")) }, }, err: nil, }, { testName: "no-terminal-version-cluster", cluster: &model.CombinedStatus{ ClusterEdgeID: noTerminalVersionClusterEdgeID, }, expected: &model.SupportStatus{ InfraSupportStatus: &model.InfraSupportStatus{ Status: status.Supported, Message: fmt.Sprintf("Edge Infra version %s supported", latestVersion), }, EdgeOsSupportStatus: &model.EdgeOsSupportStatus{ Status: status.ErrGettingVersion, Message: status.NoTerminalVersion, }, }, mockSQLQueries: []func() *sqlmock.ExpectedQuery{ func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.SelectEdgeLabelsForCluster).WithArgs(noTerminalVersionClusterEdgeID). WillReturnRows(mock.NewRows([]string{"label_edge_id", "label_key", "color", "visible", "editable", "banner_edge_id", "label_unique", "description", "label_type"}). AddRow("label_edge_id", "dsds", "color", true, true, "banner_edge_id", true, "description", "label_type")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetArtifactVersionsAndCompatibilityForArtifactName).WithArgs(fleet.Store, latestVersion, constants.EdgeOSArtifact). WillReturnRows(mock.NewRows([]string{"artifact_name", "artifact_version", "nth_index", "compatible_artifact_name", "compatible_artifact_version"}). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.15"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.14"). AddRow(fleet.Store, latestVersion, 2, constants.EdgeOSArtifact, "1.13")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetActiveEdgeVersionFromWatchedField). WithArgs(noTerminalVersionClusterEdgeID). WillReturnRows(sqlmock.NewRows([]string{"value"}). AddRow(fmt.Sprintf("\"%s-rc.1715271464+commit.a8854d8\"", latestVersion)). AddRow(fmt.Sprintf("\"%s-rc.1715271464+commit.a8854d8\"", latestVersion)). AddRow("True")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalByClusterEdgeIDQuery).WithArgs(noTerminalVersionClusterEdgeID). WillReturnRows(mock.NewRows(terminalColumns). AddRow("t001", "lane1", "controlplane", noTerminalVersionClusterEdgeID, "test-cluster", "server", "none", "", "", "", false, "test")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalDiskByTerminalIDQuery).WithArgs(sqlmock.AnyArg()). WillReturnRows(mock.NewRows(terminalDiskColumns). AddRow("", "", false, false, "", false)) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalInterfaceByTerminalIDQuery).WithArgs(sqlmock.AnyArg()). WillReturnRows(mock.NewRows(terminalIfaceColumns). AddRow("", "", false, false, "", "", "")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalAddressByInterfaceIDQuery).WithArgs(sqlmock.AnyArg()). WillReturnRows(mock.NewRows(terminalAddrColumns). AddRow("", "", 1, "", "")) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetTerminalLabels). WithArgs(sqlmock.AnyArg(), sql.NullString{}). WillReturnRows(mock.NewRows([]string{"terminal_id", "terminal_label_edge_id", "label_edge_id", "labelkey", "color", "visible", "editable", "banner", "unique", "description", "label_type"}). AddRow("t001", "388d1144-27c5-44e2-856a-e69a3d4f859f", testLabelEdgeID, label.Key, label.Color, label.Visible, label.Editable, label.BannerEdgeID, label.Unique, label.Description, label.Type)) }, func() *sqlmock.ExpectedQuery { return mock.ExpectQuery(sqlquery.GetProjectIDByClusterEdgeID). WithArgs(noTerminalVersionClusterEdgeID). WillReturnRows(mock.NewRows([]string{"project_id"}). AddRow("test-org")) }, }, err: nil, }, } for _, tc := range testCases { for _, mockSQLFn := range tc.mockSQLQueries { mockSQLFn() } t.Run(tc.testName, func(t *testing.T) { supportStatus, err := service.GetSupportStatus(context.Background(), tc.cluster) if tc.err != nil { assert.True(t, errors.Is(err, tc.err)) } else { assert.NoError(t, err) assert.Equal(t, tc.expected.InfraSupportStatus, supportStatus.InfraSupportStatus) assert.Equal(t, tc.expected.EdgeOsSupportStatus, supportStatus.EdgeOsSupportStatus) } }) } }