mirror of
https://github.com/fleetdm/fleet
synced 2026-05-15 04:58:25 +00:00
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #38534 moved `/api/_version_/fleet/hosts/{id:[0-9]+}/activities` endpoint and `MarkActivitiesAsStreamed` to activity bounded context # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. ## Testing - [x] Added/updated automated tests - [x] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added new endpoint to retrieve host-specific past activities with pagination metadata. * **Refactor** * Refactored activity service architecture and authorization layer to improve data provider integration and activity streaming capabilities. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
135 lines
4.2 KiB
Go
135 lines
4.2 KiB
Go
// Package testutils provides shared test utilities for the activity bounded context.
|
|
package testutils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
common_mysql "github.com/fleetdm/fleet/v4/server/platform/mysql"
|
|
mysql_testing_utils "github.com/fleetdm/fleet/v4/server/platform/mysql/testing_utils"
|
|
"github.com/go-kit/log"
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestDB holds the database connection for tests.
|
|
type TestDB struct {
|
|
DB *sqlx.DB
|
|
Logger log.Logger
|
|
}
|
|
|
|
// SetupTestDB creates a test database with the Fleet schema loaded.
|
|
func SetupTestDB(t *testing.T, testNamePrefix string) *TestDB {
|
|
t.Helper()
|
|
|
|
testName, opts := mysql_testing_utils.ProcessOptions(t, &mysql_testing_utils.DatastoreTestOptions{
|
|
UniqueTestName: testNamePrefix + "_" + t.Name(),
|
|
})
|
|
|
|
mysql_testing_utils.LoadDefaultSchema(t, testName, opts)
|
|
config := mysql_testing_utils.MysqlTestConfig(testName)
|
|
db, err := common_mysql.NewDB(config, &common_mysql.DBOptions{}, "")
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() { db.Close() })
|
|
|
|
return &TestDB{
|
|
DB: db,
|
|
Logger: log.NewNopLogger(),
|
|
}
|
|
}
|
|
|
|
// Conns returns DBConnections for creating a datastore.
|
|
func (tdb *TestDB) Conns() *common_mysql.DBConnections {
|
|
return &common_mysql.DBConnections{Primary: tdb.DB, Replica: tdb.DB}
|
|
}
|
|
|
|
// TruncateTables clears the tables used by activity bounded context.
|
|
func (tdb *TestDB) TruncateTables(t *testing.T) {
|
|
t.Helper()
|
|
mysql_testing_utils.TruncateTables(t, tdb.DB, tdb.Logger, nil, "host_activities", "activities", "hosts", "users")
|
|
}
|
|
|
|
// InsertUser creates a user in the database and returns the user ID.
|
|
func (tdb *TestDB) InsertUser(t *testing.T, name, email string) uint {
|
|
t.Helper()
|
|
ctx := t.Context()
|
|
|
|
result, err := tdb.DB.ExecContext(ctx, `
|
|
INSERT INTO users (name, email, password, salt, created_at, updated_at)
|
|
VALUES (?, ?, 'password', 'salt', NOW(), NOW())
|
|
`, name, email)
|
|
require.NoError(t, err)
|
|
|
|
id, err := result.LastInsertId()
|
|
require.NoError(t, err)
|
|
return uint(id)
|
|
}
|
|
|
|
// InsertActivity creates an activity in the database and returns the activity ID.
|
|
// Pass nil for userID to create a system activity (no associated user).
|
|
func (tdb *TestDB) InsertActivity(t *testing.T, userID *uint, activityType string, details map[string]any) uint {
|
|
t.Helper()
|
|
return tdb.InsertActivityWithTime(t, userID, activityType, details, time.Now().UTC())
|
|
}
|
|
|
|
// InsertActivityWithTime creates an activity with a specific timestamp.
|
|
// Pass nil for userID to create a system activity (no associated user).
|
|
func (tdb *TestDB) InsertActivityWithTime(t *testing.T, userID *uint, activityType string, details map[string]any, createdAt time.Time) uint {
|
|
t.Helper()
|
|
ctx := t.Context()
|
|
|
|
detailsJSON, err := json.Marshal(details)
|
|
require.NoError(t, err)
|
|
|
|
var userName *string
|
|
var userEmail string // NOT NULL DEFAULT '' in schema
|
|
if userID != nil {
|
|
var user struct {
|
|
Name string `db:"name"`
|
|
Email string `db:"email"`
|
|
}
|
|
err = sqlx.GetContext(ctx, tdb.DB, &user, "SELECT name, email FROM users WHERE id = ?", *userID)
|
|
require.NoError(t, err)
|
|
userName = &user.Name
|
|
userEmail = user.Email
|
|
}
|
|
|
|
result, err := tdb.DB.ExecContext(ctx, `
|
|
INSERT INTO activities (user_id, user_name, user_email, activity_type, details, created_at, host_only, streamed)
|
|
VALUES (?, ?, ?, ?, ?, ?, false, false)
|
|
`, userID, userName, userEmail, activityType, detailsJSON, createdAt)
|
|
require.NoError(t, err)
|
|
|
|
id, err := result.LastInsertId()
|
|
require.NoError(t, err)
|
|
return uint(id)
|
|
}
|
|
|
|
// InsertHost creates a host in the database and returns the host ID.
|
|
func (tdb *TestDB) InsertHost(t *testing.T, hostname string, teamID *uint) uint {
|
|
t.Helper()
|
|
ctx := t.Context()
|
|
|
|
result, err := tdb.DB.ExecContext(ctx, `
|
|
INSERT INTO hosts (hostname, team_id, created_at, updated_at)
|
|
VALUES (?, ?, NOW(), NOW())
|
|
`, hostname, teamID)
|
|
require.NoError(t, err)
|
|
|
|
id, err := result.LastInsertId()
|
|
require.NoError(t, err)
|
|
return uint(id)
|
|
}
|
|
|
|
// InsertHostActivity creates a link between a host and an activity in the host_activities junction table.
|
|
func (tdb *TestDB) InsertHostActivity(t *testing.T, hostID, activityID uint) {
|
|
t.Helper()
|
|
ctx := t.Context()
|
|
|
|
_, err := tdb.DB.ExecContext(ctx, `
|
|
INSERT INTO host_activities (host_id, activity_id) VALUES (?, ?)
|
|
`, hostID, activityID)
|
|
require.NoError(t, err)
|
|
}
|