406 lines
15 KiB
Go
406 lines
15 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"testing"
|
|
|
|
"git.nakama.town/fmartingr/hako/internal/archival/archiver"
|
|
archivalDomain "git.nakama.town/fmartingr/hako/internal/archival/domain"
|
|
archivalStore "git.nakama.town/fmartingr/hako/internal/archival/store"
|
|
authDomain "git.nakama.town/fmartingr/hako/internal/auth/domain"
|
|
"git.nakama.town/fmartingr/hako/internal/database"
|
|
"git.nakama.town/fmartingr/hako/internal/extractors"
|
|
"git.nakama.town/fmartingr/hako/internal/model"
|
|
"git.nakama.town/fmartingr/hako/internal/server/middleware"
|
|
"git.nakama.town/fmartingr/hako/internal/testutil"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestHandleSystem_NotLoggedIn(t *testing.T) {
|
|
// Create temporary database for extractor config store
|
|
tmpDB, err := os.CreateTemp("", "hako_test_*.db")
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = tmpDB.Close()
|
|
_ = os.Remove(tmpDB.Name())
|
|
_ = os.Remove(tmpDB.Name() + "-wal")
|
|
_ = os.Remove(tmpDB.Name() + "-shm")
|
|
}()
|
|
|
|
dbURL := "sqlite:" + tmpDB.Name()
|
|
dbConnections, err := database.NewConnections(dbURL, dbURL)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = dbConnections.Close()
|
|
}()
|
|
|
|
// Run migrations
|
|
err = database.InitSchema(dbConnections)
|
|
require.NoError(t, err)
|
|
|
|
// Create extractor manager
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
extractorMgr := extractors.NewManager(logger)
|
|
|
|
// Create archiver manager
|
|
archiverMgr := archiver.NewManager()
|
|
|
|
// Create archiver config store
|
|
archiverConfigStore := archivalStore.NewArchiverConfigStore(dbConnections.Read, dbConnections.Write)
|
|
|
|
// Create settings store
|
|
settingsStore := archivalStore.NewSettingsStore(dbConnections.Read, dbConnections.Write)
|
|
|
|
// Create system handler
|
|
systemHandler := NewSystemHandler(func(ctx context.Context) (string, error) {
|
|
return model.BuildVersion, nil
|
|
}, extractorMgr, archiverMgr, archiverConfigStore, settingsStore)
|
|
|
|
// Perform request without authentication
|
|
w := testutil.PerformRequest(systemHandler.HandleSystem, http.MethodGet, "/system")
|
|
|
|
// Assert response
|
|
resp := testutil.NewTestResponse(w)
|
|
resp.AssertStatus(t, http.StatusOK)
|
|
|
|
// Check JSON structure contains version, commit, and date
|
|
var jsonData map[string]any
|
|
err = json.Unmarshal(resp.GetBody(), &jsonData)
|
|
require.NoError(t, err, "Response should be valid JSON")
|
|
|
|
// Check that version, commit, and date keys exist
|
|
versionValue, ok := jsonData["version"]
|
|
require.True(t, ok, "Response should contain 'version' key")
|
|
require.NotEmpty(t, versionValue, "Version should not be empty")
|
|
|
|
commitValue, ok := jsonData["commit"]
|
|
require.True(t, ok, "Response should contain 'commit' key")
|
|
|
|
dateValue, ok := jsonData["date"]
|
|
require.True(t, ok, "Response should contain 'date' key")
|
|
|
|
// Verify it's the hako application version (defaults to "dev" in tests)
|
|
require.Equal(t, model.BuildVersion, versionValue)
|
|
require.Equal(t, model.BuildCommit, commitValue)
|
|
require.Equal(t, model.BuildDate, dateValue)
|
|
}
|
|
|
|
func TestHandleSystem_LoggedIn(t *testing.T) {
|
|
ctx := context.Background()
|
|
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
|
|
|
|
// Initialize domains
|
|
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
|
|
|
|
// Create a test user and get token
|
|
_, token, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
|
|
require.NoError(t, err)
|
|
|
|
// Create extractor manager
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
extractorMgr := extractors.NewManager(logger)
|
|
|
|
// Create archiver manager
|
|
archiverMgr := archiver.NewManager()
|
|
|
|
// Create archiver config store using database connections from deps
|
|
archiverConfigStore := archivalStore.NewArchiverConfigStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create settings store
|
|
settingsStore := archivalStore.NewSettingsStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create system handler
|
|
systemHandler := NewSystemHandler(func(ctx context.Context) (string, error) {
|
|
return model.BuildVersion, nil
|
|
}, extractorMgr, archiverMgr, archiverConfigStore, settingsStore)
|
|
|
|
// Create handler with global auth middleware (sets context if token is present)
|
|
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
|
|
handler := globalAuthMiddleware(http.HandlerFunc(systemHandler.HandleSystem))
|
|
|
|
// Perform authenticated request
|
|
w := testutil.PerformRequestWithHandler(handler, http.MethodGet, "/system",
|
|
testutil.WithAuthToken(token),
|
|
)
|
|
|
|
// Assert response
|
|
resp := testutil.NewTestResponse(w)
|
|
resp.AssertStatus(t, http.StatusOK)
|
|
|
|
// Check JSON structure contains version, commit, and date
|
|
var jsonData map[string]any
|
|
err = json.Unmarshal(resp.GetBody(), &jsonData)
|
|
require.NoError(t, err, "Response should be valid JSON")
|
|
|
|
// Check that version, commit, and date keys exist
|
|
versionValue, ok := jsonData["version"]
|
|
require.True(t, ok, "Response should contain 'version' key when logged in")
|
|
require.NotEmpty(t, versionValue, "Version should not be empty")
|
|
|
|
commitValue, ok := jsonData["commit"]
|
|
require.True(t, ok, "Response should contain 'commit' key when logged in")
|
|
|
|
dateValue, ok := jsonData["date"]
|
|
require.True(t, ok, "Response should contain 'date' key when logged in")
|
|
|
|
// Verify it's the hako application version (defaults to "dev" in tests)
|
|
require.Equal(t, model.BuildVersion, versionValue)
|
|
require.Equal(t, model.BuildCommit, commitValue)
|
|
require.Equal(t, model.BuildDate, dateValue)
|
|
}
|
|
|
|
func TestHandleGetRulesConfig(t *testing.T) {
|
|
ctx := context.Background()
|
|
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
|
|
|
|
// Initialize domains
|
|
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
|
|
|
|
// Create a test user and get token
|
|
_, token, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
|
|
require.NoError(t, err)
|
|
|
|
// Create extractor manager
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
extractorMgr := extractors.NewManager(logger)
|
|
|
|
// Create archiver manager
|
|
archiverMgr := archiver.NewManager()
|
|
|
|
// Create archiver config store
|
|
archiverConfigStore := archivalStore.NewArchiverConfigStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create settings store
|
|
settingsStore := archivalStore.NewSettingsStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create system handler
|
|
systemHandler := NewSystemHandler(func(ctx context.Context) (string, error) {
|
|
return model.BuildVersion, nil
|
|
}, extractorMgr, archiverMgr, archiverConfigStore, settingsStore)
|
|
|
|
// Create handler with global auth middleware
|
|
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
|
|
handler := globalAuthMiddleware(http.HandlerFunc(systemHandler.HandleGetRulesConfig))
|
|
|
|
// Perform authenticated request
|
|
w := testutil.PerformRequestWithHandler(handler, http.MethodGet, "/system/rules/config",
|
|
testutil.WithAuthToken(token),
|
|
)
|
|
|
|
// Assert response
|
|
resp := testutil.NewTestResponse(w)
|
|
resp.AssertStatus(t, http.StatusOK)
|
|
|
|
// Check JSON structure
|
|
var jsonData map[string]any
|
|
err = json.Unmarshal(resp.GetBody(), &jsonData)
|
|
require.NoError(t, err, "Response should be valid JSON")
|
|
|
|
// Check that rules and default_extractors keys exist
|
|
_, ok := jsonData["rules"]
|
|
require.True(t, ok, "Response should contain 'rules' key")
|
|
_, ok = jsonData["default_extractors"]
|
|
require.True(t, ok, "Response should contain 'default_extractors' key")
|
|
}
|
|
|
|
func TestHandleSaveRulesConfig(t *testing.T) {
|
|
ctx := context.Background()
|
|
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
|
|
|
|
// Initialize domains
|
|
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
|
|
|
|
// Create a test user and get token
|
|
_, token, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
|
|
require.NoError(t, err)
|
|
|
|
// Create extractor manager
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
extractorMgr := extractors.NewManager(logger)
|
|
|
|
// Create archiver manager
|
|
archiverMgr := archiver.NewManager()
|
|
|
|
// Create archiver config store
|
|
archiverConfigStore := archivalStore.NewArchiverConfigStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create settings store
|
|
settingsStore := archivalStore.NewSettingsStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create system handler
|
|
systemHandler := NewSystemHandler(func(ctx context.Context) (string, error) {
|
|
return model.BuildVersion, nil
|
|
}, extractorMgr, archiverMgr, archiverConfigStore, settingsStore)
|
|
|
|
// Create handler with global auth middleware
|
|
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
|
|
handler := globalAuthMiddleware(http.HandlerFunc(systemHandler.HandleSaveRulesConfig))
|
|
|
|
// Valid rules config
|
|
validRulesConfig := `{
|
|
"rules": [
|
|
{
|
|
"mimetype": "text/html",
|
|
"extractors": [{"key": "obelisk"}]
|
|
}
|
|
],
|
|
"default_extractors": [{"key": "obelisk"}, {"key": "thumbnail"}]
|
|
}`
|
|
|
|
// Perform authenticated request to save rules
|
|
w := testutil.PerformRequestWithHandler(handler, http.MethodPut, "/system/rules/config",
|
|
testutil.WithAuthToken(token),
|
|
testutil.WithBody(validRulesConfig),
|
|
testutil.WithHeader("Content-Type", "application/json"),
|
|
)
|
|
|
|
// Assert response
|
|
resp := testutil.NewTestResponse(w)
|
|
resp.AssertStatus(t, http.StatusOK)
|
|
|
|
// Check response message
|
|
var responseData map[string]string
|
|
err = json.Unmarshal(resp.GetBody(), &responseData)
|
|
require.NoError(t, err, "Response should be valid JSON")
|
|
require.Equal(t, "Rules config saved successfully", responseData["message"])
|
|
|
|
// Verify rules were saved by getting them back
|
|
getHandler := globalAuthMiddleware(http.HandlerFunc(systemHandler.HandleGetRulesConfig))
|
|
w = testutil.PerformRequestWithHandler(getHandler, http.MethodGet, "/system/rules/config",
|
|
testutil.WithAuthToken(token),
|
|
)
|
|
|
|
resp = testutil.NewTestResponse(w)
|
|
resp.AssertStatus(t, http.StatusOK)
|
|
|
|
var savedConfig map[string]any
|
|
err = json.Unmarshal(resp.GetBody(), &savedConfig)
|
|
require.NoError(t, err)
|
|
|
|
// Verify rules were saved
|
|
rules, ok := savedConfig["rules"].([]any)
|
|
require.True(t, ok, "Rules should be an array")
|
|
require.Len(t, rules, 1, "Should have one rule")
|
|
}
|
|
|
|
func TestHandleSaveRulesConfig_InvalidJSON(t *testing.T) {
|
|
ctx := context.Background()
|
|
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
|
|
|
|
// Initialize domains
|
|
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
|
|
|
|
// Create a test user and get token
|
|
_, token, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
|
|
require.NoError(t, err)
|
|
|
|
// Create extractor manager
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
extractorMgr := extractors.NewManager(logger)
|
|
|
|
// Create archiver manager
|
|
archiverMgr := archiver.NewManager()
|
|
|
|
// Create archiver config store
|
|
archiverConfigStore := archivalStore.NewArchiverConfigStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create settings store
|
|
settingsStore := archivalStore.NewSettingsStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create system handler
|
|
systemHandler := NewSystemHandler(func(ctx context.Context) (string, error) {
|
|
return model.BuildVersion, nil
|
|
}, extractorMgr, archiverMgr, archiverConfigStore, settingsStore)
|
|
|
|
// Create handler with global auth middleware
|
|
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
|
|
handler := globalAuthMiddleware(http.HandlerFunc(systemHandler.HandleSaveRulesConfig))
|
|
|
|
// Invalid JSON
|
|
invalidJSON := `{invalid json}`
|
|
|
|
// Perform authenticated request
|
|
w := testutil.PerformRequestWithHandler(handler, http.MethodPut, "/system/rules/config",
|
|
testutil.WithAuthToken(token),
|
|
testutil.WithBody(invalidJSON),
|
|
testutil.WithHeader("Content-Type", "application/json"),
|
|
)
|
|
|
|
// Assert response
|
|
resp := testutil.NewTestResponse(w)
|
|
resp.AssertStatus(t, http.StatusBadRequest)
|
|
}
|
|
|
|
func TestHandleSaveRulesConfig_InvalidRuleStructure(t *testing.T) {
|
|
ctx := context.Background()
|
|
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
|
|
|
|
// Initialize domains
|
|
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
|
|
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
|
|
|
|
// Create a test user and get token
|
|
_, token, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
|
|
require.NoError(t, err)
|
|
|
|
// Create extractor manager
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
extractorMgr := extractors.NewManager(logger)
|
|
|
|
// Create archiver manager
|
|
archiverMgr := archiver.NewManager()
|
|
|
|
// Create archiver config store
|
|
archiverConfigStore := archivalStore.NewArchiverConfigStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create settings store
|
|
settingsStore := archivalStore.NewSettingsStore(deps.DBConnections.Read, deps.DBConnections.Write)
|
|
|
|
// Create system handler
|
|
systemHandler := NewSystemHandler(func(ctx context.Context) (string, error) {
|
|
return model.BuildVersion, nil
|
|
}, extractorMgr, archiverMgr, archiverConfigStore, settingsStore)
|
|
|
|
// Create handler with global auth middleware
|
|
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
|
|
handler := globalAuthMiddleware(http.HandlerFunc(systemHandler.HandleSaveRulesConfig))
|
|
|
|
// Valid JSON but invalid rule structure (missing required fields)
|
|
invalidRuleConfig := `{
|
|
"rules": [
|
|
{
|
|
"mimetype": ""
|
|
}
|
|
],
|
|
"default_extractors": []
|
|
}`
|
|
|
|
// Perform authenticated request
|
|
w := testutil.PerformRequestWithHandler(handler, http.MethodPut, "/system/rules/config",
|
|
testutil.WithAuthToken(token),
|
|
testutil.WithBody(invalidRuleConfig),
|
|
testutil.WithHeader("Content-Type", "application/json"),
|
|
)
|
|
|
|
// Assert response - should fail validation
|
|
resp := testutil.NewTestResponse(w)
|
|
resp.AssertStatus(t, http.StatusBadRequest)
|
|
}
|