hako/internal/server/handlers/links_test.go
2026-01-18 09:51:25 +01:00

280 lines
10 KiB
Go

package handlers
import (
"context"
"encoding/json"
"log/slog"
"net/http"
"os"
"testing"
"time"
archivalDomain "git.nakama.town/fmartingr/hako/internal/archival/domain"
authDomain "git.nakama.town/fmartingr/hako/internal/auth/domain"
"git.nakama.town/fmartingr/hako/internal/model"
"git.nakama.town/fmartingr/hako/internal/server/middleware"
"git.nakama.town/fmartingr/hako/internal/testutil"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)
func TestHandleCreateLink_Success(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 logger for tests
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
// Create link handler
linkHandler := NewLinkHandler(deps.Dependencies, logger)
// Create handlers with auth middleware
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuthMiddleware := middleware.RequireAuthMiddleware()
handler := globalAuthMiddleware(requireAuthMiddleware(http.HandlerFunc(linkHandler.HandleCreateLink)))
// Test request
requestBody := `{"url": "https://example.com"}`
w := testutil.PerformRequestWithHandler(handler, http.MethodPost, "/api/v1/links",
testutil.WithAuthToken(token),
testutil.WithBody(requestBody),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusCreated)
// Check response structure
var linkResp model.LinkListItem
err = json.Unmarshal(resp.GetBody(), &linkResp)
require.NoError(t, err)
require.NotEmpty(t, linkResp.ID)
require.Equal(t, "https://example.com", linkResp.URL)
}
func TestHandleCreateLink_MissingURL(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 logger for tests
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
// Create link handler
linkHandler := NewLinkHandler(deps.Dependencies, logger)
// Create handlers with auth middleware
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuthMiddleware := middleware.RequireAuthMiddleware()
handler := globalAuthMiddleware(requireAuthMiddleware(http.HandlerFunc(linkHandler.HandleCreateLink)))
// Test request with empty URL
requestBody := `{"url": ""}`
w := testutil.PerformRequestWithHandler(handler, http.MethodPost, "/api/v1/links",
testutil.WithAuthToken(token),
testutil.WithBody(requestBody),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusBadRequest)
}
func TestHandleCreateLink_Unauthorized(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Create logger for tests
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
// Create link handler
linkHandler := NewLinkHandler(deps.Dependencies, logger)
// Create handlers with auth middleware
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuthMiddleware := middleware.RequireAuthMiddleware()
handler := globalAuthMiddleware(requireAuthMiddleware(http.HandlerFunc(linkHandler.HandleCreateLink)))
// Test request without token
requestBody := `{"url": "https://example.com"}`
w := testutil.PerformRequestWithHandler(handler, http.MethodPost, "/api/v1/links",
testutil.WithBody(requestBody),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusUnauthorized)
}
func TestHandleListLinks_Success(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 logger for tests
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
// Create link handler
linkHandler := NewLinkHandler(deps.Dependencies, logger)
// Create handlers with auth middleware
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuthMiddleware := middleware.RequireAuthMiddleware()
handler := globalAuthMiddleware(requireAuthMiddleware(http.HandlerFunc(linkHandler.HandleListLinks)))
// Test request
w := testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/links",
testutil.WithAuthToken(token),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
// Check response structure
var listResp ListLinksResponse
err = json.Unmarshal(resp.GetBody(), &listResp)
require.NoError(t, err)
require.NotNil(t, listResp.Items)
require.GreaterOrEqual(t, listResp.Total, 0)
}
func TestHandleListLinks_WithSearch(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
user, token, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
require.NoError(t, err)
// Create logger for tests
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
// Create link handler
linkHandler := NewLinkHandler(deps.Dependencies, logger)
// Create handlers with auth middleware
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuthMiddleware := middleware.RequireAuthMiddleware()
handler := globalAuthMiddleware(requireAuthMiddleware(http.HandlerFunc(linkHandler.HandleListLinks)))
// Create a link with a searchable URL
linkDomain := deps.Dependencies.Domains().Links()
link, err := linkDomain.CreateLink(ctx, "https://example.com/searchable", user.ID.String())
require.NoError(t, err)
// Create an archive with a searchable title
now := time.Now()
archive := &model.Archive{
ID: uuid.New().String(),
LinkID: link.ID,
UserID: user.ID.String(),
Status: model.ArchiveStatusCompleted,
Title: "Searchable Title Content",
CreatedAt: now,
}
err = deps.Dependencies.ArchiveStore.Create(ctx, archive)
require.NoError(t, err)
// Create an archive file with searchable content
archiveFile := &model.ArchiveFile{
ID: uuid.New().String(),
ArchiveID: archive.ID,
ArchiverKey: "test",
Filename: "test.txt",
MimeType: "text/plain",
Content: "This is searchable content text",
CreatedAt: now,
}
err = deps.Dependencies.ArchiveFileStore.Create(ctx, archiveFile)
require.NoError(t, err)
// Test search by URL
w := testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/links?q=searchable",
testutil.WithAuthToken(token),
)
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
var listResp ListLinksResponse
err = json.Unmarshal(resp.GetBody(), &listResp)
require.NoError(t, err)
require.GreaterOrEqual(t, len(listResp.Items), 1, "Should find link by URL search")
require.Equal(t, link.ID, listResp.Items[0].ID)
// Test search by title
w = testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/links?q=Title",
testutil.WithAuthToken(token),
)
resp = testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
err = json.Unmarshal(resp.GetBody(), &listResp)
require.NoError(t, err)
require.GreaterOrEqual(t, len(listResp.Items), 1, "Should find link by title search")
// Test that search queries with less than 3 characters are ignored
w = testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/links?q=ab",
testutil.WithAuthToken(token),
)
resp = testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
err = json.Unmarshal(resp.GetBody(), &listResp)
require.NoError(t, err)
// Should return all links (no search filter applied)
require.GreaterOrEqual(t, len(listResp.Items), 1, "Should return all links when query is too short")
// Test search by content
w = testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/links?q=content",
testutil.WithAuthToken(token),
)
resp = testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
err = json.Unmarshal(resp.GetBody(), &listResp)
require.NoError(t, err)
require.GreaterOrEqual(t, len(listResp.Items), 1, "Should find link by content search")
// Test search with no results
w = testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/links?q=nonexistentterm12345",
testutil.WithAuthToken(token),
)
resp = testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
err = json.Unmarshal(resp.GetBody(), &listResp)
require.NoError(t, err)
require.Equal(t, 0, len(listResp.Items), "Should return no results for nonexistent term")
}