diff --git a/docker-compose.yml b/docker-compose.yml index fd146bfd98..65a9be573f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,10 +21,12 @@ services: platform: linux/x86_64 # innodb-file-per-table=OFF gives ~20% speedup for test runs. command: mysqld --datadir=/tmpfs --slow_query_log=1 --log_output=TABLE --log-queries-not-using-indexes --event-scheduler=ON --innodb-file-per-table=OFF - tmpfs: /tmpfs environment: *mysql-default-environment ports: - "3307:3306" + tmpfs: + - /var/lib/mysql:rw,noexec,nosuid + - /tmpfs mailhog: image: mailhog/mailhog:latest diff --git a/server/datastore/datastore.go b/server/datastore/datastore.go deleted file mode 100644 index 2f16f9af3c..0000000000 --- a/server/datastore/datastore.go +++ /dev/null @@ -1,106 +0,0 @@ -// Package datastore provides testcases for Datastore implementations. -package datastore - -import ( - "testing" - - "github.com/fleetdm/fleet/v4/server/fleet" -) - -// TestFunctions are the test functions that a Datastore implementation should -// run to verify proper implementation. -var TestFunctions = []func(*testing.T, fleet.Datastore){ - testOrgInfo, - testAdditionalQueries, - testEnrollSecrets, - testEnrollSecretsCaseSensitive, - testEnrollSecretRoundtrip, - testEnrollSecretUniqueness, - testCreateInvite, - testInviteByEmail, - testInviteByToken, - testListInvites, - testDeleteInvite, - testDeleteQuery, - testDeleteQueries, - testSaveQuery, - testListQuery, - testDeletePack, - testSavePack, - testEnrollHost, - testAuthenticateHost, - testAuthenticateHostCaseSensitive, - testLabels, - testSaveLabel, - testPasswordResetRequests, - testCreateUser, - testSaveUser, - testUserByID, - testListUsers, - testPasswordResetRequests, - testSearchHosts, - testSearchHostsLimit, - testSearchLabels, - testSearchLabelsLimit, - testListHostsInLabel, - testListUniqueHostsInLabels, - testSaveHosts, - testSaveHostPackStats, - testDeleteHost, - testListHosts, - testListHostsFilterAdditional, - testListHostsStatus, - testListHostsQuery, - testListPacksForHost, - testHostIDsByName, - testHostByIdentifier, - testAddHostsToTeam, - testListPacks, - testDistributedQueryCampaign, - testCleanupDistributedQueryCampaigns, - testBuiltInLabels, - testLoadPacksForQueries, - testScheduledQuery, - testDeleteScheduledQuery, - testNewScheduledQuery, - testListScheduledQueriesInPack, - testCascadingDeletionOfQueries, - testGetPackByName, - testGetQueryByName, - testGenerateHostStatusStatistics, - testMarkHostSeen, - testMarkHostsSeen, - testCleanupIncomingHosts, - testDuplicateNewQuery, - testChangeEmail, - testChangeLabelDetails, - testMigrationStatus, - testUnicode, - testCountHostsInTargets, - testHostStatus, - testHostIDsInTargets, - testApplyQueries, - testApplyPackSpecRoundtrip, - testApplyPackSpecMissingQueries, - testApplyPackSpecMissingName, - testGetPackSpec, - testApplyLabelSpecsRoundtrip, - testGetLabelSpec, - testLabelIDsByName, - testHostAdditional, - testCarveMetadata, - testCarveBlocks, - testCarveListCarves, - testCarveCleanupCarves, - testCarveUpdateCarve, - testTeamGetSetDelete, - testTeamUsers, - testTeamListTeams, - testTeamSearchTeams, - testUserTeams, - testUserCreateWithTeams, - testSaveHostSoftware, - testSaveUsers, - testNewActivity, - testEnsureGlobalPack, -} diff --git a/server/datastore/datastore_activities.go b/server/datastore/datastore_activities.go deleted file mode 100644 index 356922b260..0000000000 --- a/server/datastore/datastore_activities.go +++ /dev/null @@ -1,42 +0,0 @@ -package datastore - -import ( - "github.com/fleetdm/fleet/v4/server/fleet" - "github.com/fleetdm/fleet/v4/server/ptr" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "testing" -) - -func testNewActivity(t *testing.T, ds fleet.Datastore) { - u := &fleet.User{ - Password: []byte("asd"), - Name: "fullname", - Email: "email@asd.com", - GlobalRole: ptr.String(fleet.RoleObserver), - } - _, err := ds.NewUser(u) - require.Nil(t, err) - require.NoError(t, ds.NewActivity(u, "test1", &map[string]interface{}{"detail": 1, "sometext": "aaa"})) - require.NoError(t, ds.NewActivity(u, "test2", &map[string]interface{}{"detail": 2})) - - opt := fleet.ListOptions{ - Page: 0, - PerPage: 1, - } - activities, err := ds.ListActivities(opt) - require.NoError(t, err) - assert.Len(t, activities, 1) - assert.Equal(t, "fullname", activities[0].ActorFullName) - assert.Equal(t, "test1", activities[0].Type) - - opt = fleet.ListOptions{ - Page: 1, - PerPage: 1, - } - activities, err = ds.ListActivities(opt) - require.NoError(t, err) - assert.Len(t, activities, 1) - assert.Equal(t, "fullname", activities[0].ActorFullName) - assert.Equal(t, "test2", activities[0].Type) -} diff --git a/server/datastore/datastore_labels.go b/server/datastore/datastore_labels.go deleted file mode 100644 index ec1f7185d9..0000000000 --- a/server/datastore/datastore_labels.go +++ /dev/null @@ -1,492 +0,0 @@ -package datastore - -import ( - "fmt" - "sort" - "strconv" - "testing" - "time" - - "github.com/fleetdm/fleet/v4/server/fleet" - "github.com/fleetdm/fleet/v4/server/ptr" - "github.com/fleetdm/fleet/v4/server/test" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func testLabels(t *testing.T, db fleet.Datastore) { - test.AddAllHostsLabel(t, db) - hosts := []fleet.Host{} - var host *fleet.Host - var err error - for i := 0; i < 10; i++ { - host, err = db.EnrollHost(fmt.Sprint(i), fmt.Sprint(i), nil, 0) - require.Nil(t, err, "enrollment should succeed") - hosts = append(hosts, *host) - } - host.Platform = "darwin" - require.NoError(t, db.SaveHost(host)) - - baseTime := time.Now() - - // No labels to check - queries, err := db.LabelQueriesForHost(host, baseTime) - assert.Nil(t, err) - assert.Len(t, queries, 0) - - // Only 'All Hosts' label should be returned - labels, err := db.ListLabelsForHost(host.ID) - assert.Nil(t, err) - assert.Len(t, labels, 1) - - newLabels := []*fleet.LabelSpec{ - // Note these are intentionally out of order - &fleet.LabelSpec{ - Name: "label3", - Query: "query3", - Platform: "darwin", - }, - &fleet.LabelSpec{ - Name: "label1", - Query: "query1", - }, - &fleet.LabelSpec{ - Name: "label2", - Query: "query2", - Platform: "darwin", - }, - &fleet.LabelSpec{ - Name: "label4", - Query: "query4", - Platform: "darwin", - }, - } - err = db.ApplyLabelSpecs(newLabels) - require.Nil(t, err) - - expectQueries := map[string]string{ - "2": "query3", - "3": "query1", - "4": "query2", - "5": "query4", - } - - host.Platform = "darwin" - - // Now queries should be returned - queries, err = db.LabelQueriesForHost(host, baseTime) - assert.Nil(t, err) - assert.Equal(t, expectQueries, queries) - - // No labels should match with no results yet - labels, err = db.ListLabelsForHost(host.ID) - assert.Nil(t, err) - assert.Len(t, labels, 1) - - // Record a query execution - err = db.RecordLabelQueryExecutions(host, map[uint]bool{1: true, 2: false, 3: true, 4: false, 5: false}, baseTime) - assert.Nil(t, err) - - host, err = db.Host(host.ID) - require.NoError(t, err) - host.LabelUpdatedAt = baseTime - - // Now no queries should be returned - queries, err = db.LabelQueriesForHost(host, baseTime.Add(-1*time.Minute)) - assert.Nil(t, err) - assert.Len(t, queries, 0) - - // Ensure enough gap in created_at - time.Sleep(2 * time.Second) - - // A new label targeting another platform should not effect the labels for - // this host - err = db.ApplyLabelSpecs([]*fleet.LabelSpec{ - &fleet.LabelSpec{ - Name: "label5", - Platform: "not-matching", - Query: "query5", - }, - }) - require.NoError(t, err) - queries, err = db.LabelQueriesForHost(host, baseTime.Add(-1*time.Minute)) - assert.Nil(t, err) - assert.Len(t, queries, 0) - - // If a new label is added, all labels should be returned - err = db.ApplyLabelSpecs([]*fleet.LabelSpec{ - &fleet.LabelSpec{ - Name: "label6", - Platform: "", - Query: "query6", - }, - }) - require.NoError(t, err) - expectQueries["7"] = "query6" - queries, err = db.LabelQueriesForHost(host, baseTime.Add(-1*time.Minute)) - assert.Nil(t, err) - assert.Len(t, queries, 5) - - // After expiration, all queries should be returned - queries, err = db.LabelQueriesForHost(host, baseTime.Add((2 * time.Minute))) - assert.Nil(t, err) - assert.Equal(t, expectQueries, queries) - - // Now the two matching labels should be returned - labels, err = db.ListLabelsForHost(host.ID) - assert.Nil(t, err) - if assert.Len(t, labels, 2) { - labelNames := []string{labels[0].Name, labels[1].Name} - sort.Strings(labelNames) - assert.Equal(t, "All Hosts", labelNames[0]) - assert.Equal(t, "label1", labelNames[1]) - } - - // A host that hasn't executed any label queries should still be asked - // to execute those queries - hosts[0].Platform = "darwin" - queries, err = db.LabelQueriesForHost(&hosts[0], time.Now()) - assert.Nil(t, err) - assert.Len(t, queries, 5) - - // Only the 'All Hosts' label should apply for a host with no labels - // executed. - labels, err = db.ListLabelsForHost(hosts[0].ID) - assert.Nil(t, err) - assert.Len(t, labels, 1) -} - -func testSearchLabels(t *testing.T, db fleet.Datastore) { - specs := []*fleet.LabelSpec{ - &fleet.LabelSpec{ - ID: 1, - Name: "foo", - }, - &fleet.LabelSpec{ - ID: 2, - Name: "bar", - }, - &fleet.LabelSpec{ - ID: 3, - Name: "foo-bar", - }, - &fleet.LabelSpec{ - ID: 4, - Name: "All Hosts", - LabelType: fleet.LabelTypeBuiltIn, - }, - } - err := db.ApplyLabelSpecs(specs) - require.Nil(t, err) - - all, err := db.Label(specs[3].ID) - require.Nil(t, err) - l3, err := db.Label(specs[2].ID) - require.Nil(t, err) - - user := &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)} - filter := fleet.TeamFilter{User: user} - - // We once threw errors when the search query was empty. Verify that we - // don't error. - labels, err := db.SearchLabels(filter, "") - require.Nil(t, err) - assert.Contains(t, labels, all) - - labels, err = db.SearchLabels(filter, "foo") - require.Nil(t, err) - assert.Len(t, labels, 3) - assert.Contains(t, labels, all) - - labels, err = db.SearchLabels(filter, "foo", all.ID, l3.ID) - require.Nil(t, err) - assert.Len(t, labels, 1) - assert.Equal(t, "foo", labels[0].Name) - - labels, err = db.SearchLabels(filter, "xxx") - require.Nil(t, err) - assert.Len(t, labels, 1) - assert.Contains(t, labels, all) -} - -func testSearchLabelsLimit(t *testing.T, db fleet.Datastore) { - if db.Name() == "inmem" { - t.Skip("inmem is being deprecated, test skipped") - } - - all := &fleet.LabelSpec{ - Name: "All Hosts", - LabelType: fleet.LabelTypeBuiltIn, - } - err := db.ApplyLabelSpecs([]*fleet.LabelSpec{all}) - require.Nil(t, err) - - for i := 0; i < 15; i++ { - l := &fleet.LabelSpec{ - Name: fmt.Sprintf("foo%d", i), - } - err := db.ApplyLabelSpecs([]*fleet.LabelSpec{l}) - require.Nil(t, err) - } - - user := &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)} - filter := fleet.TeamFilter{User: user} - - labels, err := db.SearchLabels(filter, "foo") - require.Nil(t, err) - assert.Len(t, labels, 11) -} - -func testListHostsInLabel(t *testing.T, db fleet.Datastore) { - h1, err := db.NewHost(&fleet.Host{ - DetailUpdatedAt: time.Now(), - LabelUpdatedAt: time.Now(), - SeenTime: time.Now(), - OsqueryHostID: "1", - NodeKey: "1", - UUID: "1", - Hostname: "foo.local", - }) - require.Nil(t, err) - - h2, err := db.NewHost(&fleet.Host{ - DetailUpdatedAt: time.Now(), - LabelUpdatedAt: time.Now(), - SeenTime: time.Now(), - OsqueryHostID: "2", - NodeKey: "2", - UUID: "2", - Hostname: "bar.local", - }) - require.Nil(t, err) - - h3, err := db.NewHost(&fleet.Host{ - DetailUpdatedAt: time.Now(), - LabelUpdatedAt: time.Now(), - SeenTime: time.Now(), - OsqueryHostID: "3", - NodeKey: "3", - UUID: "3", - Hostname: "baz.local", - }) - require.Nil(t, err) - - l1 := &fleet.LabelSpec{ - ID: 1, - Name: "label foo", - Query: "query1", - } - err = db.ApplyLabelSpecs([]*fleet.LabelSpec{l1}) - require.Nil(t, err) - - filter := fleet.TeamFilter{User: test.UserAdmin} - - { - hosts, err := db.ListHostsInLabel(filter, l1.ID, fleet.HostListOptions{}) - require.Nil(t, err) - assert.Len(t, hosts, 0) - } - - for _, h := range []*fleet.Host{h1, h2, h3} { - err = db.RecordLabelQueryExecutions(h, map[uint]bool{l1.ID: true}, time.Now()) - assert.Nil(t, err) - } - - { - hosts, err := db.ListHostsInLabel(filter, l1.ID, fleet.HostListOptions{}) - require.Nil(t, err) - assert.Len(t, hosts, 3) - } -} - -func testBuiltInLabels(t *testing.T, db fleet.Datastore) { - require.Nil(t, db.MigrateData()) - - user := &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)} - filter := fleet.TeamFilter{User: user} - - hits, err := db.SearchLabels(filter, "macOS") - require.Nil(t, err) - // Should get Mac OS X and All Hosts - assert.Equal(t, 2, len(hits)) - assert.Equal(t, fleet.LabelTypeBuiltIn, hits[0].LabelType) - assert.Equal(t, fleet.LabelTypeBuiltIn, hits[1].LabelType) -} - -func testListUniqueHostsInLabels(t *testing.T, db fleet.Datastore) { - hosts := []*fleet.Host{} - for i := 0; i < 4; i++ { - h, err := db.NewHost(&fleet.Host{ - DetailUpdatedAt: time.Now(), - LabelUpdatedAt: time.Now(), - SeenTime: time.Now(), - OsqueryHostID: strconv.Itoa(i), - NodeKey: strconv.Itoa(i), - UUID: strconv.Itoa(i), - Hostname: fmt.Sprintf("host_%d", i), - }) - require.Nil(t, err) - require.NotNil(t, h) - hosts = append(hosts, h) - } - - l1 := fleet.LabelSpec{ - ID: 1, - Name: "label foo", - Query: "query1", - } - l2 := fleet.LabelSpec{ - ID: 2, - Name: "label bar", - Query: "query2", - } - err := db.ApplyLabelSpecs([]*fleet.LabelSpec{&l1, &l2}) - require.Nil(t, err) - - for i := 0; i < 3; i++ { - err = db.RecordLabelQueryExecutions(hosts[i], map[uint]bool{l1.ID: true}, time.Now()) - assert.Nil(t, err) - } - // host 2 executes twice - for i := 2; i < len(hosts); i++ { - err = db.RecordLabelQueryExecutions(hosts[i], map[uint]bool{l2.ID: true}, time.Now()) - assert.Nil(t, err) - } - - filter := fleet.TeamFilter{User: test.UserAdmin} - - uniqueHosts, err := db.ListUniqueHostsInLabels(filter, []uint{l1.ID, l2.ID}) - assert.Nil(t, err) - assert.Equal(t, len(hosts), len(uniqueHosts)) - - labels, err := db.ListLabels(filter, fleet.ListOptions{}) - require.Nil(t, err) - require.Len(t, labels, 2) - -} - -func testChangeLabelDetails(t *testing.T, db fleet.Datastore) { - if db.Name() == "inmem" { - t.Skip("inmem is being deprecated") - } - - label := fleet.LabelSpec{ - ID: 1, - Name: "my label", - Description: "a label", - Query: "select 1 from processes", - Platform: "darwin", - } - err := db.ApplyLabelSpecs([]*fleet.LabelSpec{&label}) - require.Nil(t, err) - - label.Description = "changed description" - err = db.ApplyLabelSpecs([]*fleet.LabelSpec{&label}) - require.Nil(t, err) - - saved, err := db.Label(label.ID) - require.Nil(t, err) - assert.Equal(t, label.Name, saved.Name) -} - -func setupLabelSpecsTest(t *testing.T, ds fleet.Datastore) []*fleet.LabelSpec { - for i := 0; i < 10; i++ { - _, err := ds.NewHost(&fleet.Host{ - DetailUpdatedAt: time.Now(), - LabelUpdatedAt: time.Now(), - SeenTime: time.Now(), - OsqueryHostID: strconv.Itoa(i), - NodeKey: strconv.Itoa(i), - UUID: strconv.Itoa(i), - Hostname: strconv.Itoa(i), - }) - require.Nil(t, err) - } - - expectedSpecs := []*fleet.LabelSpec{ - { - Name: "foo", - Query: "select * from foo", - Description: "foo description", - Platform: "darwin", - }, - { - Name: "bar", - Query: "select * from bar", - }, - { - Name: "bing", - Query: "select * from bing", - }, - { - Name: "All Hosts", - Query: "SELECT 1", - LabelType: fleet.LabelTypeBuiltIn, - LabelMembershipType: fleet.LabelMembershipTypeManual, - }, - { - Name: "Manual Label", - LabelMembershipType: fleet.LabelMembershipTypeManual, - Hosts: []string{ - "1", "2", "3", "4", - }, - }, - } - err := ds.ApplyLabelSpecs(expectedSpecs) - require.Nil(t, err) - - return expectedSpecs -} - -func testGetLabelSpec(t *testing.T, ds fleet.Datastore) { - expectedSpecs := setupLabelSpecsTest(t, ds) - - for _, s := range expectedSpecs { - spec, err := ds.GetLabelSpec(s.Name) - require.Nil(t, err) - assert.Equal(t, s, spec) - } -} - -func testApplyLabelSpecsRoundtrip(t *testing.T, ds fleet.Datastore) { - expectedSpecs := setupLabelSpecsTest(t, ds) - - specs, err := ds.GetLabelSpecs() - require.Nil(t, err) - test.ElementsMatchSkipTimestampsID(t, expectedSpecs, specs) - - // Should be idempotent - err = ds.ApplyLabelSpecs(expectedSpecs) - require.Nil(t, err) - specs, err = ds.GetLabelSpecs() - require.Nil(t, err) - test.ElementsMatchSkipTimestampsID(t, expectedSpecs, specs) -} - -func testLabelIDsByName(t *testing.T, ds fleet.Datastore) { - setupLabelSpecsTest(t, ds) - - labels, err := ds.LabelIDsByName([]string{"foo", "bar", "bing"}) - require.Nil(t, err) - sort.Slice(labels, func(i, j int) bool { return labels[i] < labels[j] }) - assert.Equal(t, []uint{1, 2, 3}, labels) -} - -func testSaveLabel(t *testing.T, db fleet.Datastore) { - label := &fleet.Label{ - Name: "my label", - Description: "a label", - Query: "select 1 from processes;", - Platform: "darwin", - } - label, err := db.NewLabel(label) - require.Nil(t, err) - label.Name = "changed name" - label.Description = "changed description" - _, err = db.SaveLabel(label) - require.Nil(t, err) - saved, err := db.Label(label.ID) - require.Nil(t, err) - assert.Equal(t, label.Name, saved.Name) - assert.Equal(t, label.Description, saved.Description) -} diff --git a/server/datastore/mysql/activities_test.go b/server/datastore/mysql/activities_test.go index 692582fc29..ccd2b388ad 100644 --- a/server/datastore/mysql/activities_test.go +++ b/server/datastore/mysql/activities_test.go @@ -1,11 +1,12 @@ package mysql import ( + "testing" + "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/ptr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func TestActivityUsernameChange(t *testing.T) { @@ -45,3 +46,39 @@ func TestActivityUsernameChange(t *testing.T) { assert.Len(t, activities, 2) assert.Equal(t, "fullname", activities[0].ActorFullName) } + +func TestNewActivity(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + + u := &fleet.User{ + Password: []byte("asd"), + Name: "fullname", + Email: "email@asd.com", + GlobalRole: ptr.String(fleet.RoleObserver), + } + _, err := ds.NewUser(u) + require.Nil(t, err) + require.NoError(t, ds.NewActivity(u, "test1", &map[string]interface{}{"detail": 1, "sometext": "aaa"})) + require.NoError(t, ds.NewActivity(u, "test2", &map[string]interface{}{"detail": 2})) + + opt := fleet.ListOptions{ + Page: 0, + PerPage: 1, + } + activities, err := ds.ListActivities(opt) + require.NoError(t, err) + assert.Len(t, activities, 1) + assert.Equal(t, "fullname", activities[0].ActorFullName) + assert.Equal(t, "test1", activities[0].Type) + + opt = fleet.ListOptions{ + Page: 1, + PerPage: 1, + } + activities, err = ds.ListActivities(opt) + require.NoError(t, err) + assert.Len(t, activities, 1) + assert.Equal(t, "fullname", activities[0].ActorFullName) + assert.Equal(t, "test2", activities[0].Type) +} diff --git a/server/datastore/datastore_app.go b/server/datastore/mysql/app_configs_test.go similarity index 91% rename from server/datastore/datastore_app.go rename to server/datastore/mysql/app_configs_test.go index 517a4cda35..07874dfd31 100644 --- a/server/datastore/datastore_app.go +++ b/server/datastore/mysql/app_configs_test.go @@ -1,17 +1,21 @@ -package datastore +package mysql import ( "encoding/json" - "github.com/fleetdm/fleet/v4/server/ptr" "sort" "testing" + "github.com/fleetdm/fleet/v4/server/ptr" + "github.com/fleetdm/fleet/v4/server/fleet" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func testOrgInfo(t *testing.T, ds fleet.Datastore) { +func TestOrgInfo(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + info := &fleet.AppConfig{ OrgName: "Test", OrgLogoURL: "localhost:8080/logo.png", @@ -79,7 +83,10 @@ func testOrgInfo(t *testing.T, ds fleet.Datastore) { assert.False(t, verify.SSOEnabled) } -func testAdditionalQueries(t *testing.T, ds fleet.Datastore) { +func TestAdditionalQueries(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + additional := json.RawMessage("not valid json") info := &fleet.AppConfig{ OrgName: "Test", @@ -100,7 +107,10 @@ func testAdditionalQueries(t *testing.T, ds fleet.Datastore) { assert.JSONEq(t, `{"foo":"bar"}`, string(*info.AdditionalQueries)) } -func testEnrollSecrets(t *testing.T, ds fleet.Datastore) { +func TestEnrollSecrets(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + team1, err := ds.NewTeam(&fleet.Team{Name: "team1"}) require.NoError(t, err) @@ -152,7 +162,10 @@ func testEnrollSecrets(t *testing.T, ds fleet.Datastore) { assert.Equal(t, (*uint)(nil), secret.TeamID) } -func testEnrollSecretsCaseSensitive(t *testing.T, ds fleet.Datastore) { +func TestEnrollSecretsCaseSensitive(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + err := ds.ApplyEnrollSecrets( nil, []*fleet.EnrollSecret{ @@ -167,7 +180,10 @@ func testEnrollSecretsCaseSensitive(t *testing.T, ds fleet.Datastore) { assert.Error(t, err, "enroll secret with different case should not verify") } -func testEnrollSecretRoundtrip(t *testing.T, ds fleet.Datastore) { +func TestEnrollSecretRoundtrip(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + team1, err := ds.NewTeam(&fleet.Team{Name: "team1"}) require.NoError(t, err) @@ -206,7 +222,10 @@ func testEnrollSecretRoundtrip(t *testing.T, ds fleet.Datastore) { } -func testEnrollSecretUniqueness(t *testing.T, ds fleet.Datastore) { +func TestEnrollSecretUniqueness(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + team1, err := ds.NewTeam(&fleet.Team{Name: "team1"}) require.NoError(t, err) diff --git a/server/datastore/datastore_campaigns.go b/server/datastore/mysql/campaigns_test.go similarity index 95% rename from server/datastore/datastore_campaigns.go rename to server/datastore/mysql/campaigns_test.go index 8185db5931..a1f310e2ab 100644 --- a/server/datastore/datastore_campaigns.go +++ b/server/datastore/mysql/campaigns_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "testing" @@ -19,7 +19,10 @@ func checkTargets(t *testing.T, ds fleet.Datastore, campaignID uint, expectedTar assert.ElementsMatch(t, expectedTargets.TeamIDs, targets.TeamIDs) } -func testDistributedQueryCampaign(t *testing.T, ds fleet.Datastore) { +func TestDistributedQueryCampaign(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + user := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) mockClock := clock.NewMockClock() @@ -70,7 +73,10 @@ func testDistributedQueryCampaign(t *testing.T, ds fleet.Datastore) { } -func testCleanupDistributedQueryCampaigns(t *testing.T, ds fleet.Datastore) { +func TestCleanupDistributedQueryCampaigns(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + user := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) mockClock := clock.NewMockClock() diff --git a/server/datastore/datastore_carves.go b/server/datastore/mysql/carves_test.go similarity index 93% rename from server/datastore/datastore_carves.go rename to server/datastore/mysql/carves_test.go index 5b836982df..d7237291eb 100644 --- a/server/datastore/datastore_carves.go +++ b/server/datastore/mysql/carves_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "crypto/rand" @@ -13,7 +13,10 @@ import ( var mockCreatedAt time.Time = time.Now().UTC().Truncate(time.Second) -func testCarveMetadata(t *testing.T, ds fleet.Datastore) { +func TestCarveMetadata(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + h := test.NewHost(t, ds, "foo.local", "192.168.1.10", "1", "1", time.Now()) expectedCarve := &fleet.CarveMetadata{ @@ -71,7 +74,10 @@ func testCarveMetadata(t *testing.T, ds fleet.Datastore) { assert.Equal(t, expectedCarve, carve) } -func testCarveBlocks(t *testing.T, ds fleet.Datastore) { +func TestCarveBlocks(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + h := test.NewHost(t, ds, "foo.local", "192.168.1.10", "1", "1", time.Now()) blockCount := int64(25) @@ -112,7 +118,10 @@ func testCarveBlocks(t *testing.T, ds fleet.Datastore) { } -func testCarveCleanupCarves(t *testing.T, ds fleet.Datastore) { +func TestCarveCleanupCarves(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + h := test.NewHost(t, ds, "foo.local", "192.168.1.10", "1", "1", time.Now()) blockCount := int64(25) @@ -164,7 +173,10 @@ func testCarveCleanupCarves(t *testing.T, ds fleet.Datastore) { assert.True(t, carve.Expired) } -func testCarveListCarves(t *testing.T, ds fleet.Datastore) { +func TestCarveListCarves(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + h := test.NewHost(t, ds, "foo.local", "192.168.1.10", "1", "1", time.Now()) expectedCarve := &fleet.CarveMetadata{ @@ -222,7 +234,10 @@ func testCarveListCarves(t *testing.T, ds fleet.Datastore) { assert.Len(t, carves, 2) } -func testCarveUpdateCarve(t *testing.T, ds fleet.Datastore) { +func TestCarveUpdateCarve(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + h := test.NewHost(t, ds, "foo.local", "192.168.1.10", "1", "1", time.Now()) actualCount := int64(10) diff --git a/server/datastore/mysql/datastore_test.go b/server/datastore/mysql/datastore_test.go deleted file mode 100644 index cd859d34b7..0000000000 --- a/server/datastore/mysql/datastore_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package mysql - -import ( - "testing" - - "github.com/fleetdm/fleet/v4/server/datastore" - _ "github.com/go-sql-driver/mysql" -) - -func TestMySQL(t *testing.T) { - RunTestsAgainstMySQL(t, datastore.TestFunctions) -} diff --git a/server/datastore/datastore_email_changes.go b/server/datastore/mysql/email_changes_test.go similarity index 93% rename from server/datastore/datastore_email_changes.go rename to server/datastore/mysql/email_changes_test.go index 7c13faff1f..65388e64e1 100644 --- a/server/datastore/datastore_email_changes.go +++ b/server/datastore/mysql/email_changes_test.go @@ -1,15 +1,19 @@ -package datastore +package mysql import ( - "github.com/fleetdm/fleet/v4/server/ptr" "testing" + "github.com/fleetdm/fleet/v4/server/ptr" + "github.com/fleetdm/fleet/v4/server/fleet" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func testChangeEmail(t *testing.T, ds fleet.Datastore) { +func TestChangeEmail(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + if ds.Name() == "inmem" { t.Skip("inmem is being deprecated, test skipped") } diff --git a/server/datastore/datastore_hosts.go b/server/datastore/mysql/hosts_test.go similarity index 94% rename from server/datastore/datastore_hosts.go rename to server/datastore/mysql/hosts_test.go index 8c23bad744..1ad21a3130 100644 --- a/server/datastore/datastore_hosts.go +++ b/server/datastore/mysql/hosts_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "encoding/json" @@ -42,7 +42,10 @@ var enrollTests = []struct { }, } -func testSaveHosts(t *testing.T, ds fleet.Datastore) { +func TestSaveHosts(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + host, err := ds.NewHost(&fleet.Host{ DetailUpdatedAt: time.Now(), LabelUpdatedAt: time.Now(), @@ -93,7 +96,10 @@ func testSaveHosts(t *testing.T, ds fleet.Datastore) { assert.Nil(t, host) } -func testSaveHostPackStats(t *testing.T, ds fleet.Datastore) { +func TestSaveHostPackStats(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + host, err := ds.NewHost(&fleet.Host{ DetailUpdatedAt: time.Now(), LabelUpdatedAt: time.Now(), @@ -212,7 +218,10 @@ func testSaveHostPackStats(t *testing.T, ds fleet.Datastore) { require.Len(t, host.PackStats, 0) } -func testDeleteHost(t *testing.T, ds fleet.Datastore) { +func TestDeleteHost(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + host, err := ds.NewHost(&fleet.Host{ DetailUpdatedAt: time.Now(), LabelUpdatedAt: time.Now(), @@ -284,7 +293,10 @@ func testListHosts(t *testing.T, ds fleet.Datastore) { require.Equal(t, hosts[0].ID, hosts2[0].ID) } -func testListHostsFilterAdditional(t *testing.T, ds fleet.Datastore) { +func TestListHostsFilterAdditional(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + h, err := ds.NewHost(&fleet.Host{ DetailUpdatedAt: time.Now(), LabelUpdatedAt: time.Now(), @@ -321,7 +333,10 @@ func testListHostsFilterAdditional(t *testing.T, ds fleet.Datastore) { assert.Equal(t, &additional, hosts[0].Additional) } -func testListHostsStatus(t *testing.T, ds fleet.Datastore) { +func TestListHostsStatus(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + for i := 0; i < 10; i++ { _, err := ds.NewHost(&fleet.Host{ DetailUpdatedAt: time.Now(), @@ -357,7 +372,10 @@ func testListHostsStatus(t *testing.T, ds fleet.Datastore) { assert.Equal(t, 10, len(hosts)) } -func testListHostsQuery(t *testing.T, ds fleet.Datastore) { +func TestListHostsQuery(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + hosts := []*fleet.Host{} for i := 0; i < 10; i++ { host, err := ds.NewHost(&fleet.Host{ @@ -423,7 +441,10 @@ func testListHostsQuery(t *testing.T, ds fleet.Datastore) { assert.Equal(t, 1, len(gotHosts)) } -func testEnrollHost(t *testing.T, ds fleet.Datastore) { +func TestEnrollHost(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + test.AddAllHostsLabel(t, ds) team, err := ds.NewTeam(&fleet.Team{Name: "team1"}) @@ -460,7 +481,10 @@ func testEnrollHost(t *testing.T, ds fleet.Datastore) { } } -func testAuthenticateHost(t *testing.T, ds fleet.Datastore) { +func TestAuthenticateHost(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + test.AddAllHostsLabel(t, ds) for _, tt := range enrollTests { h, err := ds.EnrollHost(tt.uuid, tt.nodeKey, nil, 0) @@ -478,7 +502,10 @@ func testAuthenticateHost(t *testing.T, ds fleet.Datastore) { assert.Error(t, err) } -func testAuthenticateHostCaseSensitive(t *testing.T, ds fleet.Datastore) { +func TestAuthenticateHostCaseSensitive(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + test.AddAllHostsLabel(t, ds) for _, tt := range enrollTests { h, err := ds.EnrollHost(tt.uuid, tt.nodeKey, nil, 0) @@ -489,7 +516,10 @@ func testAuthenticateHostCaseSensitive(t *testing.T, ds fleet.Datastore) { } } -func testSearchHosts(t *testing.T, ds fleet.Datastore) { +func TestSearchHosts(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + _, err := ds.NewHost(&fleet.Host{ OsqueryHostID: "1234", DetailUpdatedAt: time.Now(), @@ -578,7 +608,10 @@ func testSearchHosts(t *testing.T, ds fleet.Datastore) { assert.Equal(t, 1, len(hits)) } -func testSearchHostsLimit(t *testing.T, ds fleet.Datastore) { +func TestSearchHostsLimit(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + filter := fleet.TeamFilter{User: test.UserAdmin} for i := 0; i < 15; i++ { @@ -599,7 +632,10 @@ func testSearchHostsLimit(t *testing.T, ds fleet.Datastore) { assert.Len(t, hosts, 10) } -func testGenerateHostStatusStatistics(t *testing.T, ds fleet.Datastore) { +func TestGenerateHostStatusStatistics(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + filter := fleet.TeamFilter{User: test.UserAdmin} mockClock := clock.NewMockClock() @@ -678,7 +714,10 @@ func testGenerateHostStatusStatistics(t *testing.T, ds fleet.Datastore) { assert.Equal(t, uint(4), new) } -func testMarkHostSeen(t *testing.T, ds fleet.Datastore) { +func TestMarkHostSeen(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + mockClock := clock.NewMockClock() anHourAgo := mockClock.Now().Add(-1 * time.Hour).UTC() @@ -713,7 +752,10 @@ func testMarkHostSeen(t *testing.T, ds fleet.Datastore) { } } -func testMarkHostsSeen(t *testing.T, ds fleet.Datastore) { +func TestMarkHostsSeen(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + mockClock := clock.NewMockClock() aSecondAgo := mockClock.Now().Add(-1 * time.Second).UTC() @@ -774,7 +816,10 @@ func testMarkHostsSeen(t *testing.T, ds fleet.Datastore) { } -func testCleanupIncomingHosts(t *testing.T, ds fleet.Datastore) { +func TestCleanupIncomingHosts(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + mockClock := clock.NewMockClock() h1, err := ds.NewHost(&fleet.Host{ @@ -820,7 +865,10 @@ func testCleanupIncomingHosts(t *testing.T, ds fleet.Datastore) { assert.Nil(t, err) } -func testHostIDsByName(t *testing.T, ds fleet.Datastore) { +func TestHostIDsByName(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + for i := 0; i < 10; i++ { _, err := ds.NewHost(&fleet.Host{ DetailUpdatedAt: time.Now(), @@ -841,7 +889,10 @@ func testHostIDsByName(t *testing.T, ds fleet.Datastore) { assert.Equal(t, hosts, []uint{2, 3, 6}) } -func testHostAdditional(t *testing.T, ds fleet.Datastore) { +func TestHostAdditional(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + _, err := ds.NewHost(&fleet.Host{ DetailUpdatedAt: time.Now(), LabelUpdatedAt: time.Now(), @@ -912,7 +963,10 @@ func testHostAdditional(t *testing.T, ds fleet.Datastore) { assert.Equal(t, &additional, h.Additional) } -func testHostByIdentifier(t *testing.T, ds fleet.Datastore) { +func TestHostByIdentifier(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + for i := 1; i <= 10; i++ { _, err := ds.NewHost(&fleet.Host{ DetailUpdatedAt: time.Now(), @@ -950,7 +1004,10 @@ func testHostByIdentifier(t *testing.T, ds fleet.Datastore) { require.Error(t, err) } -func testAddHostsToTeam(t *testing.T, ds fleet.Datastore) { +func TestAddHostsToTeam(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + team1, err := ds.NewTeam(&fleet.Team{Name: "team1"}) require.NoError(t, err) team2, err := ds.NewTeam(&fleet.Team{Name: "team2"}) @@ -997,7 +1054,10 @@ func testAddHostsToTeam(t *testing.T, ds fleet.Datastore) { } } -func testSaveUsers(t *testing.T, ds fleet.Datastore) { +func TestSaveUsers(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + host, err := ds.NewHost(&fleet.Host{ DetailUpdatedAt: time.Now(), LabelUpdatedAt: time.Now(), diff --git a/server/datastore/datastore_invites.go b/server/datastore/mysql/invites_test.go similarity index 89% rename from server/datastore/datastore_invites.go rename to server/datastore/mysql/invites_test.go index 1a69899518..2bff3a4ec7 100644 --- a/server/datastore/datastore_invites.go +++ b/server/datastore/mysql/invites_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "errors" @@ -11,7 +11,10 @@ import ( "gopkg.in/guregu/null.v3" ) -func testCreateInvite(t *testing.T, ds fleet.Datastore) { +func TestCreateInvite(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + for i := 0; i < 3; i++ { _, err := ds.NewTeam(&fleet.Team{Name: fmt.Sprintf("%d", i)}) require.NoError(t, err) @@ -64,7 +67,10 @@ func setupTestInvites(t *testing.T, ds fleet.Datastore) { } -func testListInvites(t *testing.T, ds fleet.Datastore) { +func TestListInvites(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + setupTestInvites(t, ds) opt := fleet.ListOptions{ @@ -91,7 +97,9 @@ func testListInvites(t *testing.T, ds fleet.Datastore) { } -func testDeleteInvite(t *testing.T, ds fleet.Datastore) { +func TestDeleteInvite(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() setupTestInvites(t, ds) @@ -109,7 +117,10 @@ func testDeleteInvite(t *testing.T, ds fleet.Datastore) { } -func testInviteByToken(t *testing.T, ds fleet.Datastore) { +func TestInviteByToken(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + setupTestInvites(t, ds) var inviteTests = []struct { @@ -141,7 +152,10 @@ func testInviteByToken(t *testing.T, ds fleet.Datastore) { } } -func testInviteByEmail(t *testing.T, ds fleet.Datastore) { +func TestInviteByEmail(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + setupTestInvites(t, ds) var inviteTests = []struct { diff --git a/server/datastore/mysql/labels_test.go b/server/datastore/mysql/labels_test.go index b10c042ddf..8c6293e0af 100644 --- a/server/datastore/mysql/labels_test.go +++ b/server/datastore/mysql/labels_test.go @@ -1,9 +1,15 @@ package mysql import ( + "fmt" + "sort" "strconv" "testing" + "time" + "github.com/fleetdm/fleet/v4/server/fleet" + "github.com/fleetdm/fleet/v4/server/ptr" + "github.com/fleetdm/fleet/v4/server/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -28,3 +34,513 @@ func TestBatchHostnamesLarge(t *testing.T) { assert.Equal(t, large[150000:200000], batched[3]) assert.Equal(t, large[200000:230000], batched[4]) } + +func TestLabels(t *testing.T) { + db := CreateMySQLDS(t) + defer db.Close() + + test.AddAllHostsLabel(t, db) + hosts := []fleet.Host{} + var host *fleet.Host + var err error + for i := 0; i < 10; i++ { + host, err = db.EnrollHost(fmt.Sprint(i), fmt.Sprint(i), nil, 0) + require.Nil(t, err, "enrollment should succeed") + hosts = append(hosts, *host) + } + host.Platform = "darwin" + require.NoError(t, db.SaveHost(host)) + + baseTime := time.Now() + + // No labels to check + queries, err := db.LabelQueriesForHost(host, baseTime) + assert.Nil(t, err) + assert.Len(t, queries, 0) + + // Only 'All Hosts' label should be returned + labels, err := db.ListLabelsForHost(host.ID) + assert.Nil(t, err) + assert.Len(t, labels, 1) + + newLabels := []*fleet.LabelSpec{ + // Note these are intentionally out of order + &fleet.LabelSpec{ + Name: "label3", + Query: "query3", + Platform: "darwin", + }, + &fleet.LabelSpec{ + Name: "label1", + Query: "query1", + }, + &fleet.LabelSpec{ + Name: "label2", + Query: "query2", + Platform: "darwin", + }, + &fleet.LabelSpec{ + Name: "label4", + Query: "query4", + Platform: "darwin", + }, + } + err = db.ApplyLabelSpecs(newLabels) + require.Nil(t, err) + + expectQueries := map[string]string{ + "2": "query3", + "3": "query1", + "4": "query2", + "5": "query4", + } + + host.Platform = "darwin" + + // Now queries should be returned + queries, err = db.LabelQueriesForHost(host, baseTime) + assert.Nil(t, err) + assert.Equal(t, expectQueries, queries) + + // No labels should match with no results yet + labels, err = db.ListLabelsForHost(host.ID) + assert.Nil(t, err) + assert.Len(t, labels, 1) + + // Record a query execution + err = db.RecordLabelQueryExecutions(host, map[uint]bool{1: true, 2: false, 3: true, 4: false, 5: false}, baseTime) + assert.Nil(t, err) + + host, err = db.Host(host.ID) + require.NoError(t, err) + host.LabelUpdatedAt = baseTime + + // Now no queries should be returned + queries, err = db.LabelQueriesForHost(host, baseTime.Add(-1*time.Minute)) + assert.Nil(t, err) + assert.Len(t, queries, 0) + + // Ensure enough gap in created_at + time.Sleep(2 * time.Second) + + // A new label targeting another platform should not effect the labels for + // this host + err = db.ApplyLabelSpecs([]*fleet.LabelSpec{ + &fleet.LabelSpec{ + Name: "label5", + Platform: "not-matching", + Query: "query5", + }, + }) + require.NoError(t, err) + queries, err = db.LabelQueriesForHost(host, baseTime.Add(-1*time.Minute)) + assert.Nil(t, err) + assert.Len(t, queries, 0) + + // If a new label is added, all labels should be returned + err = db.ApplyLabelSpecs([]*fleet.LabelSpec{ + &fleet.LabelSpec{ + Name: "label6", + Platform: "", + Query: "query6", + }, + }) + require.NoError(t, err) + expectQueries["7"] = "query6" + queries, err = db.LabelQueriesForHost(host, baseTime.Add(-1*time.Minute)) + assert.Nil(t, err) + assert.Len(t, queries, 5) + + // After expiration, all queries should be returned + queries, err = db.LabelQueriesForHost(host, baseTime.Add((2 * time.Minute))) + assert.Nil(t, err) + assert.Equal(t, expectQueries, queries) + + // Now the two matching labels should be returned + labels, err = db.ListLabelsForHost(host.ID) + assert.Nil(t, err) + if assert.Len(t, labels, 2) { + labelNames := []string{labels[0].Name, labels[1].Name} + sort.Strings(labelNames) + assert.Equal(t, "All Hosts", labelNames[0]) + assert.Equal(t, "label1", labelNames[1]) + } + + // A host that hasn't executed any label queries should still be asked + // to execute those queries + hosts[0].Platform = "darwin" + queries, err = db.LabelQueriesForHost(&hosts[0], time.Now()) + assert.Nil(t, err) + assert.Len(t, queries, 5) + + // Only the 'All Hosts' label should apply for a host with no labels + // executed. + labels, err = db.ListLabelsForHost(hosts[0].ID) + assert.Nil(t, err) + assert.Len(t, labels, 1) +} + +func TestSearchLabels(t *testing.T) { + db := CreateMySQLDS(t) + defer db.Close() + + specs := []*fleet.LabelSpec{ + &fleet.LabelSpec{ + ID: 1, + Name: "foo", + }, + &fleet.LabelSpec{ + ID: 2, + Name: "bar", + }, + &fleet.LabelSpec{ + ID: 3, + Name: "foo-bar", + }, + &fleet.LabelSpec{ + ID: 4, + Name: "All Hosts", + LabelType: fleet.LabelTypeBuiltIn, + }, + } + err := db.ApplyLabelSpecs(specs) + require.Nil(t, err) + + all, err := db.Label(specs[3].ID) + require.Nil(t, err) + l3, err := db.Label(specs[2].ID) + require.Nil(t, err) + + user := &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)} + filter := fleet.TeamFilter{User: user} + + // We once threw errors when the search query was empty. Verify that we + // don't error. + labels, err := db.SearchLabels(filter, "") + require.Nil(t, err) + assert.Contains(t, labels, all) + + labels, err = db.SearchLabels(filter, "foo") + require.Nil(t, err) + assert.Len(t, labels, 3) + assert.Contains(t, labels, all) + + labels, err = db.SearchLabels(filter, "foo", all.ID, l3.ID) + require.Nil(t, err) + assert.Len(t, labels, 1) + assert.Equal(t, "foo", labels[0].Name) + + labels, err = db.SearchLabels(filter, "xxx") + require.Nil(t, err) + assert.Len(t, labels, 1) + assert.Contains(t, labels, all) +} + +func TestSearchLabelsLimit(t *testing.T) { + db := CreateMySQLDS(t) + defer db.Close() + + if db.Name() == "inmem" { + t.Skip("inmem is being deprecated, test skipped") + } + + all := &fleet.LabelSpec{ + Name: "All Hosts", + LabelType: fleet.LabelTypeBuiltIn, + } + err := db.ApplyLabelSpecs([]*fleet.LabelSpec{all}) + require.Nil(t, err) + + for i := 0; i < 15; i++ { + l := &fleet.LabelSpec{ + Name: fmt.Sprintf("foo%d", i), + } + err := db.ApplyLabelSpecs([]*fleet.LabelSpec{l}) + require.Nil(t, err) + } + + user := &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)} + filter := fleet.TeamFilter{User: user} + + labels, err := db.SearchLabels(filter, "foo") + require.Nil(t, err) + assert.Len(t, labels, 11) +} + +func TestListHostsInLabel(t *testing.T) { + db := CreateMySQLDS(t) + defer db.Close() + + h1, err := db.NewHost(&fleet.Host{ + DetailUpdatedAt: time.Now(), + LabelUpdatedAt: time.Now(), + SeenTime: time.Now(), + OsqueryHostID: "1", + NodeKey: "1", + UUID: "1", + Hostname: "foo.local", + }) + require.Nil(t, err) + + h2, err := db.NewHost(&fleet.Host{ + DetailUpdatedAt: time.Now(), + LabelUpdatedAt: time.Now(), + SeenTime: time.Now(), + OsqueryHostID: "2", + NodeKey: "2", + UUID: "2", + Hostname: "bar.local", + }) + require.Nil(t, err) + + h3, err := db.NewHost(&fleet.Host{ + DetailUpdatedAt: time.Now(), + LabelUpdatedAt: time.Now(), + SeenTime: time.Now(), + OsqueryHostID: "3", + NodeKey: "3", + UUID: "3", + Hostname: "baz.local", + }) + require.Nil(t, err) + + l1 := &fleet.LabelSpec{ + ID: 1, + Name: "label foo", + Query: "query1", + } + err = db.ApplyLabelSpecs([]*fleet.LabelSpec{l1}) + require.Nil(t, err) + + filter := fleet.TeamFilter{User: test.UserAdmin} + + { + hosts, err := db.ListHostsInLabel(filter, l1.ID, fleet.HostListOptions{}) + require.Nil(t, err) + assert.Len(t, hosts, 0) + } + + for _, h := range []*fleet.Host{h1, h2, h3} { + err = db.RecordLabelQueryExecutions(h, map[uint]bool{l1.ID: true}, time.Now()) + assert.Nil(t, err) + } + + { + hosts, err := db.ListHostsInLabel(filter, l1.ID, fleet.HostListOptions{}) + require.Nil(t, err) + assert.Len(t, hosts, 3) + } +} + +func TestBuiltInLabels(t *testing.T) { + db := CreateMySQLDS(t) + defer db.Close() + + require.Nil(t, db.MigrateData()) + + user := &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)} + filter := fleet.TeamFilter{User: user} + + hits, err := db.SearchLabels(filter, "macOS") + require.Nil(t, err) + // Should get Mac OS X and All Hosts + assert.Equal(t, 2, len(hits)) + assert.Equal(t, fleet.LabelTypeBuiltIn, hits[0].LabelType) + assert.Equal(t, fleet.LabelTypeBuiltIn, hits[1].LabelType) +} + +func TestListUniqueHostsInLabels(t *testing.T) { + db := CreateMySQLDS(t) + defer db.Close() + + hosts := []*fleet.Host{} + for i := 0; i < 4; i++ { + h, err := db.NewHost(&fleet.Host{ + DetailUpdatedAt: time.Now(), + LabelUpdatedAt: time.Now(), + SeenTime: time.Now(), + OsqueryHostID: strconv.Itoa(i), + NodeKey: strconv.Itoa(i), + UUID: strconv.Itoa(i), + Hostname: fmt.Sprintf("host_%d", i), + }) + require.Nil(t, err) + require.NotNil(t, h) + hosts = append(hosts, h) + } + + l1 := fleet.LabelSpec{ + ID: 1, + Name: "label foo", + Query: "query1", + } + l2 := fleet.LabelSpec{ + ID: 2, + Name: "label bar", + Query: "query2", + } + err := db.ApplyLabelSpecs([]*fleet.LabelSpec{&l1, &l2}) + require.Nil(t, err) + + for i := 0; i < 3; i++ { + err = db.RecordLabelQueryExecutions(hosts[i], map[uint]bool{l1.ID: true}, time.Now()) + assert.Nil(t, err) + } + // host 2 executes twice + for i := 2; i < len(hosts); i++ { + err = db.RecordLabelQueryExecutions(hosts[i], map[uint]bool{l2.ID: true}, time.Now()) + assert.Nil(t, err) + } + + filter := fleet.TeamFilter{User: test.UserAdmin} + + uniqueHosts, err := db.ListUniqueHostsInLabels(filter, []uint{l1.ID, l2.ID}) + assert.Nil(t, err) + assert.Equal(t, len(hosts), len(uniqueHosts)) + + labels, err := db.ListLabels(filter, fleet.ListOptions{}) + require.Nil(t, err) + require.Len(t, labels, 2) + +} + +func TestChangeLabelDetails(t *testing.T) { + db := CreateMySQLDS(t) + defer db.Close() + + if db.Name() == "inmem" { + t.Skip("inmem is being deprecated") + } + + label := fleet.LabelSpec{ + ID: 1, + Name: "my label", + Description: "a label", + Query: "select 1 from processes", + Platform: "darwin", + } + err := db.ApplyLabelSpecs([]*fleet.LabelSpec{&label}) + require.Nil(t, err) + + label.Description = "changed description" + err = db.ApplyLabelSpecs([]*fleet.LabelSpec{&label}) + require.Nil(t, err) + + saved, err := db.Label(label.ID) + require.Nil(t, err) + assert.Equal(t, label.Name, saved.Name) +} + +func setupLabelSpecsTest(t *testing.T, ds fleet.Datastore) []*fleet.LabelSpec { + for i := 0; i < 10; i++ { + _, err := ds.NewHost(&fleet.Host{ + DetailUpdatedAt: time.Now(), + LabelUpdatedAt: time.Now(), + SeenTime: time.Now(), + OsqueryHostID: strconv.Itoa(i), + NodeKey: strconv.Itoa(i), + UUID: strconv.Itoa(i), + Hostname: strconv.Itoa(i), + }) + require.Nil(t, err) + } + + expectedSpecs := []*fleet.LabelSpec{ + { + Name: "foo", + Query: "select * from foo", + Description: "foo description", + Platform: "darwin", + }, + { + Name: "bar", + Query: "select * from bar", + }, + { + Name: "bing", + Query: "select * from bing", + }, + { + Name: "All Hosts", + Query: "SELECT 1", + LabelType: fleet.LabelTypeBuiltIn, + LabelMembershipType: fleet.LabelMembershipTypeManual, + }, + { + Name: "Manual Label", + LabelMembershipType: fleet.LabelMembershipTypeManual, + Hosts: []string{ + "1", "2", "3", "4", + }, + }, + } + err := ds.ApplyLabelSpecs(expectedSpecs) + require.Nil(t, err) + + return expectedSpecs +} + +func TestGetLabelSpec(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + + expectedSpecs := setupLabelSpecsTest(t, ds) + + for _, s := range expectedSpecs { + spec, err := ds.GetLabelSpec(s.Name) + require.Nil(t, err) + assert.Equal(t, s, spec) + } +} + +func TestApplyLabelSpecsRoundtrip(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + + expectedSpecs := setupLabelSpecsTest(t, ds) + + specs, err := ds.GetLabelSpecs() + require.Nil(t, err) + test.ElementsMatchSkipTimestampsID(t, expectedSpecs, specs) + + // Should be idempotent + err = ds.ApplyLabelSpecs(expectedSpecs) + require.Nil(t, err) + specs, err = ds.GetLabelSpecs() + require.Nil(t, err) + test.ElementsMatchSkipTimestampsID(t, expectedSpecs, specs) +} + +func TestLabelIDsByName(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + + setupLabelSpecsTest(t, ds) + + labels, err := ds.LabelIDsByName([]string{"foo", "bar", "bing"}) + require.Nil(t, err) + sort.Slice(labels, func(i, j int) bool { return labels[i] < labels[j] }) + assert.Equal(t, []uint{1, 2, 3}, labels) +} + +func TestSaveLabel(t *testing.T) { + db := CreateMySQLDS(t) + defer db.Close() + + label := &fleet.Label{ + Name: "my label", + Description: "a label", + Query: "select 1 from processes;", + Platform: "darwin", + } + label, err := db.NewLabel(label) + require.Nil(t, err) + label.Name = "changed name" + label.Description = "changed description" + _, err = db.SaveLabel(label) + require.Nil(t, err) + saved, err := db.Label(label.ID) + require.Nil(t, err) + assert.Equal(t, label.Name, saved.Name) + assert.Equal(t, label.Description, saved.Description) +} diff --git a/server/datastore/datastore_migrations.go b/server/datastore/mysql/migrations_test.go similarity index 87% rename from server/datastore/datastore_migrations.go rename to server/datastore/mysql/migrations_test.go index 725c591f0a..a082f79f40 100644 --- a/server/datastore/datastore_migrations.go +++ b/server/datastore/mysql/migrations_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "testing" @@ -8,7 +8,10 @@ import ( "github.com/stretchr/testify/require" ) -func testMigrationStatus(t *testing.T, ds fleet.Datastore) { +func TestMigrationStatus(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + if ds.Name() == "inmem" { t.Skip("inmem is being deprecated, test skipped") } diff --git a/server/datastore/mysql/mysql_test.go b/server/datastore/mysql/mysql_test.go index 90850f3977..0c67908d83 100644 --- a/server/datastore/mysql/mysql_test.go +++ b/server/datastore/mysql/mysql_test.go @@ -1,6 +1,7 @@ package mysql import ( + "os" "testing" "github.com/DATA-DOG/go-sqlmock" @@ -15,6 +16,14 @@ import ( "github.com/stretchr/testify/require" ) +func TestMain(m *testing.M) { + if _, ok := os.LookupEnv("MYSQL_TEST"); ok { + // Initialize the schema once for the entire test run. + initializeSchemaOrPanic() + } + os.Exit(m.Run()) +} + func TestSanitizeColumn(t *testing.T) { t.Parallel() diff --git a/server/datastore/datastore_packs.go b/server/datastore/mysql/packs_test.go similarity index 90% rename from server/datastore/datastore_packs.go rename to server/datastore/mysql/packs_test.go index d308135fd9..7f9d91c31b 100644 --- a/server/datastore/datastore_packs.go +++ b/server/datastore/mysql/packs_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "testing" @@ -11,7 +11,10 @@ import ( "github.com/stretchr/testify/require" ) -func testDeletePack(t *testing.T, ds fleet.Datastore) { +func TestDeletePack(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + pack := test.NewPack(t, ds, "foo") assert.NotEqual(t, uint(0), pack.ID) @@ -26,7 +29,10 @@ func testDeletePack(t *testing.T, ds fleet.Datastore) { assert.NotNil(t, err) } -func testSavePack(t *testing.T, ds fleet.Datastore) { +func TestSavePack(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + expectedPack := &fleet.Pack{ Name: "foo", HostIDs: []uint{1}, @@ -60,7 +66,10 @@ func testSavePack(t *testing.T, ds fleet.Datastore) { test.EqualSkipTimestampsID(t, expectedPack, pack) } -func testGetPackByName(t *testing.T, ds fleet.Datastore) { +func TestGetPackByName(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + pack := test.NewPack(t, ds, "foo") assert.NotEqual(t, uint(0), pack.ID) @@ -77,7 +86,10 @@ func testGetPackByName(t *testing.T, ds fleet.Datastore) { } -func testListPacks(t *testing.T, ds fleet.Datastore) { +func TestListPacks(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + p1 := &fleet.PackSpec{ ID: 1, Name: "foo_pack", @@ -207,7 +219,10 @@ func setupPackSpecsTest(t *testing.T, ds fleet.Datastore) []*fleet.PackSpec { return expectedSpecs } -func testApplyPackSpecRoundtrip(t *testing.T, ds fleet.Datastore) { +func TestApplyPackSpecRoundtrip(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + expectedSpecs := setupPackSpecsTest(t, ds) gotSpec, err := ds.GetPackSpecs() @@ -215,7 +230,10 @@ func testApplyPackSpecRoundtrip(t *testing.T, ds fleet.Datastore) { assert.Equal(t, expectedSpecs, gotSpec) } -func testGetPackSpec(t *testing.T, ds fleet.Datastore) { +func TestGetPackSpec(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + expectedSpecs := setupPackSpecsTest(t, ds) for _, s := range expectedSpecs { @@ -225,7 +243,10 @@ func testGetPackSpec(t *testing.T, ds fleet.Datastore) { } } -func testApplyPackSpecMissingQueries(t *testing.T, ds fleet.Datastore) { +func TestApplyPackSpecMissingQueries(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + // Do not define queries mentioned in spec specs := []*fleet.PackSpec{ { @@ -250,7 +271,10 @@ func testApplyPackSpecMissingQueries(t *testing.T, ds fleet.Datastore) { } } -func testApplyPackSpecMissingName(t *testing.T, ds fleet.Datastore) { +func TestApplyPackSpecMissingName(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + setupPackSpecsTest(t, ds) specs := []*fleet.PackSpec{ @@ -276,7 +300,10 @@ func testApplyPackSpecMissingName(t *testing.T, ds fleet.Datastore) { assert.Equal(t, "foo", spec.Queries[0].Name) } -func testListPacksForHost(t *testing.T, ds fleet.Datastore) { +func TestListPacksForHost(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + if ds.Name() == "inmem" { t.Skip("inmem is deprecated") } @@ -384,7 +411,10 @@ func testListPacksForHost(t *testing.T, ds fleet.Datastore) { } } -func testEnsureGlobalPack(t *testing.T, ds fleet.Datastore) { +func TestEnsureGlobalPack(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + test.AddAllHostsLabel(t, ds) packs, err := ds.ListPacks(fleet.ListOptions{}) diff --git a/server/datastore/datastore_password_reset.go b/server/datastore/mysql/password_reset_test.go similarity index 85% rename from server/datastore/datastore_password_reset.go rename to server/datastore/mysql/password_reset_test.go index 03f57365b4..eec4a9ac10 100644 --- a/server/datastore/datastore_password_reset.go +++ b/server/datastore/mysql/password_reset_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "testing" @@ -8,7 +8,10 @@ import ( "github.com/stretchr/testify/assert" ) -func testPasswordResetRequests(t *testing.T, db fleet.Datastore) { +func TestPasswordResetRequests(t *testing.T) { + db := CreateMySQLDS(t) + defer db.Close() + createTestUsers(t, db) now := time.Now().UTC() tomorrow := now.Add(time.Hour * 24) diff --git a/server/datastore/datastore_queries.go b/server/datastore/mysql/queries_test.go similarity index 93% rename from server/datastore/datastore_queries.go rename to server/datastore/mysql/queries_test.go index f9d662f281..abf3d2eb33 100644 --- a/server/datastore/datastore_queries.go +++ b/server/datastore/mysql/queries_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "fmt" @@ -11,7 +11,10 @@ import ( "github.com/stretchr/testify/require" ) -func testApplyQueries(t *testing.T, ds fleet.Datastore) { +func TestApplyQueries(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + test.AddAllHostsLabel(t, ds) zwass := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) @@ -75,7 +78,10 @@ func testApplyQueries(t *testing.T, ds fleet.Datastore) { assert.Equal(t, &zwass.ID, queries[2].AuthorID) } -func testDeleteQuery(t *testing.T, ds fleet.Datastore) { +func TestDeleteQuery(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + user := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) query := &fleet.Query{ @@ -96,7 +102,10 @@ func testDeleteQuery(t *testing.T, ds fleet.Datastore) { assert.NotNil(t, err) } -func testGetQueryByName(t *testing.T, ds fleet.Datastore) { +func TestGetQueryByName(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + user := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) test.NewQuery(t, ds, "q1", "select * from time", user.ID, true) actual, err := ds.QueryByName("q1") @@ -109,7 +118,10 @@ func testGetQueryByName(t *testing.T, ds fleet.Datastore) { assert.True(t, fleet.IsNotFound(err)) } -func testDeleteQueries(t *testing.T, ds fleet.Datastore) { +func TestDeleteQueries(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + user := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) q1 := test.NewQuery(t, ds, "q1", "select * from time", user.ID, true) @@ -147,7 +159,10 @@ func testDeleteQueries(t *testing.T, ds fleet.Datastore) { } -func testSaveQuery(t *testing.T, ds fleet.Datastore) { +func TestSaveQuery(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + user := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) query := &fleet.Query{ @@ -174,7 +189,10 @@ func testSaveQuery(t *testing.T, ds fleet.Datastore) { assert.True(t, queryVerify.ObserverCanRun) } -func testListQuery(t *testing.T, ds fleet.Datastore) { +func TestListQuery(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + user := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) for i := 0; i < 10; i++ { @@ -202,7 +220,10 @@ func testListQuery(t *testing.T, ds fleet.Datastore) { assert.Equal(t, 10, len(results)) } -func testLoadPacksForQueries(t *testing.T, ds fleet.Datastore) { +func TestLoadPacksForQueries(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + zwass := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) queries := []*fleet.Query{ {Name: "q1", Query: "select * from time"}, @@ -326,7 +347,10 @@ func testLoadPacksForQueries(t *testing.T, ds fleet.Datastore) { } } -func testDuplicateNewQuery(t *testing.T, ds fleet.Datastore) { +func TestDuplicateNewQuery(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + user := test.NewUser(t, ds, "Mike Arpaia", "mike@fleet.co", true) q1, err := ds.NewQuery(&fleet.Query{ Name: "foo", diff --git a/server/datastore/datastore_scheduled_queries.go b/server/datastore/mysql/scheduled_queries_test.go similarity index 91% rename from server/datastore/datastore_scheduled_queries.go rename to server/datastore/mysql/scheduled_queries_test.go index ff8f01c4dc..35f2339071 100644 --- a/server/datastore/datastore_scheduled_queries.go +++ b/server/datastore/mysql/scheduled_queries_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "testing" @@ -10,7 +10,10 @@ import ( "github.com/stretchr/testify/require" ) -func testListScheduledQueriesInPack(t *testing.T, ds fleet.Datastore) { +func TestListScheduledQueriesInPack(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + zwass := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) queries := []*fleet.Query{ {Name: "foo", Description: "get the foos", Query: "select * from foo"}, @@ -76,7 +79,10 @@ func testListScheduledQueriesInPack(t *testing.T, ds fleet.Datastore) { require.Len(t, gotQueries, 3) } -func testNewScheduledQuery(t *testing.T, ds fleet.Datastore) { +func TestNewScheduledQuery(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + u1 := test.NewUser(t, ds, "Admin", "admin@fleet.co", true) q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) p1 := test.NewPack(t, ds, "baz") @@ -92,7 +98,10 @@ func testNewScheduledQuery(t *testing.T, ds fleet.Datastore) { assert.Equal(t, "select * from time;", query.Query) } -func testScheduledQuery(t *testing.T, ds fleet.Datastore) { +func TestScheduledQuery(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + u1 := test.NewUser(t, ds, "Admin", "admin@fleet.co", true) q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) p1 := test.NewPack(t, ds, "baz") @@ -116,7 +125,10 @@ func testScheduledQuery(t *testing.T, ds fleet.Datastore) { assert.False(t, *query.Denylist) } -func testDeleteScheduledQuery(t *testing.T, ds fleet.Datastore) { +func TestDeleteScheduledQuery(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + u1 := test.NewUser(t, ds, "Admin", "admin@fleet.co", true) q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) p1 := test.NewPack(t, ds, "baz") @@ -133,7 +145,10 @@ func testDeleteScheduledQuery(t *testing.T, ds fleet.Datastore) { require.NotNil(t, err) } -func testCascadingDeletionOfQueries(t *testing.T, ds fleet.Datastore) { +func TestCascadingDeletionOfQueries(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + zwass := test.NewUser(t, ds, "Zach", "zwass@fleet.co", true) queries := []*fleet.Query{ {Name: "foo", Description: "get the foos", Query: "select * from foo"}, diff --git a/server/datastore/datastore_software.go b/server/datastore/mysql/software_test.go similarity index 96% rename from server/datastore/datastore_software.go rename to server/datastore/mysql/software_test.go index a121d33291..7ba0893ebd 100644 --- a/server/datastore/datastore_software.go +++ b/server/datastore/mysql/software_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "testing" @@ -10,7 +10,10 @@ import ( "github.com/stretchr/testify/require" ) -func testSaveHostSoftware(t *testing.T, ds fleet.Datastore) { +func TestSaveHostSoftware(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + host1 := test.NewHost(t, ds, "host1", "", "host1key", "host1uuid", time.Now()) host2 := test.NewHost(t, ds, "host2", "", "host2key", "host2uuid", time.Now()) diff --git a/server/datastore/datastore_targets.go b/server/datastore/mysql/targets_test.go similarity index 97% rename from server/datastore/datastore_targets.go rename to server/datastore/mysql/targets_test.go index c3fb41cff4..05e265518c 100644 --- a/server/datastore/datastore_targets.go +++ b/server/datastore/mysql/targets_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "strconv" @@ -13,7 +13,10 @@ import ( "github.com/stretchr/testify/require" ) -func testCountHostsInTargets(t *testing.T, ds fleet.Datastore) { +func TestCountHostsInTargets(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + if ds.Name() == "inmem" { t.Skip("inmem is being deprecated, test skipped") } @@ -159,7 +162,10 @@ func testCountHostsInTargets(t *testing.T, ds fleet.Datastore) { } -func testHostStatus(t *testing.T, ds fleet.Datastore) { +func TestHostStatus(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + test.AddAllHostsLabel(t, ds) if ds.Name() == "inmem" { @@ -223,7 +229,10 @@ func testHostStatus(t *testing.T, ds fleet.Datastore) { } } -func testHostIDsInTargets(t *testing.T, ds fleet.Datastore) { +func TestHostIDsInTargets(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + if ds.Name() == "inmem" { t.Skip("inmem is being deprecated, test skipped") } diff --git a/server/datastore/datastore_teams.go b/server/datastore/mysql/teams_test.go similarity index 94% rename from server/datastore/datastore_teams.go rename to server/datastore/mysql/teams_test.go index f45a5eb13d..97d357cf97 100644 --- a/server/datastore/datastore_teams.go +++ b/server/datastore/mysql/teams_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "sort" @@ -12,7 +12,10 @@ import ( "github.com/stretchr/testify/require" ) -func testTeamGetSetDelete(t *testing.T, ds fleet.Datastore) { +func TestTeamGetSetDelete(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + var createTests = []struct { name, description string }{ @@ -48,7 +51,10 @@ func testTeamGetSetDelete(t *testing.T, ds fleet.Datastore) { } } -func testTeamUsers(t *testing.T, ds fleet.Datastore) { +func TestTeamUsers(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + users := createTestUsers(t, ds) user1 := fleet.User{Name: users[0].Name, Email: users[0].Email, ID: users[0].ID} user2 := fleet.User{Name: users[1].Name, Email: users[1].Email, ID: users[1].ID} @@ -104,7 +110,10 @@ func testTeamUsers(t *testing.T, ds fleet.Datastore) { assert.ElementsMatch(t, team2Users, team2.Users) } -func testTeamListTeams(t *testing.T, ds fleet.Datastore) { +func TestTeamListTeams(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + users := createTestUsers(t, ds) user1 := fleet.User{Name: users[0].Name, Email: users[0].Email, ID: users[0].ID, GlobalRole: ptr.String(fleet.RoleAdmin)} user2 := fleet.User{Name: users[1].Name, Email: users[1].Email, ID: users[1].ID, GlobalRole: ptr.String(fleet.RoleObserver)} @@ -158,7 +167,10 @@ func testTeamListTeams(t *testing.T, ds fleet.Datastore) { assert.Equal(t, 1, teams[1].UserCount) } -func testTeamSearchTeams(t *testing.T, ds fleet.Datastore) { +func TestTeamSearchTeams(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + team1, err := ds.NewTeam(&fleet.Team{Name: "team1"}) require.NoError(t, err) team2, err := ds.NewTeam(&fleet.Team{Name: "team2"}) diff --git a/server/datastore/mysql/testing_utils.go b/server/datastore/mysql/testing_utils.go index 99befa4fbf..dd42900d3f 100644 --- a/server/datastore/mysql/testing_utils.go +++ b/server/datastore/mysql/testing_utils.go @@ -17,17 +17,12 @@ import ( const ( schemaDbName = "schemadb" - dumpfile = "dump.sql" + dumpfile = "/tmpfs/dump.sql" testUsername = "root" testPassword = "toor" testAddress = "localhost:3307" ) -func init() { - // Initialize the schema once for the entire test run. - initializeSchemaOrPanic() -} - func panicif(err error) { if err != nil { panic(err) @@ -114,7 +109,7 @@ func initializeDatabase(t *testing.T, testName string) *Datastore { func runTest(t *testing.T, testFunc func(*testing.T, fleet.Datastore)) { t.Run(test.FunctionName(testFunc), func(t *testing.T) { - t.Parallel() + //t.Parallel() // Create a new database and load the schema for each test ds := initializeDatabase(t, test.FunctionName(testFunc)) diff --git a/server/datastore/datastore_unicode.go b/server/datastore/mysql/unicode_test.go similarity index 93% rename from server/datastore/datastore_unicode.go rename to server/datastore/mysql/unicode_test.go index 3e702e1244..c2e68e95b0 100644 --- a/server/datastore/datastore_unicode.go +++ b/server/datastore/mysql/unicode_test.go @@ -1,17 +1,21 @@ -package datastore +package mysql import ( - "github.com/fleetdm/fleet/v4/server/ptr" "testing" "time" + "github.com/fleetdm/fleet/v4/server/ptr" + "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func testUnicode(t *testing.T, ds fleet.Datastore) { +func TestUnicode(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + if ds.Name() == "inmem" { t.Skip("inmem is being deprecated, test skipped") } diff --git a/server/datastore/datastore_users.go b/server/datastore/mysql/users_test.go similarity index 93% rename from server/datastore/datastore_users.go rename to server/datastore/mysql/users_test.go index d10b95e0ed..b7625e50dd 100644 --- a/server/datastore/datastore_users.go +++ b/server/datastore/mysql/users_test.go @@ -1,4 +1,4 @@ -package datastore +package mysql import ( "fmt" @@ -11,7 +11,10 @@ import ( "github.com/stretchr/testify/require" ) -func testCreateUser(t *testing.T, ds fleet.Datastore) { +func TestCreateUser(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + var createTests = []struct { password, email string isAdmin, passwordReset, sso bool @@ -41,7 +44,10 @@ func testCreateUser(t *testing.T, ds fleet.Datastore) { } } -func testUserByID(t *testing.T, ds fleet.Datastore) { +func TestUserByID(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + users := createTestUsers(t, ds) for _, tt := range users { returned, err := ds.UserByID(tt.ID) @@ -82,7 +88,10 @@ func createTestUsers(t *testing.T, ds fleet.Datastore) []*fleet.User { return users } -func testSaveUser(t *testing.T, ds fleet.Datastore) { +func TestSaveUser(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + users := createTestUsers(t, ds) testUserGlobalRole(t, ds, users) testEmailAttribute(t, ds, users) @@ -137,7 +146,10 @@ func testUserGlobalRole(t *testing.T, ds fleet.Datastore, users []*fleet.User) { assert.Equal(t, "Cannot specify both Global Role and Team Roles", flErr.Message) } -func testListUsers(t *testing.T, ds fleet.Datastore) { +func TestListUsers(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + createTestUsers(t, ds) users, err := ds.ListUsers(fleet.UserListOptions{}) @@ -155,7 +167,10 @@ func testListUsers(t *testing.T, ds fleet.Datastore) { assert.Equal(t, "mike@fleet.co", users[0].Email) } -func testUserTeams(t *testing.T, ds fleet.Datastore) { +func TestUserTeams(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + for i := 0; i < 10; i++ { _, err := ds.NewTeam(&fleet.Team{Name: fmt.Sprintf("%d", i)}) require.NoError(t, err) @@ -242,7 +257,10 @@ func testUserTeams(t *testing.T, ds fleet.Datastore) { assert.Len(t, users[1].Teams, 0) } -func testUserCreateWithTeams(t *testing.T, ds fleet.Datastore) { +func TestUserCreateWithTeams(t *testing.T) { + ds := CreateMySQLDS(t) + defer ds.Close() + for i := 0; i < 10; i++ { _, err := ds.NewTeam(&fleet.Team{Name: fmt.Sprintf("%d", i)}) require.NoError(t, err)