readysite / website / controllers / testhelper_test.go
4.7 KB
testhelper_test.go
package controllers

import (
	"embed"
	"io/fs"
	"net/http"
	"sync"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"github.com/readysite/readysite/pkg/application"
	"github.com/readysite/readysite/pkg/database"
	"github.com/readysite/readysite/pkg/database/engines"
	"github.com/readysite/readysite/website/internal/access"
	"github.com/readysite/readysite/website/models"
)

var (
	//go:embed testdata/views
	testViewsRaw embed.FS

	testViews, _ = fs.Sub(testViewsRaw, "testdata")

	setupOnce sync.Once
	testApp   *application.App
	testAdmin *models.User
)

// setupTestDB initializes an in-memory database for tests.
// This should be called in init() of test files to ensure the memory DB
// is used instead of the file-based DB from models/db.go.
func setupTestDB() {
	setupOnce.Do(func() {
		// Replace the file-based DB with an in-memory DB for tests
		models.DB = engines.NewMemory()

		// Reinitialize ALL collections to use the new DB with indexes
		models.Users = database.Manage(models.DB, new(models.User),
			database.WithUniqueIndex[models.User]("Email"),
		)
		models.Pages = database.Manage(models.DB, new(models.Page),
			database.WithIndex[models.Page]("ParentID"),
			database.WithIndex[models.Page]("Published"),
		)
		models.PageContents = database.Manage(models.DB, new(models.PageContent),
			database.WithIndex[models.PageContent]("PageID"),
			database.WithIndex[models.PageContent]("CreatedBy"),
		)
		models.Collections = database.Manage(models.DB, new(models.Collection))
		models.Documents = database.Manage(models.DB, new(models.Document),
			database.WithIndex[models.Document]("CollectionID"),
		)
		models.Conversations = database.Manage(models.DB, new(models.Conversation),
			database.WithIndex[models.Conversation]("UserID"),
		)
		models.Messages = database.Manage(models.DB, new(models.Message),
			database.WithIndex[models.Message]("ConversationID"),
		)
		models.Mutations = database.Manage(models.DB, new(models.Mutation),
			database.WithIndex[models.Mutation]("MessageID"),
			database.WithIndex[models.Mutation]("EntityType", "EntityID"),
		)
		models.ACLRules = database.Manage(models.DB, new(models.ACLRule),
			database.WithIndex[models.ACLRule]("SubjectType", "SubjectID"),
			database.WithIndex[models.ACLRule]("ResourceType", "ResourceID"),
		)
		models.SettingsStore = database.Manage(models.DB, new(models.Settings),
			database.WithUniqueIndex[models.Settings]("Key"),
		)
		models.Files = database.Manage(models.DB, new(models.File),
			database.WithIndex[models.File]("UserID"),
			database.WithIndex[models.File]("MimeType"),
			database.WithIndex[models.File]("Published"),
		)
		models.MessageFiles = database.Manage(models.DB, new(models.MessageFile),
			database.WithIndex[models.MessageFile]("MessageID"),
			database.WithIndex[models.MessageFile]("FileID"),
		)
		models.AuditLogs = database.Manage(models.DB, new(models.AuditLog),
			database.WithIndex[models.AuditLog]("UserID"),
			database.WithIndex[models.AuditLog]("ResourceType", "ResourceID"),
			database.WithIndex[models.AuditLog]("Action"),
		)
		models.Partials = database.Manage(models.DB, new(models.Partial),
			database.WithIndex[models.Partial]("Published"),
		)

		// Create test admin user
		testAdmin = &models.User{
			Email: "admin@test.com",
			Name:  "Test Admin",
			Role:  access.RoleAdmin,
		}
		testAdmin.ID = "test-admin-user"
		models.Users.Insert(testAdmin)

		// Create test App with embedded views for controller tests
		testApp = application.New(
			application.WithViews(testViews),
			application.WithController(Chat()),
			application.WithController(Collections()),
		)
	})
}

// testChatController returns a ChatController configured with the test app.
// Use this instead of Chat() in tests that call rendering methods.
func testChatController() *ChatController {
	c := &ChatController{}
	c.BaseController.Setup(testApp)
	return c
}

// testCollectionsController returns a CollectionsController configured with the test app.
func testCollectionsController() *CollectionsController {
	c := &CollectionsController{}
	c.BaseController.Setup(testApp)
	return c
}

// authenticateRequest adds an admin session cookie to the request.
// This allows tests to pass authentication and authorization checks.
func authenticateRequest(req *http.Request) {
	// Create JWT token for test admin
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"user_id": testAdmin.ID,
		"exp":     time.Now().Add(24 * time.Hour).Unix(),
	})

	// Use the same default secret as the access package in dev mode
	tokenString, _ := token.SignedString([]byte("dev-only-not-for-production"))

	// Add session cookie to request
	req.AddCookie(&http.Cookie{
		Name:  "session",
		Value: tokenString,
	})
}

func init() {
	setupTestDB()
}
← Back