Skip to content

Commit beff828

Browse files
committed
test: add binary data, size overwrite, and CAS conflict tests
- test_binary_values: verify non-UTF8 binary data round-trip - test_overwrite_different_sizes: small->large->small value overwrites - test_concurrent_cas_conflict: two concurrent CAS updates to same key, verify exactly one succeeds and one gets UnexpectedVersionId - Total: 45 integration tests + 12 unit tests + 1 doctest = 58 tests Signed-off-by: mattisonchao <mattisonchao@gmail.com>
1 parent aeafec9 commit beff828

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed

liboxia-native/tests/integration_tests.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,3 +1587,125 @@ async fn test_cas_loop() {
15871587
.unwrap();
15881588
client.shutdown().await.unwrap();
15891589
}
1590+
1591+
// ============================================================
1592+
// Binary (non-UTF8) data
1593+
// ============================================================
1594+
1595+
#[tokio::test]
1596+
async fn test_binary_values() {
1597+
let (_container, address) = start_oxia().await;
1598+
let client = new_client(&address).await;
1599+
1600+
// Store binary data that isn't valid UTF-8
1601+
let binary_data: Vec<u8> = vec![0x00, 0x01, 0xFF, 0xFE, 0x80, 0x90, 0xAB, 0xCD];
1602+
client
1603+
.put("bin/data".to_string(), binary_data.clone())
1604+
.await
1605+
.unwrap();
1606+
1607+
let result = client.get("bin/data".to_string()).await.unwrap();
1608+
assert_eq!(result.value, Some(binary_data));
1609+
1610+
client
1611+
.delete_range("bin/".to_string(), "bin/~".to_string())
1612+
.await
1613+
.unwrap();
1614+
client.shutdown().await.unwrap();
1615+
}
1616+
1617+
// ============================================================
1618+
// Overwrite with different value sizes
1619+
// ============================================================
1620+
1621+
#[tokio::test]
1622+
async fn test_overwrite_different_sizes() {
1623+
let (_container, address) = start_oxia().await;
1624+
let client = new_client(&address).await;
1625+
1626+
// Start with a small value
1627+
client
1628+
.put("sizes/key".to_string(), b"small".to_vec())
1629+
.await
1630+
.unwrap();
1631+
1632+
// Overwrite with a larger value
1633+
let large_value = vec![b'x'; 10000];
1634+
client
1635+
.put("sizes/key".to_string(), large_value.clone())
1636+
.await
1637+
.unwrap();
1638+
1639+
let result = client.get("sizes/key".to_string()).await.unwrap();
1640+
assert_eq!(result.value, Some(large_value));
1641+
1642+
// Overwrite back to a small value
1643+
client
1644+
.put("sizes/key".to_string(), b"tiny".to_vec())
1645+
.await
1646+
.unwrap();
1647+
1648+
let result = client.get("sizes/key".to_string()).await.unwrap();
1649+
assert_eq!(result.value, Some(b"tiny".to_vec()));
1650+
1651+
client
1652+
.delete_range("sizes/".to_string(), "sizes/~".to_string())
1653+
.await
1654+
.unwrap();
1655+
client.shutdown().await.unwrap();
1656+
}
1657+
1658+
// ============================================================
1659+
// Concurrent CAS conflict detection
1660+
// ============================================================
1661+
1662+
#[tokio::test]
1663+
async fn test_concurrent_cas_conflict() {
1664+
let (_container, address) = start_oxia().await;
1665+
let client = new_client(&address).await;
1666+
1667+
// Create a key
1668+
let initial = client
1669+
.put("conflict/key".to_string(), b"v0".to_vec())
1670+
.await
1671+
.unwrap();
1672+
let version = initial.version.version_id;
1673+
1674+
// Two concurrent CAS updates with the same version - one should fail
1675+
let c1 = client.clone();
1676+
let c2 = client.clone();
1677+
1678+
let h1 = tokio::spawn(async move {
1679+
c1.put_with_options(
1680+
"conflict/key".to_string(),
1681+
b"v1-from-c1".to_vec(),
1682+
vec![PutOption::ExpectVersionId(version)],
1683+
)
1684+
.await
1685+
});
1686+
1687+
let h2 = tokio::spawn(async move {
1688+
c2.put_with_options(
1689+
"conflict/key".to_string(),
1690+
b"v1-from-c2".to_vec(),
1691+
vec![PutOption::ExpectVersionId(version)],
1692+
)
1693+
.await
1694+
});
1695+
1696+
let r1 = h1.await.unwrap();
1697+
let r2 = h2.await.unwrap();
1698+
1699+
// Exactly one should succeed and one should fail with UnexpectedVersionId
1700+
let (successes, failures): (Vec<_>, Vec<_>) = vec![r1, r2].into_iter().partition(|r| r.is_ok());
1701+
1702+
assert_eq!(successes.len(), 1, "Exactly one CAS should succeed");
1703+
assert_eq!(failures.len(), 1, "Exactly one CAS should fail");
1704+
assert!(matches!(failures[0], Err(OxiaError::UnexpectedVersionId())));
1705+
1706+
client
1707+
.delete_range("conflict/".to_string(), "conflict/~".to_string())
1708+
.await
1709+
.unwrap();
1710+
client.shutdown().await.unwrap();
1711+
}

0 commit comments

Comments
 (0)