/// Integration tests for ZynamoDB const std = @import("std"); // Import modules from main source const rocksdb = @import("../src/rocksdb.zig"); const storage = @import("../src/dynamodb/storage.zig"); const types = @import("../src/dynamodb/types.zig"); test "integration: full table lifecycle" { const allocator = std.testing.allocator; // Setup const path = "/tmp/test_integration_lifecycle"; defer std.fs.deleteTreeAbsolute(path) catch {}; var engine = try storage.StorageEngine.init(allocator, path); defer engine.deinit(); // Create table const key_schema = [_]types.KeySchemaElement{ .{ .attribute_name = "pk", .key_type = .HASH }, }; const attr_defs = [_]types.AttributeDefinition{ .{ .attribute_name = "pk", .attribute_type = .S }, }; const desc = try engine.createTable("Users", &key_schema, &attr_defs); try std.testing.expectEqualStrings("Users", desc.table_name); try std.testing.expectEqual(types.TableStatus.ACTIVE, desc.table_status); // Put items try engine.putItem("Users", "{\"pk\":{\"S\":\"user1\"},\"name\":{\"S\":\"Alice\"}}"); try engine.putItem("Users", "{\"pk\":{\"S\":\"user2\"},\"name\":{\"S\":\"Bob\"}}"); try engine.putItem("Users", "{\"pk\":{\"S\":\"user3\"},\"name\":{\"S\":\"Charlie\"}}"); // Get item const item = try engine.getItem("Users", "{\"pk\":{\"S\":\"user1\"}}"); try std.testing.expect(item != null); defer allocator.free(item.?); // Scan const all_items = try engine.scan("Users", null); defer { for (all_items) |i| allocator.free(i); allocator.free(all_items); } try std.testing.expectEqual(@as(usize, 3), all_items.len); // Delete item try engine.deleteItem("Users", "{\"pk\":{\"S\":\"user2\"}}"); // Verify deletion const after_delete = try engine.scan("Users", null); defer { for (after_delete) |i| allocator.free(i); allocator.free(after_delete); } try std.testing.expectEqual(@as(usize, 2), after_delete.len); // Delete table try engine.deleteTable("Users"); // Verify table deletion const tables = try engine.listTables(); defer allocator.free(tables); try std.testing.expectEqual(@as(usize, 0), tables.len); } test "integration: multiple tables" { const allocator = std.testing.allocator; const path = "/tmp/test_integration_multi_table"; defer std.fs.deleteTreeAbsolute(path) catch {}; var engine = try storage.StorageEngine.init(allocator, path); defer engine.deinit(); const key_schema = [_]types.KeySchemaElement{ .{ .attribute_name = "pk", .key_type = .HASH }, }; const attr_defs = [_]types.AttributeDefinition{ .{ .attribute_name = "pk", .attribute_type = .S }, }; // Create multiple tables _ = try engine.createTable("Table1", &key_schema, &attr_defs); _ = try engine.createTable("Table2", &key_schema, &attr_defs); _ = try engine.createTable("Table3", &key_schema, &attr_defs); // List tables const tables = try engine.listTables(); defer { for (tables) |t| allocator.free(t); allocator.free(tables); } try std.testing.expectEqual(@as(usize, 3), tables.len); // Put items in different tables try engine.putItem("Table1", "{\"pk\":{\"S\":\"item1\"}}"); try engine.putItem("Table2", "{\"pk\":{\"S\":\"item2\"}}"); try engine.putItem("Table3", "{\"pk\":{\"S\":\"item3\"}}"); // Verify isolation - scan should only return items from that table const table1_items = try engine.scan("Table1", null); defer { for (table1_items) |i| allocator.free(i); allocator.free(table1_items); } try std.testing.expectEqual(@as(usize, 1), table1_items.len); } test "integration: table already exists error" { const allocator = std.testing.allocator; const path = "/tmp/test_integration_exists"; defer std.fs.deleteTreeAbsolute(path) catch {}; var engine = try storage.StorageEngine.init(allocator, path); defer engine.deinit(); const key_schema = [_]types.KeySchemaElement{ .{ .attribute_name = "pk", .key_type = .HASH }, }; const attr_defs = [_]types.AttributeDefinition{ .{ .attribute_name = "pk", .attribute_type = .S }, }; // Create table _ = try engine.createTable("DuplicateTest", &key_schema, &attr_defs); // Try to create again - should fail const result = engine.createTable("DuplicateTest", &key_schema, &attr_defs); try std.testing.expectError(storage.StorageError.TableAlreadyExists, result); } test "integration: table not found error" { const allocator = std.testing.allocator; const path = "/tmp/test_integration_notfound"; defer std.fs.deleteTreeAbsolute(path) catch {}; var engine = try storage.StorageEngine.init(allocator, path); defer engine.deinit(); // Try to put item in non-existent table const result = engine.putItem("NonExistent", "{\"pk\":{\"S\":\"item1\"}}"); try std.testing.expectError(storage.StorageError.TableNotFound, result); } test "integration: scan with limit" { const allocator = std.testing.allocator; const path = "/tmp/test_integration_scan_limit"; defer std.fs.deleteTreeAbsolute(path) catch {}; var engine = try storage.StorageEngine.init(allocator, path); defer engine.deinit(); const key_schema = [_]types.KeySchemaElement{ .{ .attribute_name = "pk", .key_type = .HASH }, }; const attr_defs = [_]types.AttributeDefinition{ .{ .attribute_name = "pk", .attribute_type = .S }, }; _ = try engine.createTable("LimitTest", &key_schema, &attr_defs); // Add many items var i: usize = 0; while (i < 10) : (i += 1) { var buf: [128]u8 = undefined; const item = try std.fmt.bufPrint(&buf, "{{\"pk\":{{\"S\":\"item{d}\"}}}}", .{i}); try engine.putItem("LimitTest", item); } // Scan with limit const limited = try engine.scan("LimitTest", 5); defer { for (limited) |item| allocator.free(item); allocator.free(limited); } try std.testing.expectEqual(@as(usize, 5), limited.len); }