fleet/server/activity/internal/testutils/testutils.go

135 lines
4.3 KiB
Go

// Package testutils provides shared test utilities for the activity bounded context.
package testutils
import (
"encoding/json"
"log/slog"
"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/jmoiron/sqlx"
"github.com/stretchr/testify/require"
)
// TestDB holds the database connection for tests.
type TestDB struct {
DB *sqlx.DB
Logger *slog.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: slog.New(slog.DiscardHandler),
}
}
// 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, "activity_host_past", "activity_past", "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) //nolint:gosec // dismiss G115
}
// 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 activity_past (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) //nolint:gosec // dismiss G115
}
// 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) //nolint:gosec // dismiss G115
}
// InsertHostActivity creates a link between a host and an activity in the activity_host_past join table.
func (tdb *TestDB) InsertHostActivity(t *testing.T, hostID, activityID uint) {
t.Helper()
ctx := t.Context()
_, err := tdb.DB.ExecContext(ctx, `
INSERT INTO activity_host_past (host_id, activity_id) VALUES (?, ?)
`, hostID, activityID)
require.NoError(t, err)
}