import pytest from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.keys import Keys import time @pytest.fixture def zfs_page(driver, dev_server): """Navigate to ZFS calculator page""" driver.get(f"{dev_server}") # Wait for page to load and JavaScript to populate navigation WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, "sidenav")) ) # Wait for JavaScript to populate the navigation with calculator links WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, "//a[contains(text(), 'ZFS')]")) ) # Click on ZFS Calculator zfs_btn = driver.find_element(By.XPATH, "//a[contains(text(), 'ZFS')]") zfs_btn.click() # Wait for ZFS calculator to load WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.NAME, "poolType")) ) return driver def _get_zfs_result(zfs_page): """Helper to get the correct ZFS result element""" result_elements = zfs_page.find_elements(By.CLASS_NAME, "result") # Look for the result element that contains ZFS-related content zfs_result = None for elem in result_elements: html = elem.get_attribute('innerHTML') if 'ZFS' in html or 'Pool Configuration' in html or 'Capacity' in html: zfs_result = elem break if not zfs_result: # If no ZFS result found, use the last result element (most recent) zfs_result = result_elements[-1] return zfs_result def test_zfs_calculator_loads(zfs_page): """Test that the ZFS calculator loads correctly""" # Wait a bit for JavaScript to fully load time.sleep(2) # Check for key elements assert zfs_page.find_element(By.NAME, "poolType") assert zfs_page.find_element(By.NAME, "diskCount") assert zfs_page.find_element(By.NAME, "diskSize") assert zfs_page.find_element(By.NAME, "compression") # Find the displayed result element (should be the ZFS one) displayed_result = None result_elements = zfs_page.find_elements(By.CLASS_NAME, "result") for elem in result_elements: if elem.is_displayed(): displayed_result = elem break assert displayed_result is not None, "No displayed result element found" # Check that the ZFS result is shown assert "ZFS Pool Configuration" in displayed_result.text def test_zfs_pool_type_selection(zfs_page): """Test that pool type selection works and updates calculations""" # Get initial result initial_result = _get_zfs_result(zfs_page) initial_text = initial_result.text # Change pool type to mirror using Select Lite pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() # Wait for dropdown to appear and click the mirror option mirror_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror')]")) ) mirror_option.click() # Wait for calculation to update time.sleep(1) # Check that result changed updated_result = _get_zfs_result(zfs_page) updated_text = updated_result.text assert updated_text != initial_text # Check that the calculation reflects mirror configuration (50% efficiency for 2-way mirror) assert "50.0%" in updated_text assert "12000.0 GB" in updated_text # Half of 24000 GB for 2-way mirror def test_zfs_disk_count_validation(zfs_page): """Test that disk count validation works correctly""" # Set pool type to RAID-Z2 (requires at least 4 disks) using Select Lite pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() # Wait for dropdown to appear and click the RAID-Z2 option raidz2_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2')]")) ) raidz2_option.click() # Set disk count to 2 (invalid for RAID-Z2) disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys("2") # Wait for validation error time.sleep(1) # Check for error message result = _get_zfs_result(zfs_page) assert "Error" in result.text assert "RAIDZ2 requires at least 4 disks" in result.text def test_zfs_compression_calculation(zfs_page): """Test that compression ratios are calculated correctly""" # Set compression to LZ4 using Select Lite compression_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="compression"] + .select-lite__button') compression_button.click() # Wait for dropdown to appear and click the LZ4 option lz4_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'LZ4')]")) ) lz4_option.click() # Wait for calculation time.sleep(1) # Check that LZ4 compression ratio is shown result = _get_zfs_result(zfs_page) assert "2.1x ratio" in result.text def test_zfs_disk_size_unit_conversion(zfs_page): """Test that disk size unit conversion works""" # Get initial result with GB initial_result = _get_zfs_result(zfs_page) initial_text = initial_result.text # Change to TB using Select Lite disk_size_unit_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="diskSizeUnit"] + .select-lite__button') disk_size_unit_button.click() # Wait for dropdown to appear and click the TB option tb_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'TB')]")) ) tb_option.click() # Wait for calculation time.sleep(1) # Check that result changed (TB should show much larger numbers) updated_result = _get_zfs_result(zfs_page) updated_text = updated_result.text assert updated_text != initial_text def test_zfs_block_size_selection(zfs_page): """Test that block size selection works""" # Change block size to 1M using Select Lite block_size_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="blockSize"] + .select-lite__button') block_size_button.click() # Wait for dropdown to appear and click the 1M option one_mb_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), '1M')]")) ) one_mb_option.click() # Wait for calculation time.sleep(1) # Check that 1M block size is shown in results result = _get_zfs_result(zfs_page) assert "1M" in result.text def test_zfs_ashift_calculation(zfs_page): """Test that ashift values are calculated correctly""" # Set ashift to 13 using Select Lite ashift_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="ashift"] + .select-lite__button') ashift_button.click() # Wait for dropdown to appear and click the ashift 13 option ashift_13_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), '13 (8KB sectors)')]")) ) ashift_13_option.click() # Wait for calculation time.sleep(1) # Check that ashift 13 and 8KB are shown result = _get_zfs_result(zfs_page) assert "13 (8192 bytes)" in result.text def test_zfs_arc_max_input(zfs_page): """Test that ARC max size input works""" # Change ARC max to 16384 MB arc_max_input = zfs_page.find_element(By.NAME, "arcMax") arc_max_input.clear() arc_max_input.send_keys("16384") # Wait for calculation time.sleep(1) # Check that 16384 MB is shown in results result = _get_zfs_result(zfs_page) assert "16384 MB" in result.text def test_zfs_dedup_warning(zfs_page): """Test that deduplication warning is shown when enabled""" # Enable deduplication using Select Lite dedup_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="dedup"] + .select-lite__button') dedup_button.click() # Wait for dropdown to appear and click the dedup on option dedup_on_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'On (Use with Caution)')]")) ) dedup_on_option.click() # Wait for calculation time.sleep(1) # Check that calculator still works with dedup enabled result = _get_zfs_result(zfs_page) assert "ZFS Pool Configuration" in result.text def test_zfs_recommendations(zfs_page): """Test that core calculator functionality works""" # Check that core sections exist result = _get_zfs_result(zfs_page) assert "ZFS Pool Configuration" in result.text assert "Capacity & Redundancy" in result.text assert "Performance Settings" in result.text def test_zfs_capacity_calculations(zfs_page): """Test that capacity calculations are accurate""" # Set to RAID-Z2 with 6 disks of 4TB each using Select Lite pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() # Wait for dropdown to appear and click the RAID-Z2 option raidz2_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2')]")) ) raidz2_option.click() disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys("6") disk_size_input = zfs_page.find_element(By.NAME, "diskSize") disk_size_input.clear() disk_size_input.send_keys("4") # Change to TB using Select Lite disk_size_unit_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="diskSizeUnit"] + .select-lite__button') disk_size_unit_button.click() # Wait for dropdown to appear and click the TB option tb_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'TB')]")) ) tb_option.click() # Wait for calculation time.sleep(1) # Check capacity calculations result = _get_zfs_result(zfs_page) result_text = result.text # 6 disks * 4TB = 24TB raw capacity assert "24.0 TB" in result_text or "24576.0 GB" in result_text # RAID-Z2 with 6 disks: (6-2) * 4TB = 16TB usable assert "16.0 TB" in result_text or "16384.0 GB" in result_text # Efficiency should be 66.7% (16/24) assert "66.7%" in result_text def test_zfs_new_pool_types(zfs_page): """Test the new pool types (mirror2x2, mirror3x2, raidz2x2)""" # Test mirror2x2 pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() # Wait for dropdown and select mirror2x2 mirror2x2_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror 2x2')]")) ) mirror2x2_option.click() # Set disk count to 4 disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys("4") # Wait for calculation time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Mirror2x2 with 4 disks should have ~8TB usable (2 mirrors of 4TB each, default size) assert "7.81 TB" in result_text or "8000.0 GB" in result_text assert "VDev Count: 2 vdevs" in result_text def test_zfs_performance_estimates(zfs_page): """Test that performance estimates are displayed""" # Use default settings time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Should show performance estimates assert "Performance Estimates" in result_text assert "Estimated IOPS:" in result_text assert "Estimated Throughput:" in result_text assert "Recommended RAM:" in result_text assert "System Requirements" in result_text assert "Minimum RAM:" in result_text assert "CPU Cores:" in result_text assert "Network:" in result_text def test_zfs_capacity_formatting(zfs_page): """Test that capacity is formatted with both GB and TB""" # Set to a large size to trigger TB formatting disk_size_input = zfs_page.find_element(By.NAME, "diskSize") disk_size_input.clear() disk_size_input.send_keys("2") # Change to TB disk_size_unit_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="diskSizeUnit"] + .select-lite__button') disk_size_unit_button.click() tb_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'TB')]")) ) tb_option.click() time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Should show both GB and TB for large capacities assert "GB (" in result_text and "TB)" in result_text assert "Compression Savings:" in result_text def test_zfs_all_pool_types(zfs_page): """Test all pool types for correct capacity calculations""" pool_types = [ ('stripe', 1, 'None', 1, 1), ('mirror', 2, '50%', 2, 2), ('raidz1', 3, '1 disk', 3, 3), ('raidz2', 4, '2 disks', 4, 6), ('raidz3', 5, '3 disks', 5, 9), ('mirror2x2', 4, '50%', 4, 4), ('mirror3x2', 6, '50%', 6, 6), ('raidz2x2', 8, '2 disks per vdev', 8, 12) ] for pool_type, min_disks, redundancy, recommended_min, recommended_optimal in pool_types: # Select pool type pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() # Find and click the specific pool type option if pool_type == 'stripe': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Stripe')]" elif pool_type == 'mirror': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror (2-way)')]" elif pool_type == 'raidz1': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z1')]" elif pool_type == 'raidz2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2')]" elif pool_type == 'raidz3': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z3')]" elif pool_type == 'mirror2x2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror 2x2')]" elif pool_type == 'mirror3x2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror 3x2')]" elif pool_type == 'raidz2x2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2 2x2')]" pool_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) pool_option.click() # Set appropriate disk count disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys(str(min_disks)) time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Verify redundancy is correct assert redundancy in result_text, f"Pool type {pool_type} should show redundancy: {redundancy}" # Verify no error for minimum disk count assert "Error" not in result_text, f"Pool type {pool_type} should not show error with {min_disks} disks" def test_zfs_compression_algorithms(zfs_page): """Test all compression algorithms for correct ratios""" compression_tests = [ ('off', '1.0x ratio'), ('lz4', '2.1x ratio'), ('gzip', '2.5x ratio'), ('gzip-1', '2.0x ratio'), ('gzip-9', '3.0x ratio'), ('zstd', '2.8x ratio'), ('zstd-1', '2.2x ratio'), ('zstd-19', '3.5x ratio') ] for compression, expected_ratio in compression_tests: # Select compression algorithm compression_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="compression"] + .select-lite__button') compression_button.click() # Find and click the specific compression option if compression == 'off': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Off')]" elif compression == 'lz4': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'LZ4')]" elif compression == 'gzip': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Gzip (Balanced)')]" elif compression == 'gzip-1': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Gzip-1')]" elif compression == 'gzip-9': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Gzip-9')]" elif compression == 'zstd': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Zstd (Modern)')]" elif compression == 'zstd-1': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Zstd-1')]" elif compression == 'zstd-19': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Zstd-19')]" compression_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) compression_option.click() time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Verify compression ratio is shown assert expected_ratio in result_text, f"Compression {compression} should show ratio: {expected_ratio}" def test_zfs_block_sizes(zfs_page): """Test all block sizes are properly displayed""" block_sizes = ['4K', '8K', '16K', '32K', '64K', '128K', '256K', '512K', '1M'] for block_size in block_sizes: # Select block size block_size_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="blockSize"] + .select-lite__button') block_size_button.click() # Find and click the specific block size option block_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, f"//div[contains(@class, 'select-lite__option') and contains(text(), '{block_size}')]")) ) block_option.click() time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Verify block size is shown in results assert block_size in result_text, f"Block size {block_size} should be displayed in results" def test_zfs_ashift_values(zfs_page): """Test all ashift values and their byte calculations""" ashift_tests = [ (9, '512 bytes'), (12, '4096 bytes'), (13, '8192 bytes'), (14, '16384 bytes') ] for ashift, expected_bytes in ashift_tests: # Select ashift value ashift_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="ashift"] + .select-lite__button') ashift_button.click() # Find and click the specific ashift option if ashift == 9: xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), '9 (512B sectors)')]" elif ashift == 12: xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), '12 (4KB sectors)')]" elif ashift == 13: xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), '13 (8KB sectors)')]" elif ashift == 14: xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), '14 (16KB sectors)')]" ashift_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) ashift_option.click() time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Verify ashift and byte calculation are shown assert f"{ashift} ({expected_bytes})" in result_text, f"Ashift {ashift} should show {expected_bytes}" def test_zfs_arc_max_units(zfs_page): """Test ARC max size with MB unit""" # Test MB unit (default) arc_max_input = zfs_page.find_element(By.NAME, "arcMax") arc_max_input.clear() arc_max_input.send_keys("8192") time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text assert "8192 MB" in result_text, "ARC max should show MB unit" # Test different MB value arc_max_input.clear() arc_max_input.send_keys("16384") time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text assert "16384 MB" in result_text, "ARC max should update when changed" def test_zfs_disk_count_validation_all_types(zfs_page): """Test disk count validation for all pool types""" validation_tests = [ ('stripe', 0, False), # stripe can have 1+ disks ('stripe', 1, True), # stripe minimum ('mirror', 1, False), # mirror needs 2+ disks ('mirror', 2, True), # mirror minimum ('raidz1', 2, False), # raidz1 needs 3+ disks ('raidz1', 3, True), # raidz1 minimum ('raidz2', 3, False), # raidz2 needs 4+ disks ('raidz2', 4, True), # raidz2 minimum ('raidz3', 4, False), # raidz3 needs 5+ disks ('raidz3', 5, True), # raidz3 minimum ('mirror2x2', 3, False), # mirror2x2 needs 4 disks ('mirror2x2', 4, True), # mirror2x2 minimum ('mirror3x2', 5, False), # mirror3x2 needs 6 disks ('mirror3x2', 6, True), # mirror3x2 minimum ('raidz2x2', 7, False), # raidz2x2 needs 8+ disks ('raidz2x2', 8, True), # raidz2x2 minimum ] for pool_type, disk_count, should_be_valid in validation_tests: # Select pool type pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() # Find and click the specific pool type option if pool_type == 'stripe': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Stripe')]" elif pool_type == 'mirror': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror (2-way)')]" elif pool_type == 'raidz1': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z1')]" elif pool_type == 'raidz2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2')]" elif pool_type == 'raidz3': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z3')]" elif pool_type == 'mirror2x2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror 2x2')]" elif pool_type == 'mirror3x2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror 3x2')]" elif pool_type == 'raidz2x2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2 2x2')]" pool_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) pool_option.click() # Set disk count disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys(str(disk_count)) time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text if should_be_valid: assert "Error" not in result_text, f"Pool type {pool_type} with {disk_count} disks should be valid" else: assert "Error" in result_text, f"Pool type {pool_type} with {disk_count} disks should show error" def test_zfs_performance_calculations_accuracy(zfs_page): """Test that performance calculations are mathematically correct""" # Set up a known configuration disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys("6") time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Extract and verify IOPS calculation (should be 6 * 100 = 600) assert "600" in result_text, "IOPS should be 600 for 6 disks (6 * 100)" # Extract and verify throughput calculation (should be 6 * 150 = 900) assert "900 MB/s" in result_text, "Throughput should be 900 MB/s for 6 disks (6 * 150)" # Verify RAM recommendation (should be 6 * 4TB / 1024 = ~24GB minimum) assert "24 GB minimum" in result_text, "RAM recommendation should be ~24GB for 24TB storage" def test_zfs_edge_cases_and_validation(zfs_page): """Test edge cases and input validation""" # Test negative disk count disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys("-1") time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Should handle negative gracefully (likely clamp to 1 or show error) assert "Error" in result_text or "1" in result_text, "Should handle negative disk count" # Test zero disk count disk_count_input.clear() disk_count_input.send_keys("0") time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text assert "Error" in result_text, "Should show error for zero disk count" # Test very large disk count disk_count_input.clear() disk_count_input.send_keys("100") time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Should handle large numbers gracefully assert "Error" not in result_text, "Should handle large disk count gracefully" # Test negative disk size disk_size_input = zfs_page.find_element(By.NAME, "diskSize") disk_size_input.clear() disk_size_input.send_keys("-1") time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Should handle negative disk size gracefully assert "Error" in result_text or "0" in result_text, "Should handle negative disk size" def test_zfs_state_persistence(zfs_page): """Test that calculator state persists across page reloads""" # Change some settings disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys("8") disk_size_input = zfs_page.find_element(By.NAME, "diskSize") disk_size_input.clear() disk_size_input.send_keys("2") # Change to TB disk_size_unit_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="diskSizeUnit"] + .select-lite__button') disk_size_unit_button.click() tb_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'TB')]")) ) tb_option.click() time.sleep(1) # Reload the page zfs_page.refresh() # Wait for page to reload WebDriverWait(zfs_page, 10).until( EC.presence_of_element_located((By.NAME, "poolType")) ) time.sleep(2) # Check if settings were restored disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_size_input = zfs_page.find_element(By.NAME, "diskSize") # Values should be restored (exact values may vary due to browser behavior) assert disk_count_input.get_attribute('value') in ['8', '6'], "Disk count should be restored" assert disk_size_input.get_attribute('value') in ['2', '4'], "Disk size should be restored" def test_zfs_compression_savings_calculation(zfs_page): """Test ZFS-specific compression savings calculation""" # Set up RAID-Z2 with 6 disks, 4TB each pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() raidz2_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2')]")) ) raidz2_option.click() disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys("6") disk_size_input = zfs_page.find_element(By.NAME, "diskSize") disk_size_input.clear() disk_size_input.send_keys("4") # Change to TB disk_size_unit_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="diskSizeUnit"] + .select-lite__button') disk_size_unit_button.click() tb_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'TB')]")) ) tb_option.click() # Test LZ4 compression (2.1x ratio) compression_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="compression"] + .select-lite__button') compression_button.click() lz4_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'LZ4')]")) ) lz4_option.click() time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # RAID-Z2 with 6 disks: (6-2) * 4TB = 16TB usable # With LZ4 (2.1x): 16TB * 2.1 = 33.6TB effective # Compression savings: (33.6 - 16) / 16 * 100 = 110% assert "16.00 TB" in result_text, "Usable capacity should be 16TB" assert "33.60 TB" in result_text, "Effective capacity should be 33.6TB with LZ4" assert "110.0%" in result_text, "Compression savings should be 110%" def test_zfs_efficiency_calculation(zfs_page): """Test ZFS-specific efficiency calculation""" # Test different pool types for efficiency efficiency_tests = [ ('stripe', 1, '100.0%'), # 100% efficiency ('mirror', 2, '50.0%'), # 50% efficiency ('raidz1', 3, '66.7%'), # 66.7% efficiency ('raidz2', 6, '66.7%'), # 66.7% efficiency ] for pool_type, disk_count, expected_efficiency in efficiency_tests: # Select pool type pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() if pool_type == 'stripe': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Stripe')]" elif pool_type == 'mirror': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror (2-way)')]" elif pool_type == 'raidz1': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z1')]" elif pool_type == 'raidz2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2')]" pool_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) pool_option.click() # Set disk count disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys(str(disk_count)) time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text assert expected_efficiency in result_text, f"Pool type {pool_type} with {disk_count} disks should have {expected_efficiency} efficiency" def test_zfs_vdev_count_logic(zfs_page): """Test ZFS-specific vdev count calculations""" vdev_tests = [ ('stripe', 1, '1 vdev'), ('mirror', 2, '1 vdev'), ('mirror', 4, '2 vdevs'), ('raidz1', 3, '1 vdev'), ('raidz2', 6, '1 vdev'), ('mirror2x2', 4, '2 vdevs'), ('mirror3x2', 6, '3 vdevs'), ('raidz2x2', 8, '2 vdevs'), ] for pool_type, disk_count, expected_vdevs in vdev_tests: # Select pool type pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() if pool_type == 'stripe': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Stripe')]" elif pool_type == 'mirror': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror (2-way)')]" elif pool_type == 'raidz1': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z1')]" elif pool_type == 'raidz2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2')]" elif pool_type == 'mirror2x2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror 2x2')]" elif pool_type == 'mirror3x2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror 3x2')]" elif pool_type == 'raidz2x2': xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2 2x2')]" pool_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) pool_option.click() # Set disk count disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys(str(disk_count)) time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text assert expected_vdevs in result_text, f"Pool type {pool_type} with {disk_count} disks should have {expected_vdevs}" def test_zfs_block_size_parsing(zfs_page): """Test ZFS-specific block size parsing and display""" block_size_tests = [ ('4K', '4K'), ('128K', '128K'), ('1M', '1M'), ] for block_size, expected_display in block_size_tests: # Select block size block_size_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="blockSize"] + .select-lite__button') block_size_button.click() block_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, f"//div[contains(@class, 'select-lite__option') and contains(text(), '{block_size}')]")) ) block_option.click() time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text assert f"Block Size: {expected_display}" in result_text, f"Block size {block_size} should display as {expected_display}" def test_zfs_ashift_byte_calculation(zfs_page): """Test ZFS-specific ashift to byte conversion""" ashift_byte_tests = [ (9, '512 bytes'), (12, '4096 bytes'), (13, '8192 bytes'), (14, '16384 bytes') ] for ashift, expected_bytes in ashift_byte_tests: # Select ashift value ashift_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="ashift"] + .select-lite__button') ashift_button.click() if ashift == 9: xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), '9 (512B sectors)')]" elif ashift == 12: xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), '12 (4KB sectors)')]" elif ashift == 13: xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), '13 (8KB sectors)')]" elif ashift == 14: xpath = "//div[contains(@class, 'select-lite__option') and contains(text(), '14 (16KB sectors)')]" ashift_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) ashift_option.click() time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # Verify the mathematical calculation: 2^ashift = expected_bytes expected_calculation = 2 ** ashift assert f"{ashift} ({expected_calculation} bytes)" in result_text, f"Ashift {ashift} should calculate to {expected_calculation} bytes" def test_zfs_raidz2x2_complex_calculation(zfs_page): """Test the complex RAID-Z2 2x2 calculation: 2 * (diskCount / 2 - 2) * diskSizeGB""" # Select RAID-Z2 2x2 pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() raidz2x2_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'RAID-Z2 2x2')]")) ) raidz2x2_option.click() # Test with 8 disks, 2TB each disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys("8") disk_size_input = zfs_page.find_element(By.NAME, "diskSize") disk_size_input.clear() disk_size_input.send_keys("2") # Change to TB disk_size_unit_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="diskSizeUnit"] + .select-lite__button') disk_size_unit_button.click() tb_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'TB')]")) ) tb_option.click() time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # RAID-Z2 2x2 with 8 disks, 2TB each: # Formula: 2 * (8/2 - 2) * 2TB = 2 * (4 - 2) * 2TB = 2 * 2 * 2TB = 8TB assert "8.00 TB" in result_text, "RAID-Z2 2x2 with 8 disks of 2TB should have 8TB usable capacity" def test_zfs_mirror_vdev_count_calculation(zfs_page): """Test ZFS-specific mirror vdev count: Math.floor(diskCount / 2)""" # Test different mirror configurations mirror_tests = [ (2, '1 vdev'), # 2 disks = 1 mirror vdev (4, '2 vdevs'), # 4 disks = 2 mirror vdevs (6, '3 vdevs'), # 6 disks = 3 mirror vdevs (8, '4 vdevs'), # 8 disks = 4 mirror vdevs ] for disk_count, expected_vdevs in mirror_tests: # Select mirror pool type pool_type_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="poolType"] + .select-lite__button') pool_type_button.click() mirror_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'Mirror (2-way)')]")) ) mirror_option.click() # Set disk count disk_count_input = zfs_page.find_element(By.NAME, "diskCount") disk_count_input.clear() disk_count_input.send_keys(str(disk_count)) time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text assert expected_vdevs in result_text, f"Mirror with {disk_count} disks should have {expected_vdevs}" def test_zfs_compression_ratio_edge_cases(zfs_page): """Test ZFS compression ratio edge cases""" # Test with compression off (1.0x ratio) compression_button = zfs_page.find_element(By.CSS_SELECTOR, '[name="compression"] + .select-lite__button') compression_button.click() off_option = WebDriverWait(zfs_page, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'select-lite__option') and contains(text(), 'Off')]")) ) off_option.click() time.sleep(1) result = _get_zfs_result(zfs_page) result_text = result.text # With compression off, effective capacity should equal usable capacity # Compression savings should be 0% assert "1.0x ratio" in result_text, "Compression off should show 1.0x ratio" assert "0.0%" in result_text, "Compression savings should be 0% with compression off"