package com.cube.index;
import org.junit.jupiter.api.*;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests for Cubic Index System
*/
public class CubicIndexTest {
@Test
public void testCubicIndexCalculation() {
// Test cubic index formula: N³ × 6
assertEquals(6, CubicIndexNode.calculateCubicIndex(1)); // 1³×6 = 6
assertEquals(48, CubicIndexNode.calculateCubicIndex(2)); // 2³×6 = 48
assertEquals(162, CubicIndexNode.calculateCubicIndex(3)); // 3³×6 = 162
assertEquals(384, CubicIndexNode.calculateCubicIndex(4)); // 4³×6 = 384
assertEquals(750, CubicIndexNode.calculateCubicIndex(5)); // 5³×6 = 750
assertEquals(1296, CubicIndexNode.calculateCubicIndex(6)); // 6³×6 = 1296
}
@Test
public void testLevelCalculation() {
// Test calculating level from index value
assertEquals(1, CubicIndexNode.calculateLevel(6));
assertEquals(2, CubicIndexNode.calculateLevel(48));
assertEquals(3, CubicIndexNode.calculateLevel(162));
assertEquals(4, CubicIndexNode.calculateLevel(384));
assertEquals(5, CubicIndexNode.calculateLevel(750));
}
@Test
public void testSideDetermination() {
// Test that keys are consistently mapped to sides
String key1 = "test-key-1";
CubicIndexNode.Side side1 = CubicIndexNode.determineSide(key1);
assertNotNull(side1);
// Same key should always map to same side
assertEquals(side1, CubicIndexNode.determineSide(key1));
// Test all possible sides
Set<CubicIndexNode.Side> seenSides = new HashSet<>();
for (int i = 0; i < 100; i++) {
CubicIndexNode.Side side = CubicIndexNode.determineSide("key-" + i);
seenSides.add(side);
}
// With 100 keys, we should see multiple sides
assertTrue(seenSides.size() > 1, "Keys should distribute across multiple sides");
}
@Test
public void testCubicNodeCreation() {
CubicIndexNode node = new CubicIndexNode(3);
assertEquals(3, node.getLevel());
assertEquals(162, node.getIndexValue());
assertEquals(0, node.getTotalSize());
}
@Test
public void testCubicNodePutAndGet() {
CubicIndexNode node = new CubicIndexNode(2);
// Put data
node.put("key1", "value1".getBytes());
node.put("key2", "value2".getBytes());
node.put("key3", "value3".getBytes());
// Get data
assertArrayEquals("value1".getBytes(), node.get("key1"));
assertArrayEquals("value2".getBytes(), node.get("key2"));
assertArrayEquals("value3".getBytes(), node.get("key3"));
// Total size
assertEquals(3, node.getTotalSize());
}
@Test
public void testCubicNodeSideDistribution() {
CubicIndexNode node = new CubicIndexNode(3);
// Add many keys
for (int i = 0; i < 60; i++) {
node.put("key-" + i, ("value-" + i).getBytes());
}
assertEquals(60, node.getTotalSize());
// Check that keys are distributed across sides
Map<String, Object> stats = node.getStats();
@SuppressWarnings("unchecked")
Map<String, Integer> sideDistribution = (Map<String, Integer>) stats.get("sideDistribution");
assertNotNull(sideDistribution);
assertEquals(6, sideDistribution.size());
// At least some sides should have keys
long nonEmptySides = sideDistribution.values().stream().filter(count -> count > 0).count();
assertTrue(nonEmptySides > 1, "Keys should be distributed across multiple sides");
}
@Test
public void testCubicIndexTree() {
CubicIndexTree tree = new CubicIndexTree(3, 10, true);
// Put data
tree.put("user:1", "Alice".getBytes());
tree.put("user:2", "Bob".getBytes());
tree.put("user:3", "Charlie".getBytes());
tree.put("product:1", "Laptop".getBytes());
tree.put("product:2", "Mouse".getBytes());
// Get data
assertArrayEquals("Alice".getBytes(), tree.get("user:1"));
assertArrayEquals("Bob".getBytes(), tree.get("user:2"));
assertArrayEquals("Laptop".getBytes(), tree.get("product:1"));
// Total size
assertEquals(5, tree.getTotalSize());
}
@Test
public void testPrefixSearch() {
CubicIndexTree tree = new CubicIndexTree(3, 10, true);
// Add data
tree.put("user:1:name", "Alice".getBytes());
tree.put("user:1:email", "alice@example.com".getBytes());
tree.put("user:2:name", "Bob".getBytes());
tree.put("user:2:email", "bob@example.com".getBytes());
tree.put("product:1", "Laptop".getBytes());
// Search by prefix
List<String> userKeys = tree.searchPrefix("user:");
assertEquals(4, userKeys.size());
assertTrue(userKeys.contains("user:1:name"));
assertTrue(userKeys.contains("user:1:email"));
List<String> user1Keys = tree.searchPrefix("user:1");
assertEquals(2, user1Keys.size());
List<String> productKeys = tree.searchPrefix("product:");
assertEquals(1, productKeys.size());
}
@Test
public void testRangeSearch() {
CubicIndexTree tree = new CubicIndexTree(3, 10, true);
// Add sequential keys
for (int i = 0; i < 20; i++) {
tree.put(String.format("key-%03d", i), ("value-" + i).getBytes());
}
// Range search
List<String> range = tree.searchRange("key-005", "key-010");
assertTrue(range.size() >= 6); // At least 005-010
assertTrue(range.contains("key-005"));
assertTrue(range.contains("key-010"));
}
@Test
public void testAutoExpansion() {
CubicIndexTree tree = new CubicIndexTree(2, 10, true);
assertEquals(2, tree.getLevelCount());
// Add enough data to potentially trigger expansion
for (int i = 0; i < 100; i++) {
tree.put("key-" + i, ("value-" + i).getBytes());
}
// Tree should maintain or expand levels
assertTrue(tree.getLevelCount() >= 2);
}
@Test
public void testRebalance() {
CubicIndexTree tree = new CubicIndexTree(3, 10, true);
// Add data
for (int i = 0; i < 50; i++) {
tree.put("key-" + i, ("value-" + i).getBytes());
}
int beforeSize = tree.getTotalSize();
// Rebalance
tree.rebalance();
// Size should remain the same
assertEquals(beforeSize, tree.getTotalSize());
// Data should still be accessible
assertArrayEquals("value-0".getBytes(), tree.get("key-0"));
assertArrayEquals("value-25".getBytes(), tree.get("key-25"));
}
@Test
public void testRemove() {
CubicIndexTree tree = new CubicIndexTree(3, 10, true);
tree.put("key1", "value1".getBytes());
tree.put("key2", "value2".getBytes());
assertEquals(2, tree.getTotalSize());
// Remove
assertTrue(tree.remove("key1"));
assertEquals(1, tree.getTotalSize());
assertNull(tree.get("key1"));
assertArrayEquals("value2".getBytes(), tree.get("key2"));
// Remove non-existent key
assertFalse(tree.remove("nonexistent"));
}
@Test
public void testGetAllKeys() {
CubicIndexTree tree = new CubicIndexTree(3, 10, true);
tree.put("key1", "value1".getBytes());
tree.put("key2", "value2".getBytes());
tree.put("key3", "value3".getBytes());
Set<String> allKeys = tree.getAllKeys();
assertEquals(3, allKeys.size());
assertTrue(allKeys.contains("key1"));
assertTrue(allKeys.contains("key2"));
assertTrue(allKeys.contains("key3"));
}
@Test
public void testCubicIndexStatistics() {
CubicIndexTree tree = new CubicIndexTree(5, 20, true);
// Add data
for (int i = 0; i < 100; i++) {
tree.put("key-" + i, ("value-" + i).getBytes());
}
Map<String, Object> stats = tree.getStats();
assertNotNull(stats);
assertTrue((Integer) stats.get("totalKeys") > 0);
assertTrue((Integer) stats.get("totalLevels") > 0);
assertNotNull(stats.get("sideDistribution"));
assertNotNull(stats.get("levels"));
}
@Test
public void testSideEnumeration() {
// Test all 6 sides exist
CubicIndexNode.Side[] sides = CubicIndexNode.Side.values();
assertEquals(6, sides.length);
// Test side names
assertEquals("FRONT", CubicIndexNode.Side.FRONT.name());
assertEquals("BACK", CubicIndexNode.Side.BACK.name());
assertEquals("LEFT", CubicIndexNode.Side.LEFT.name());
assertEquals("RIGHT", CubicIndexNode.Side.RIGHT.name());
assertEquals("TOP", CubicIndexNode.Side.TOP.name());
assertEquals("BOTTOM", CubicIndexNode.Side.BOTTOM.name());
// Test side indices
assertEquals(0, CubicIndexNode.Side.FRONT.getIndex());
assertEquals(5, CubicIndexNode.Side.BOTTOM.getIndex());
}
}