package services import ( "context" "fmt" "testing" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" "github.com/aws/aws-sdk-go-v2/service/dynamodb" "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "custom-start-page/internal/models" "custom-start-page/internal/storage" ) func setupPageServiceTest(t *testing.T) (*PageService, context.Context, func()) { ctx := context.Background() // Create DynamoDB client db, err := storage.NewDynamoDBClient(ctx, "http://localhost:8000") require.NoError(t, err, "Failed to create DynamoDB client") // Create tables err = db.CreatePagesTable(ctx) require.NoError(t, err, "Failed to create Pages table") err = db.CreateWidgetsTable(ctx) require.NoError(t, err, "Failed to create Widgets table") service := NewPageService(db) cleanup := func() { // Clean up test data client := db.GetClient() // Delete Pages table _, _ = client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("Pages"), }) // Delete Widgets table _, _ = client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("Widgets"), }) } return service, ctx, cleanup } func TestCreateDefaultPage(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-1" page, err := service.CreateDefaultPage(ctx, userID) require.NoError(t, err) assert.NotEmpty(t, page.ID) assert.Equal(t, userID, page.UserID) assert.Equal(t, "Home", page.Name) assert.Equal(t, 0, page.Order) assert.False(t, page.CreatedAt.IsZero()) assert.False(t, page.UpdatedAt.IsZero()) } func TestGetPages(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-2" // Create multiple pages page1, err := service.CreatePage(ctx, userID, "Home") require.NoError(t, err) page2, err := service.CreatePage(ctx, userID, "Work") require.NoError(t, err) page3, err := service.CreatePage(ctx, userID, "Personal") require.NoError(t, err) // Get all pages pages, err := service.GetPages(ctx, userID) require.NoError(t, err) assert.Len(t, pages, 3) // Verify pages are sorted by order assert.Equal(t, page1.ID, pages[0].ID) assert.Equal(t, page2.ID, pages[1].ID) assert.Equal(t, page3.ID, pages[2].ID) assert.Equal(t, 0, pages[0].Order) assert.Equal(t, 1, pages[1].Order) assert.Equal(t, 2, pages[2].Order) } func TestGetPagesEmptyResult(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-nonexistent" pages, err := service.GetPages(ctx, userID) require.NoError(t, err) assert.Empty(t, pages) } func TestCreatePage(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-3" page, err := service.CreatePage(ctx, userID, "Test Page") require.NoError(t, err) assert.NotEmpty(t, page.ID) assert.Equal(t, userID, page.UserID) assert.Equal(t, "Test Page", page.Name) assert.Equal(t, 0, page.Order) // Create another page page2, err := service.CreatePage(ctx, userID, "Second Page") require.NoError(t, err) assert.Equal(t, 1, page2.Order) } func TestUpdatePage(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-4" // Create a page page, err := service.CreatePage(ctx, userID, "Original Name") require.NoError(t, err) // Update the page name updatedPage, err := service.UpdatePage(ctx, userID, page.ID, "Updated Name") require.NoError(t, err) assert.Equal(t, page.ID, updatedPage.ID) assert.Equal(t, "Updated Name", updatedPage.Name) assert.True(t, updatedPage.UpdatedAt.After(page.UpdatedAt) || updatedPage.UpdatedAt.Equal(page.UpdatedAt)) } func TestDeletePage(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-5" // Create multiple pages page1, err := service.CreatePage(ctx, userID, "Page 1") require.NoError(t, err) page2, err := service.CreatePage(ctx, userID, "Page 2") require.NoError(t, err) // Delete page2 err = service.DeletePage(ctx, userID, page2.ID) require.NoError(t, err) // Verify only page1 remains pages, err := service.GetPages(ctx, userID) require.NoError(t, err) assert.Len(t, pages, 1) assert.Equal(t, page1.ID, pages[0].ID) } func TestDeleteLastPagePrevention(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-6" // Create a single page page, err := service.CreatePage(ctx, userID, "Only Page") require.NoError(t, err) // Attempt to delete the last page err = service.DeletePage(ctx, userID, page.ID) assert.Error(t, err) assert.Contains(t, err.Error(), "cannot delete the last page") // Verify page still exists pages, err := service.GetPages(ctx, userID) require.NoError(t, err) assert.Len(t, pages, 1) } func TestDeletePageWithWidgets(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-7" // Create two pages _, err := service.CreatePage(ctx, userID, "Page 1") require.NoError(t, err) page2, err := service.CreatePage(ctx, userID, "Page 2") require.NoError(t, err) // Add widgets to page2 db := service.db for i := 0; i < 3; i++ { widget := &models.Widget{ ID: fmt.Sprintf("widget-%d", i), PageID: page2.ID, UserID: userID, Type: models.WidgetTypeBookmark, Position: models.Position{X: 0, Y: i}, Size: models.Size{Width: 1, Height: 1}, Config: make(map[string]interface{}), CreatedAt: time.Now(), UpdatedAt: time.Now(), } item, err := attributevalue.MarshalMap(widget) require.NoError(t, err) err = db.PutItem(ctx, &dynamodb.PutItemInput{ TableName: aws.String("Widgets"), Item: item, }) require.NoError(t, err) } // Delete page2 (should cascade delete widgets) err = service.DeletePage(ctx, userID, page2.ID) require.NoError(t, err) // Verify widgets are deleted widgetsOutput, err := db.Query(ctx, &dynamodb.QueryInput{ TableName: aws.String("Widgets"), KeyConditionExpression: aws.String("page_id = :page_id"), ExpressionAttributeValues: map[string]types.AttributeValue{ ":page_id": &types.AttributeValueMemberS{Value: page2.ID}, }, }) require.NoError(t, err) assert.Empty(t, widgetsOutput.Items) } func TestReorderPages(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-8" // Create three pages page1, err := service.CreatePage(ctx, userID, "Page 1") require.NoError(t, err) page2, err := service.CreatePage(ctx, userID, "Page 2") require.NoError(t, err) page3, err := service.CreatePage(ctx, userID, "Page 3") require.NoError(t, err) // Reorder: page3, page1, page2 newOrder := []string{page3.ID, page1.ID, page2.ID} err = service.ReorderPages(ctx, userID, newOrder) require.NoError(t, err) // Verify new order pages, err := service.GetPages(ctx, userID) require.NoError(t, err) assert.Len(t, pages, 3) assert.Equal(t, page3.ID, pages[0].ID) assert.Equal(t, 0, pages[0].Order) assert.Equal(t, page1.ID, pages[1].ID) assert.Equal(t, 1, pages[1].Order) assert.Equal(t, page2.ID, pages[2].ID) assert.Equal(t, 2, pages[2].Order) } func TestReorderPagesInvalidLength(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-9" // Create two pages page1, err := service.CreatePage(ctx, userID, "Page 1") require.NoError(t, err) _, err = service.CreatePage(ctx, userID, "Page 2") require.NoError(t, err) // Attempt to reorder with wrong number of pages err = service.ReorderPages(ctx, userID, []string{page1.ID}) assert.Error(t, err) assert.Contains(t, err.Error(), "page order list length mismatch") } func TestReorderPagesInvalidPageID(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-10" // Create two pages page1, err := service.CreatePage(ctx, userID, "Page 1") require.NoError(t, err) _, err = service.CreatePage(ctx, userID, "Page 2") require.NoError(t, err) // Attempt to reorder with invalid page ID err = service.ReorderPages(ctx, userID, []string{page1.ID, "invalid-page-id"}) assert.Error(t, err) assert.Contains(t, err.Error(), "not found or does not belong to user") } func TestReorderPagesPersistence(t *testing.T) { service, ctx, cleanup := setupPageServiceTest(t) defer cleanup() userID := "test-user-11" // Create three pages page1, err := service.CreatePage(ctx, userID, "Page 1") require.NoError(t, err) page2, err := service.CreatePage(ctx, userID, "Page 2") require.NoError(t, err) page3, err := service.CreatePage(ctx, userID, "Page 3") require.NoError(t, err) // Reorder newOrder := []string{page2.ID, page3.ID, page1.ID} err = service.ReorderPages(ctx, userID, newOrder) require.NoError(t, err) // Retrieve pages again to verify persistence pages, err := service.GetPages(ctx, userID) require.NoError(t, err) assert.Equal(t, page2.ID, pages[0].ID) assert.Equal(t, page3.ID, pages[1].ID) assert.Equal(t, page1.ID, pages[2].ID) }