package storage import ( "context" "os" "testing" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/dynamodb" "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" ) func setupTestClient(t *testing.T) (*DynamoDBClient, context.Context) { t.Helper() // Set dummy AWS credentials for local DynamoDB os.Setenv("AWS_ACCESS_KEY_ID", "dummy") os.Setenv("AWS_SECRET_ACCESS_KEY", "dummy") os.Setenv("AWS_REGION", "us-east-1") endpoint := os.Getenv("DYNAMODB_ENDPOINT") if endpoint == "" { endpoint = "http://localhost:8000" } ctx := context.Background() client, err := NewDynamoDBClient(ctx, endpoint) if err != nil { t.Skipf("Skipping test: DynamoDB not available: %v", err) } // Test connection by listing tables _, err = client.client.ListTables(ctx, &dynamodb.ListTablesInput{}) if err != nil { t.Skipf("Skipping test: Cannot connect to DynamoDB: %v", err) } return client, ctx } func TestNewDynamoDBClient(t *testing.T) { client, _ := setupTestClient(t) if client == nil { t.Fatal("Expected non-nil client") } if client.client == nil { t.Fatal("Expected non-nil underlying client") } } func TestTransactWriteItems(t *testing.T) { client, ctx := setupTestClient(t) // Create test table tableName := "TestTransactions" createTestTable(t, ctx, client, tableName) defer deleteTestTable(t, ctx, client, tableName) // Test transaction with two puts testID1 := "test-txn-1" testID2 := "test-txn-2" input := &dynamodb.TransactWriteItemsInput{ TransactItems: []types.TransactWriteItem{ { Put: &types.Put{ TableName: aws.String(tableName), Item: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID1}, "value": &types.AttributeValueMemberS{Value: "value1"}, }, }, }, { Put: &types.Put{ TableName: aws.String(tableName), Item: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID2}, "value": &types.AttributeValueMemberS{Value: "value2"}, }, }, }, }, } err := client.TransactWriteItems(ctx, input) if err != nil { t.Fatalf("TransactWriteItems failed: %v", err) } // Verify both items were written output1, err := client.GetItem(ctx, &dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID1}, }, }) if err != nil { t.Fatalf("GetItem failed: %v", err) } if len(output1.Item) == 0 { t.Fatal("Expected item to exist after transaction") } output2, err := client.GetItem(ctx, &dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID2}, }, }) if err != nil { t.Fatalf("GetItem failed: %v", err) } if len(output2.Item) == 0 { t.Fatal("Expected item to exist after transaction") } } func TestBatchGetItems(t *testing.T) { client, ctx := setupTestClient(t) // Create test table tableName := "TestBatchGet" createTestTable(t, ctx, client, tableName) defer deleteTestTable(t, ctx, client, tableName) // Put test items testIDs := []string{"batch-1", "batch-2", "batch-3"} for _, id := range testIDs { err := client.PutItem(ctx, &dynamodb.PutItemInput{ TableName: aws.String(tableName), Item: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: id}, "value": &types.AttributeValueMemberS{Value: "test-value"}, }, }) if err != nil { t.Fatalf("PutItem failed: %v", err) } } // Batch get items keys := make([]map[string]types.AttributeValue, len(testIDs)) for i, id := range testIDs { keys[i] = map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: id}, } } output, err := client.BatchGetItems(ctx, &dynamodb.BatchGetItemInput{ RequestItems: map[string]types.KeysAndAttributes{ tableName: { Keys: keys, }, }, }) if err != nil { t.Fatalf("BatchGetItems failed: %v", err) } if len(output.Responses[tableName]) != len(testIDs) { t.Fatalf("Expected %d items, got %d", len(testIDs), len(output.Responses[tableName])) } } func TestBatchWriteItems(t *testing.T) { client, ctx := setupTestClient(t) // Create test table tableName := "TestBatchWrite" createTestTable(t, ctx, client, tableName) defer deleteTestTable(t, ctx, client, tableName) // Batch write items testIDs := []string{"write-1", "write-2", "write-3"} writeRequests := make([]types.WriteRequest, len(testIDs)) for i, id := range testIDs { writeRequests[i] = types.WriteRequest{ PutRequest: &types.PutRequest{ Item: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: id}, "value": &types.AttributeValueMemberS{Value: "batch-value"}, }, }, } } err := client.BatchWriteItems(ctx, &dynamodb.BatchWriteItemInput{ RequestItems: map[string][]types.WriteRequest{ tableName: writeRequests, }, }) if err != nil { t.Fatalf("BatchWriteItems failed: %v", err) } // Verify items were written for _, id := range testIDs { output, err := client.GetItem(ctx, &dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: id}, }, }) if err != nil { t.Fatalf("GetItem failed: %v", err) } if len(output.Item) == 0 { t.Fatalf("Expected item %s to exist after batch write", id) } } } func TestPutAndGetItem(t *testing.T) { client, ctx := setupTestClient(t) // Create test table tableName := "TestPutGet" createTestTable(t, ctx, client, tableName) defer deleteTestTable(t, ctx, client, tableName) // Put item testID := "put-get-test" err := client.PutItem(ctx, &dynamodb.PutItemInput{ TableName: aws.String(tableName), Item: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID}, "value": &types.AttributeValueMemberS{Value: "test-value"}, }, }) if err != nil { t.Fatalf("PutItem failed: %v", err) } // Get item output, err := client.GetItem(ctx, &dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID}, }, }) if err != nil { t.Fatalf("GetItem failed: %v", err) } if len(output.Item) == 0 { t.Fatal("Expected item to exist") } valueAttr, ok := output.Item["value"] if !ok { t.Fatal("Expected 'value' attribute") } value := valueAttr.(*types.AttributeValueMemberS).Value if value != "test-value" { t.Fatalf("Expected value 'test-value', got '%s'", value) } } func TestUpdateItem(t *testing.T) { client, ctx := setupTestClient(t) // Create test table tableName := "TestUpdate" createTestTable(t, ctx, client, tableName) defer deleteTestTable(t, ctx, client, tableName) // Put initial item testID := "update-test" err := client.PutItem(ctx, &dynamodb.PutItemInput{ TableName: aws.String(tableName), Item: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID}, "value": &types.AttributeValueMemberS{Value: "initial"}, }, }) if err != nil { t.Fatalf("PutItem failed: %v", err) } // Update item _, err = client.UpdateItem(ctx, &dynamodb.UpdateItemInput{ TableName: aws.String(tableName), Key: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID}, }, UpdateExpression: aws.String("SET #v = :val"), ExpressionAttributeNames: map[string]string{ "#v": "value", }, ExpressionAttributeValues: map[string]types.AttributeValue{ ":val": &types.AttributeValueMemberS{Value: "updated"}, }, }) if err != nil { t.Fatalf("UpdateItem failed: %v", err) } // Verify update output, err := client.GetItem(ctx, &dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID}, }, }) if err != nil { t.Fatalf("GetItem failed: %v", err) } value := output.Item["value"].(*types.AttributeValueMemberS).Value if value != "updated" { t.Fatalf("Expected value 'updated', got '%s'", value) } } func TestDeleteItem(t *testing.T) { client, ctx := setupTestClient(t) // Create test table tableName := "TestDelete" createTestTable(t, ctx, client, tableName) defer deleteTestTable(t, ctx, client, tableName) // Put item testID := "delete-test" err := client.PutItem(ctx, &dynamodb.PutItemInput{ TableName: aws.String(tableName), Item: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID}, "value": &types.AttributeValueMemberS{Value: "test"}, }, }) if err != nil { t.Fatalf("PutItem failed: %v", err) } // Delete item err = client.DeleteItem(ctx, &dynamodb.DeleteItemInput{ TableName: aws.String(tableName), Key: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID}, }, }) if err != nil { t.Fatalf("DeleteItem failed: %v", err) } // Verify deletion output, err := client.GetItem(ctx, &dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: testID}, }, }) if err != nil { t.Fatalf("GetItem failed: %v", err) } if len(output.Item) != 0 { t.Fatal("Expected item to be deleted") } } func TestQuery(t *testing.T) { client, ctx := setupTestClient(t) // Create test table tableName := "TestQuery" createTestTable(t, ctx, client, tableName) defer deleteTestTable(t, ctx, client, tableName) // Put test items testIDs := []string{"query-1", "query-2", "query-3"} for _, id := range testIDs { err := client.PutItem(ctx, &dynamodb.PutItemInput{ TableName: aws.String(tableName), Item: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: id}, "value": &types.AttributeValueMemberS{Value: "test"}, }, }) if err != nil { t.Fatalf("PutItem failed: %v", err) } } // Query for specific item output, err := client.Query(ctx, &dynamodb.QueryInput{ TableName: aws.String(tableName), KeyConditionExpression: aws.String("id = :id"), ExpressionAttributeValues: map[string]types.AttributeValue{ ":id": &types.AttributeValueMemberS{Value: "query-1"}, }, }) if err != nil { t.Fatalf("Query failed: %v", err) } if len(output.Items) != 1 { t.Fatalf("Expected 1 item, got %d", len(output.Items)) } } // Helper functions func createTestTable(t *testing.T, ctx context.Context, client *DynamoDBClient, tableName string) { t.Helper() _, err := client.client.CreateTable(ctx, &dynamodb.CreateTableInput{ TableName: aws.String(tableName), AttributeDefinitions: []types.AttributeDefinition{ { AttributeName: aws.String("id"), AttributeType: types.ScalarAttributeTypeS, }, }, KeySchema: []types.KeySchemaElement{ { AttributeName: aws.String("id"), KeyType: types.KeyTypeHash, }, }, BillingMode: types.BillingModePayPerRequest, }) if err != nil { t.Fatalf("Failed to create test table: %v", err) } // Wait for table to be active waiter := dynamodb.NewTableExistsWaiter(client.client) err = waiter.Wait(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String(tableName), }, 30*time.Second) if err != nil { t.Fatalf("Failed waiting for table to be active: %v", err) } } func deleteTestTable(t *testing.T, ctx context.Context, client *DynamoDBClient, tableName string) { t.Helper() _, err := client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String(tableName), }) if err != nil { t.Logf("Warning: Failed to delete test table: %v", err) } } // TestCreateUsersTable tests the Users table creation func TestCreateUsersTable(t *testing.T) { client, ctx := setupTestClient(t) // Delete table if it exists client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("Users"), }) time.Sleep(1 * time.Second) // Create table err := client.CreateUsersTable(ctx) if err != nil { t.Fatalf("CreateUsersTable failed: %v", err) } // Verify table exists and has correct schema output, err := client.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String("Users"), }) if err != nil { t.Fatalf("DescribeTable failed: %v", err) } // Verify key schema if len(output.Table.KeySchema) != 1 { t.Fatalf("Expected 1 key, got %d", len(output.Table.KeySchema)) } if *output.Table.KeySchema[0].AttributeName != "user_id" { t.Errorf("Expected partition key 'user_id', got '%s'", *output.Table.KeySchema[0].AttributeName) } // Test idempotency - calling again should not error err = client.CreateUsersTable(ctx) if err != nil { t.Fatalf("CreateUsersTable should be idempotent: %v", err) } } // TestCreatePagesTable tests the Pages table creation with composite key func TestCreatePagesTable(t *testing.T) { client, ctx := setupTestClient(t) // Delete table if it exists client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("Pages"), }) time.Sleep(1 * time.Second) // Create table err := client.CreatePagesTable(ctx) if err != nil { t.Fatalf("CreatePagesTable failed: %v", err) } // Verify table exists and has correct schema output, err := client.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String("Pages"), }) if err != nil { t.Fatalf("DescribeTable failed: %v", err) } // Verify composite key schema if len(output.Table.KeySchema) != 2 { t.Fatalf("Expected 2 keys, got %d", len(output.Table.KeySchema)) } if *output.Table.KeySchema[0].AttributeName != "user_id" { t.Errorf("Expected partition key 'user_id', got '%s'", *output.Table.KeySchema[0].AttributeName) } if *output.Table.KeySchema[1].AttributeName != "page_id" { t.Errorf("Expected sort key 'page_id', got '%s'", *output.Table.KeySchema[1].AttributeName) } } // TestCreateWidgetsTable tests the Widgets table creation func TestCreateWidgetsTable(t *testing.T) { client, ctx := setupTestClient(t) // Delete table if it exists client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("Widgets"), }) time.Sleep(1 * time.Second) // Create table err := client.CreateWidgetsTable(ctx) if err != nil { t.Fatalf("CreateWidgetsTable failed: %v", err) } // Verify table exists and has correct schema output, err := client.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String("Widgets"), }) if err != nil { t.Fatalf("DescribeTable failed: %v", err) } // Verify composite key schema if len(output.Table.KeySchema) != 2 { t.Fatalf("Expected 2 keys, got %d", len(output.Table.KeySchema)) } if *output.Table.KeySchema[0].AttributeName != "page_id" { t.Errorf("Expected partition key 'page_id', got '%s'", *output.Table.KeySchema[0].AttributeName) } if *output.Table.KeySchema[1].AttributeName != "widget_id" { t.Errorf("Expected sort key 'widget_id', got '%s'", *output.Table.KeySchema[1].AttributeName) } } // TestCreateBookmarksTable tests the Bookmarks table creation with GSI func TestCreateBookmarksTable(t *testing.T) { client, ctx := setupTestClient(t) // Delete table if it exists client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("Bookmarks"), }) time.Sleep(1 * time.Second) // Create table err := client.CreateBookmarksTable(ctx) if err != nil { t.Fatalf("CreateBookmarksTable failed: %v", err) } // Verify table exists and has correct schema output, err := client.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String("Bookmarks"), }) if err != nil { t.Fatalf("DescribeTable failed: %v", err) } // Verify composite key schema if len(output.Table.KeySchema) != 2 { t.Fatalf("Expected 2 keys, got %d", len(output.Table.KeySchema)) } if *output.Table.KeySchema[0].AttributeName != "widget_id" { t.Errorf("Expected partition key 'widget_id', got '%s'", *output.Table.KeySchema[0].AttributeName) } if *output.Table.KeySchema[1].AttributeName != "bookmark_id" { t.Errorf("Expected sort key 'bookmark_id', got '%s'", *output.Table.KeySchema[1].AttributeName) } // Verify GSI exists if len(output.Table.GlobalSecondaryIndexes) != 1 { t.Fatalf("Expected 1 GSI, got %d", len(output.Table.GlobalSecondaryIndexes)) } gsi := output.Table.GlobalSecondaryIndexes[0] if *gsi.IndexName != "UserBookmarksIndex" { t.Errorf("Expected GSI name 'UserBookmarksIndex', got '%s'", *gsi.IndexName) } if len(gsi.KeySchema) != 2 { t.Fatalf("Expected 2 GSI keys, got %d", len(gsi.KeySchema)) } if *gsi.KeySchema[0].AttributeName != "user_id" { t.Errorf("Expected GSI partition key 'user_id', got '%s'", *gsi.KeySchema[0].AttributeName) } if *gsi.KeySchema[1].AttributeName != "created_at" { t.Errorf("Expected GSI sort key 'created_at', got '%s'", *gsi.KeySchema[1].AttributeName) } } // TestCreateNotesTable tests the Notes table creation with GSI func TestCreateNotesTable(t *testing.T) { client, ctx := setupTestClient(t) // Delete table if it exists client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("Notes"), }) time.Sleep(1 * time.Second) // Create table err := client.CreateNotesTable(ctx) if err != nil { t.Fatalf("CreateNotesTable failed: %v", err) } // Verify table exists and has correct schema output, err := client.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String("Notes"), }) if err != nil { t.Fatalf("DescribeTable failed: %v", err) } // Verify composite key schema if len(output.Table.KeySchema) != 2 { t.Fatalf("Expected 2 keys, got %d", len(output.Table.KeySchema)) } if *output.Table.KeySchema[0].AttributeName != "widget_id" { t.Errorf("Expected partition key 'widget_id', got '%s'", *output.Table.KeySchema[0].AttributeName) } if *output.Table.KeySchema[1].AttributeName != "note_id" { t.Errorf("Expected sort key 'note_id', got '%s'", *output.Table.KeySchema[1].AttributeName) } // Verify GSI exists if len(output.Table.GlobalSecondaryIndexes) != 1 { t.Fatalf("Expected 1 GSI, got %d", len(output.Table.GlobalSecondaryIndexes)) } gsi := output.Table.GlobalSecondaryIndexes[0] if *gsi.IndexName != "UserNotesIndex" { t.Errorf("Expected GSI name 'UserNotesIndex', got '%s'", *gsi.IndexName) } } // TestCreateTagAssociationsTable tests the TagAssociations table creation with GSI func TestCreateTagAssociationsTable(t *testing.T) { client, ctx := setupTestClient(t) // Delete table if it exists client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("TagAssociations"), }) time.Sleep(1 * time.Second) // Create table err := client.CreateTagAssociationsTable(ctx) if err != nil { t.Fatalf("CreateTagAssociationsTable failed: %v", err) } // Verify table exists and has correct schema output, err := client.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String("TagAssociations"), }) if err != nil { t.Fatalf("DescribeTable failed: %v", err) } // Verify composite key schema if len(output.Table.KeySchema) != 2 { t.Fatalf("Expected 2 keys, got %d", len(output.Table.KeySchema)) } if *output.Table.KeySchema[0].AttributeName != "item_id" { t.Errorf("Expected partition key 'item_id', got '%s'", *output.Table.KeySchema[0].AttributeName) } if *output.Table.KeySchema[1].AttributeName != "tag_name" { t.Errorf("Expected sort key 'tag_name', got '%s'", *output.Table.KeySchema[1].AttributeName) } // Verify GSI exists if len(output.Table.GlobalSecondaryIndexes) != 1 { t.Fatalf("Expected 1 GSI, got %d", len(output.Table.GlobalSecondaryIndexes)) } gsi := output.Table.GlobalSecondaryIndexes[0] if *gsi.IndexName != "TagItemsIndex" { t.Errorf("Expected GSI name 'TagItemsIndex', got '%s'", *gsi.IndexName) } if len(gsi.KeySchema) != 2 { t.Fatalf("Expected 2 GSI keys, got %d", len(gsi.KeySchema)) } if *gsi.KeySchema[0].AttributeName != "tag_name" { t.Errorf("Expected GSI partition key 'tag_name', got '%s'", *gsi.KeySchema[0].AttributeName) } if *gsi.KeySchema[1].AttributeName != "user_id" { t.Errorf("Expected GSI sort key 'user_id', got '%s'", *gsi.KeySchema[1].AttributeName) } // Verify projection type is INCLUDE if gsi.Projection.ProjectionType != types.ProjectionTypeInclude { t.Errorf("Expected projection type INCLUDE, got %v", gsi.Projection.ProjectionType) } } // TestCreateGroupsTable tests the Groups table creation func TestCreateGroupsTable(t *testing.T) { client, ctx := setupTestClient(t) // Delete table if it exists client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("Groups"), }) time.Sleep(1 * time.Second) // Create table err := client.CreateGroupsTable(ctx) if err != nil { t.Fatalf("CreateGroupsTable failed: %v", err) } // Verify table exists and has correct schema output, err := client.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String("Groups"), }) if err != nil { t.Fatalf("DescribeTable failed: %v", err) } // Verify composite key schema if len(output.Table.KeySchema) != 2 { t.Fatalf("Expected 2 keys, got %d", len(output.Table.KeySchema)) } if *output.Table.KeySchema[0].AttributeName != "widget_id" { t.Errorf("Expected partition key 'widget_id', got '%s'", *output.Table.KeySchema[0].AttributeName) } if *output.Table.KeySchema[1].AttributeName != "group_id" { t.Errorf("Expected sort key 'group_id', got '%s'", *output.Table.KeySchema[1].AttributeName) } } // TestCreateSharedItemsTable tests the SharedItems table creation with GSI func TestCreateSharedItemsTable(t *testing.T) { client, ctx := setupTestClient(t) // Delete table if it exists client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("SharedItems"), }) time.Sleep(1 * time.Second) // Create table err := client.CreateSharedItemsTable(ctx) if err != nil { t.Fatalf("CreateSharedItemsTable failed: %v", err) } // Verify table exists and has correct schema output, err := client.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String("SharedItems"), }) if err != nil { t.Fatalf("DescribeTable failed: %v", err) } // Verify key schema if len(output.Table.KeySchema) != 1 { t.Fatalf("Expected 1 key, got %d", len(output.Table.KeySchema)) } if *output.Table.KeySchema[0].AttributeName != "share_id" { t.Errorf("Expected partition key 'share_id', got '%s'", *output.Table.KeySchema[0].AttributeName) } // Verify GSI exists if len(output.Table.GlobalSecondaryIndexes) != 1 { t.Fatalf("Expected 1 GSI, got %d", len(output.Table.GlobalSecondaryIndexes)) } gsi := output.Table.GlobalSecondaryIndexes[0] if *gsi.IndexName != "UserSharesIndex" { t.Errorf("Expected GSI name 'UserSharesIndex', got '%s'", *gsi.IndexName) } if len(gsi.KeySchema) != 2 { t.Fatalf("Expected 2 GSI keys, got %d", len(gsi.KeySchema)) } if *gsi.KeySchema[0].AttributeName != "user_id" { t.Errorf("Expected GSI partition key 'user_id', got '%s'", *gsi.KeySchema[0].AttributeName) } if *gsi.KeySchema[1].AttributeName != "created_at" { t.Errorf("Expected GSI sort key 'created_at', got '%s'", *gsi.KeySchema[1].AttributeName) } } // TestCreatePreferencesTable tests the Preferences table creation func TestCreatePreferencesTable(t *testing.T) { client, ctx := setupTestClient(t) // Delete table if it exists client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String("Preferences"), }) time.Sleep(1 * time.Second) // Create table err := client.CreatePreferencesTable(ctx) if err != nil { t.Fatalf("CreatePreferencesTable failed: %v", err) } // Verify table exists and has correct schema output, err := client.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ TableName: aws.String("Preferences"), }) if err != nil { t.Fatalf("DescribeTable failed: %v", err) } // Verify key schema if len(output.Table.KeySchema) != 1 { t.Fatalf("Expected 1 key, got %d", len(output.Table.KeySchema)) } if *output.Table.KeySchema[0].AttributeName != "user_id" { t.Errorf("Expected partition key 'user_id', got '%s'", *output.Table.KeySchema[0].AttributeName) } } // TestCreateAllTables tests creating all tables at once func TestCreateAllTables(t *testing.T) { client, ctx := setupTestClient(t) // Delete all tables if they exist tables := []string{ "Users", "Pages", "Widgets", "Bookmarks", "Notes", "TagAssociations", "Groups", "SharedItems", "Preferences", } for _, table := range tables { client.client.DeleteTable(ctx, &dynamodb.DeleteTableInput{ TableName: aws.String(table), }) } time.Sleep(2 * time.Second) // Create all tables createFuncs := []struct { name string create func(context.Context) error }{ {"Users", client.CreateUsersTable}, {"Pages", client.CreatePagesTable}, {"Widgets", client.CreateWidgetsTable}, {"Bookmarks", client.CreateBookmarksTable}, {"Notes", client.CreateNotesTable}, {"TagAssociations", client.CreateTagAssociationsTable}, {"Groups", client.CreateGroupsTable}, {"SharedItems", client.CreateSharedItemsTable}, {"Preferences", client.CreatePreferencesTable}, } for _, fn := range createFuncs { err := fn.create(ctx) if err != nil { t.Fatalf("Failed to create %s table: %v", fn.name, err) } } // Verify all tables exist listOutput, err := client.client.ListTables(ctx, &dynamodb.ListTablesInput{}) if err != nil { t.Fatalf("ListTables failed: %v", err) } tableMap := make(map[string]bool) for _, tableName := range listOutput.TableNames { tableMap[tableName] = true } for _, table := range tables { if !tableMap[table] { t.Errorf("Expected table %s to exist", table) } } }