272 lines
7.6 KiB
Go
272 lines
7.6 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
authStore "git.nakama.town/fmartingr/hako/internal/auth/store"
|
|
"git.nakama.town/fmartingr/hako/internal/database"
|
|
"git.nakama.town/fmartingr/hako/internal/model"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func setupTestArchiveStore(t *testing.T) (*ArchiveStore, string, string, func()) {
|
|
t.Helper()
|
|
|
|
// Create a temporary database file
|
|
tmpDB, err := os.CreateTemp("", "test_archive_store_*.db")
|
|
require.NoError(t, err)
|
|
_ = tmpDB.Close()
|
|
dbURL := "sqlite:" + tmpDB.Name()
|
|
|
|
// Create database connections
|
|
conns, err := database.NewConnections(dbURL, dbURL)
|
|
require.NoError(t, err)
|
|
|
|
// Run migrations
|
|
err = database.InitSchema(conns)
|
|
require.NoError(t, err)
|
|
|
|
// Create stores
|
|
userStore := authStore.NewUserStore(conns.Read, conns.Write)
|
|
linkStore := NewLinkStore(conns.Read, conns.Write)
|
|
archiveStore := NewArchiveStore(conns.Read, conns.Write)
|
|
|
|
// Create a test user
|
|
ctx := context.Background()
|
|
userUUID := uuid.New()
|
|
user := &authStore.User{
|
|
ID: userUUID,
|
|
Email: "test@example.com",
|
|
PasswordHash: "test-hash",
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
err = userStore.Create(user)
|
|
require.NoError(t, err)
|
|
|
|
// Create a test link
|
|
userID := userUUID.String()
|
|
linkID := uuid.New().String()
|
|
link := &model.Link{
|
|
ID: linkID,
|
|
URL: "https://example.com",
|
|
UserID: userID,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
err = linkStore.Create(ctx, link)
|
|
require.NoError(t, err)
|
|
|
|
cleanup := func() {
|
|
_ = conns.Close()
|
|
_ = os.Remove(tmpDB.Name())
|
|
_ = os.Remove(tmpDB.Name() + "-wal")
|
|
_ = os.Remove(tmpDB.Name() + "-shm")
|
|
}
|
|
|
|
return archiveStore, userID, linkID, cleanup
|
|
}
|
|
|
|
func TestArchiveStore_Create(t *testing.T) {
|
|
store, userID, linkID, cleanup := setupTestArchiveStore(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
archive := &model.Archive{
|
|
ID: uuid.New().String(),
|
|
LinkID: linkID,
|
|
UserID: userID,
|
|
Status: model.ArchiveStatusPending,
|
|
ErrorMessage: "",
|
|
CreatedAt: now,
|
|
}
|
|
|
|
err := store.Create(ctx, archive)
|
|
require.NoError(t, err)
|
|
|
|
// Verify it was created
|
|
retrieved, err := store.GetByID(ctx, archive.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, archive.ID, retrieved.ID)
|
|
require.Equal(t, archive.LinkID, retrieved.LinkID)
|
|
require.Equal(t, archive.UserID, retrieved.UserID)
|
|
require.Equal(t, archive.Status, retrieved.Status)
|
|
}
|
|
|
|
func TestArchiveStore_UpdateStatus_ToCompleted(t *testing.T) {
|
|
store, userID, linkID, cleanup := setupTestArchiveStore(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
// Create an archive in pending status
|
|
archive := &model.Archive{
|
|
ID: uuid.New().String(),
|
|
LinkID: linkID,
|
|
UserID: userID,
|
|
Status: model.ArchiveStatusPending,
|
|
ErrorMessage: "",
|
|
CreatedAt: now,
|
|
}
|
|
|
|
err := store.Create(ctx, archive)
|
|
require.NoError(t, err)
|
|
|
|
// Update to processing
|
|
err = store.UpdateStatus(ctx, archive.ID, model.ArchiveStatusProcessing, "")
|
|
require.NoError(t, err)
|
|
|
|
// Verify processing status
|
|
retrieved, err := store.GetByID(ctx, archive.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, model.ArchiveStatusProcessing, retrieved.Status)
|
|
require.Empty(t, retrieved.ErrorMessage)
|
|
require.Nil(t, retrieved.CompletedAt, "CompletedAt should be nil for processing status")
|
|
|
|
// Update to completed
|
|
err = store.UpdateStatus(ctx, archive.ID, model.ArchiveStatusCompleted, "")
|
|
require.NoError(t, err)
|
|
|
|
// Verify completed status - THIS IS THE KEY TEST THAT WOULD HAVE CAUGHT THE BUG
|
|
retrieved, err = store.GetByID(ctx, archive.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, model.ArchiveStatusCompleted, retrieved.Status, "Status should be completed")
|
|
require.Empty(t, retrieved.ErrorMessage, "Error message should be empty")
|
|
require.NotNil(t, retrieved.CompletedAt, "CompletedAt should be set for completed status")
|
|
|
|
// Verify the completed_at timestamp is recent (within last 2 hours to account for timezone differences)
|
|
// The key point is that it's set at all, not the exact timestamp
|
|
require.WithinDuration(t, time.Now().UTC(), retrieved.CompletedAt.UTC(), 2*time.Hour)
|
|
}
|
|
|
|
func TestArchiveStore_UpdateStatus_ToFailed(t *testing.T) {
|
|
store, userID, linkID, cleanup := setupTestArchiveStore(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
// Create an archive
|
|
archive := &model.Archive{
|
|
ID: uuid.New().String(),
|
|
LinkID: linkID,
|
|
UserID: userID,
|
|
Status: model.ArchiveStatusPending,
|
|
ErrorMessage: "",
|
|
CreatedAt: now,
|
|
}
|
|
|
|
err := store.Create(ctx, archive)
|
|
require.NoError(t, err)
|
|
|
|
// Update to failed with error message
|
|
errorMsg := "extractor not found: test_extractor"
|
|
err = store.UpdateStatus(ctx, archive.ID, model.ArchiveStatusFailed, errorMsg)
|
|
require.NoError(t, err)
|
|
|
|
// Verify failed status
|
|
retrieved, err := store.GetByID(ctx, archive.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, model.ArchiveStatusFailed, retrieved.Status, "Status should be failed")
|
|
require.Equal(t, errorMsg, retrieved.ErrorMessage, "Error message should be set")
|
|
require.NotNil(t, retrieved.CompletedAt, "CompletedAt should be set for failed status")
|
|
}
|
|
|
|
func TestArchiveStore_UpdateStatus_ClearsErrorMessage(t *testing.T) {
|
|
store, userID, linkID, cleanup := setupTestArchiveStore(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
// Create an archive with an error
|
|
archive := &model.Archive{
|
|
ID: uuid.New().String(),
|
|
LinkID: linkID,
|
|
UserID: userID,
|
|
Status: model.ArchiveStatusFailed,
|
|
ErrorMessage: "previous error",
|
|
CreatedAt: now,
|
|
}
|
|
|
|
err := store.Create(ctx, archive)
|
|
require.NoError(t, err)
|
|
|
|
// Update to completed (should clear error message)
|
|
err = store.UpdateStatus(ctx, archive.ID, model.ArchiveStatusCompleted, "")
|
|
require.NoError(t, err)
|
|
|
|
// Verify error message was cleared
|
|
retrieved, err := store.GetByID(ctx, archive.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, model.ArchiveStatusCompleted, retrieved.Status)
|
|
require.Empty(t, retrieved.ErrorMessage, "Error message should be cleared when updating to completed")
|
|
}
|
|
|
|
func TestArchiveStore_ListByLinkID(t *testing.T) {
|
|
store, userID, linkID, cleanup := setupTestArchiveStore(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create multiple archives for the same link
|
|
archive1 := &model.Archive{
|
|
ID: uuid.New().String(),
|
|
LinkID: linkID,
|
|
UserID: userID,
|
|
Status: model.ArchiveStatusCompleted,
|
|
CreatedAt: time.Now().Add(-2 * time.Hour),
|
|
}
|
|
|
|
archive2 := &model.Archive{
|
|
ID: uuid.New().String(),
|
|
LinkID: linkID,
|
|
UserID: userID,
|
|
Status: model.ArchiveStatusPending,
|
|
CreatedAt: time.Now().Add(-1 * time.Hour),
|
|
}
|
|
|
|
archive3 := &model.Archive{
|
|
ID: uuid.New().String(),
|
|
LinkID: linkID,
|
|
UserID: userID,
|
|
Status: model.ArchiveStatusProcessing,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
|
|
err := store.Create(ctx, archive1)
|
|
require.NoError(t, err)
|
|
err = store.Create(ctx, archive2)
|
|
require.NoError(t, err)
|
|
err = store.Create(ctx, archive3)
|
|
require.NoError(t, err)
|
|
|
|
// List archives
|
|
opts := ArchiveListOptions{LinkID: linkID}
|
|
archives, err := store.ListByLinkID(ctx, opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, archives, 3)
|
|
|
|
// Should be ordered by created_at DESC (newest first)
|
|
require.Equal(t, archive3.ID, archives[0].ID)
|
|
require.Equal(t, archive2.ID, archives[1].ID)
|
|
require.Equal(t, archive1.ID, archives[2].ID)
|
|
}
|
|
|
|
func TestArchiveStore_GetByID_NotFound(t *testing.T) {
|
|
store, _, _, cleanup := setupTestArchiveStore(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
_, err := store.GetByID(ctx, "non-existent-id")
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "archive not found")
|
|
}
|