memos/server/router/api/v1/test/test_helper.go

125 lines
4.1 KiB
Go

package test
import (
"context"
"testing"
"github.com/usememos/memos/internal/markdown"
"github.com/usememos/memos/internal/profile"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/server/auth"
apiv1 "github.com/usememos/memos/server/router/api/v1"
"github.com/usememos/memos/store"
teststore "github.com/usememos/memos/store/test"
)
// TestService holds the test service setup for API v1 services.
type TestService struct {
Service *apiv1.APIV1Service
Store *store.Store
Profile *profile.Profile
Secret string
}
// NewTestService creates a new test service with SQLite database.
func NewTestService(t *testing.T) *TestService {
ctx := context.Background()
// Create a test store with SQLite
testStore := teststore.NewTestingStore(ctx, t)
if _, err := testStore.UpsertInstanceSetting(ctx, &storepb.InstanceSetting{
Key: storepb.InstanceSettingKey_ACCESS,
Value: &storepb.InstanceSetting_AccessSetting{AccessSetting: &storepb.InstanceAccessSetting{
AccessMode: storepb.InstanceAccessMode_INSTANCE_ACCESS_MODE_PUBLIC,
}},
}); err != nil {
t.Fatalf("failed to configure public access for API test service: %v", err)
}
// Align the profile data directory with the test store so attachment files and
// derived caches resolve against the same location as DeleteAttachmentStorage.
testProfile := &profile.Profile{
Demo: true,
Version: "test-1.0.0",
Commit: "test-commit",
InstanceURL: "http://localhost:8080",
Driver: "sqlite",
DSN: ":memory:",
Data: testStore.GetDataDir(),
}
// Create APIV1Service with nil grpcServer since we're testing direct calls
secret := "test-secret"
markdownService := markdown.NewService(
markdown.WithTagExtension(),
markdown.WithMentionExtension(),
)
service := &apiv1.APIV1Service{
Secret: secret,
Profile: testProfile,
Store: testStore,
MarkdownService: markdownService,
SSEHub: apiv1.NewSSEHub(),
}
return &TestService{
Service: service,
Store: testStore,
Profile: testProfile,
Secret: secret,
}
}
// SetInstanceAccessMode updates the instance access policy for a test.
func (ts *TestService) SetInstanceAccessMode(ctx context.Context, mode storepb.InstanceAccessMode) error {
_, err := ts.Store.UpsertInstanceSetting(ctx, &storepb.InstanceSetting{
Key: storepb.InstanceSettingKey_ACCESS,
Value: &storepb.InstanceSetting_AccessSetting{AccessSetting: &storepb.InstanceAccessSetting{
AccessMode: mode,
}},
})
return err
}
// Cleanup closes resources after test.
func (ts *TestService) Cleanup() {
ts.Store.Close()
}
// CreateHostUser creates an admin user for testing.
func (ts *TestService) CreateHostUser(ctx context.Context, username string) (*store.User, error) {
return ts.Store.CreateUser(ctx, &store.User{
Username: username,
Role: store.RoleAdmin,
Email: username + "@example.com",
})
}
// CreateRegularUser creates a regular user for testing.
func (ts *TestService) CreateRegularUser(ctx context.Context, username string) (*store.User, error) {
return ts.Store.CreateUser(ctx, &store.User{
Username: username,
Role: store.RoleUser,
Email: username + "@example.com",
})
}
// InviteAndAcceptSpaceMember is a test fixture helper that exercises the invitation
// lifecycle instead of bypassing invitee consent.
func (ts *TestService) InviteAndAcceptSpaceMember(ctx context.Context, create *store.SpaceMember, actorUserID int32) (*store.SpaceMember, error) {
invitation, err := ts.Store.CreateSpaceInvitation(ctx, &store.SpaceInvitation{
SpaceID: create.SpaceID,
UserID: create.UserID,
Role: create.Role,
}, actorUserID)
if err != nil {
return nil, err
}
return ts.Store.AcceptSpaceInvitation(ctx, &store.AcceptSpaceInvitation{SpaceID: invitation.SpaceID, UserID: invitation.UserID}, invitation.UserID)
}
// CreateUserContext creates a context with the given user's ID for authentication.
func (*TestService) CreateUserContext(ctx context.Context, userID int32) context.Context {
// Use the context key from the auth package
return context.WithValue(ctx, auth.UserIDContextKey, userID)
}