feat: expand collections
This commit is contained in:
1 parent
c281644719
commit
8ad8dd3e8c
1 file changed
+159
@@ -1262,3 +1262,162 @@ test "list range supports negative indices redis style" {
|
||||
try std.testing.expectEqual(@as(usize, 5), clamped.len);
|
||||
}
|
||||
|
||||
test "zset add score incrBy remove rank" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
try ZSet.add(&db, allocator, "leaderboard", "alice", 10);
|
||||
try ZSet.add(&db, allocator, "leaderboard", "bob", 20);
|
||||
try ZSet.add(&db, allocator, "leaderboard", "carol", 5);
|
||||
|
||||
try std.testing.expectEqual(@as(f64, 10), (try ZSet.score(&db, allocator, "leaderboard", "alice")).?);
|
||||
try std.testing.expectEqual(@as(usize, 3), try ZSet.card(&db, allocator, "leaderboard"));
|
||||
|
||||
const newScore = try ZSet.incrBy(&db, allocator, "leaderboard", "alice", 100);
|
||||
try std.testing.expectEqual(@as(f64, 110), newScore);
|
||||
|
||||
// Ranks ascending by score: carol(5), bob(20), alice(110).
|
||||
try std.testing.expectEqual(@as(usize, 0), (try ZSet.rank(&db, allocator, "leaderboard", "carol")).?);
|
||||
try std.testing.expectEqual(@as(usize, 2), (try ZSet.rank(&db, allocator, "leaderboard", "alice")).?);
|
||||
try std.testing.expect(try ZSet.rank(&db, allocator, "leaderboard", "missing") == null);
|
||||
|
||||
try std.testing.expect(try ZSet.remove(&db, allocator, "leaderboard", "bob"));
|
||||
try std.testing.expect(!try ZSet.remove(&db, allocator, "leaderboard", "bob"));
|
||||
try std.testing.expectEqual(@as(usize, 2), try ZSet.card(&db, allocator, "leaderboard"));
|
||||
}
|
||||
|
||||
test "zset rangeByRank and rangeByScore return ascending order" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
try ZSet.add(&db, allocator, "z", "a", -5.5);
|
||||
try ZSet.add(&db, allocator, "z", "b", 0);
|
||||
try ZSet.add(&db, allocator, "z", "c", 3.25);
|
||||
try ZSet.add(&db, allocator, "z", "d", 100);
|
||||
|
||||
const byRank = try ZSet.rangeByRank(&db, allocator, "z", 0, -1);
|
||||
defer {
|
||||
for (byRank) |m| m.free(allocator);
|
||||
allocator.free(byRank);
|
||||
}
|
||||
try std.testing.expectEqual(@as(usize, 4), byRank.len);
|
||||
try std.testing.expectEqualStrings("a", byRank[0].member);
|
||||
try std.testing.expectEqualStrings("d", byRank[3].member);
|
||||
|
||||
const top2 = try ZSet.rangeByRank(&db, allocator, "z", -2, -1);
|
||||
defer {
|
||||
for (top2) |m| m.free(allocator);
|
||||
allocator.free(top2);
|
||||
}
|
||||
try std.testing.expectEqual(@as(usize, 2), top2.len);
|
||||
try std.testing.expectEqualStrings("c", top2[0].member);
|
||||
try std.testing.expectEqualStrings("d", top2[1].member);
|
||||
|
||||
const byScore = try ZSet.rangeByScore(&db, allocator, "z", -1.0, 50.0);
|
||||
defer {
|
||||
for (byScore) |m| m.free(allocator);
|
||||
allocator.free(byScore);
|
||||
}
|
||||
try std.testing.expectEqual(@as(usize, 2), byScore.len);
|
||||
try std.testing.expectEqualStrings("b", byScore[0].member);
|
||||
try std.testing.expectEqualStrings("c", byScore[1].member);
|
||||
}
|
||||
|
||||
test "Kind.parse accepts the five CLI names and rejects the rest" {
|
||||
try std.testing.expectEqual(Kind.counter, Kind.parse("counter").?);
|
||||
try std.testing.expectEqual(Kind.hash, Kind.parse("hash").?);
|
||||
try std.testing.expectEqual(Kind.set, Kind.parse("set").?);
|
||||
try std.testing.expectEqual(Kind.list, Kind.parse("list").?);
|
||||
try std.testing.expectEqual(Kind.zset, Kind.parse("zset").?);
|
||||
try std.testing.expect(Kind.parse("bogus") == null);
|
||||
}
|
||||
|
||||
test "Kind.fromTagByte is the exact inverse of tagByte for all five kinds" {
|
||||
const kinds = [_]Kind{ .counter, .hash, .set, .list, .zset };
|
||||
for (kinds) |k| {
|
||||
try std.testing.expectEqual(k, Kind.fromTagByte(k.tagByte()).?);
|
||||
}
|
||||
try std.testing.expect(Kind.fromTagByte('?') == null);
|
||||
}
|
||||
|
||||
test "exists and drop cover counter (single key, no trailing separator)" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
try std.testing.expect(!try collectionExists(&db, allocator, .counter, "hits"));
|
||||
_ = try Counter.incrBy(&db, allocator, "hits", 0);
|
||||
try std.testing.expect(try collectionExists(&db, allocator, .counter, "hits"));
|
||||
|
||||
// A same-prefixed name must not be swept up by the drop of "hits" - the
|
||||
// counter key has no trailing separator, so this is the one case where a
|
||||
// naive prefix scan (instead of an exact key) would over-delete.
|
||||
_ = try Counter.incrBy(&db, allocator, "hitsExtra", 7);
|
||||
|
||||
try std.testing.expectEqual(@as(usize, 1), try dropCollection(&db, allocator, .counter, "hits"));
|
||||
try std.testing.expect(!try collectionExists(&db, allocator, .counter, "hits"));
|
||||
try std.testing.expectEqual(@as(i64, 7), try Counter.get(&db, allocator, "hitsExtra"));
|
||||
}
|
||||
|
||||
test "drop removes every field of a hash and every element+meta of a list" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
_ = try Hash.set(&db, allocator, "user:1", "name", "ada");
|
||||
_ = try Hash.set(&db, allocator, "user:1", "age", "36");
|
||||
try std.testing.expect(try collectionExists(&db, allocator, .hash, "user:1"));
|
||||
try std.testing.expectEqual(@as(usize, 2), try dropCollection(&db, allocator, .hash, "user:1"));
|
||||
try std.testing.expect(!try collectionExists(&db, allocator, .hash, "user:1"));
|
||||
try std.testing.expectEqual(@as(usize, 0), try Hash.len(&db, allocator, "user:1"));
|
||||
|
||||
try List.pushRight(&db, allocator, "queue", "a");
|
||||
try List.pushRight(&db, allocator, "queue", "b");
|
||||
try std.testing.expect(try collectionExists(&db, allocator, .list, "queue"));
|
||||
// 2 elements + 1 meta record.
|
||||
try std.testing.expectEqual(@as(usize, 3), try dropCollection(&db, allocator, .list, "queue"));
|
||||
try std.testing.expect(!try collectionExists(&db, allocator, .list, "queue"));
|
||||
try std.testing.expectEqual(@as(usize, 0), try List.len(&db, allocator, "queue"));
|
||||
|
||||
// Dropping something never created is a harmless no-op.
|
||||
try std.testing.expectEqual(@as(usize, 0), try dropCollection(&db, allocator, .set, "never-existed"));
|
||||
}
|
||||
|
||||
test "drop removes both the member row and score-index row of every zset entry" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
try ZSet.add(&db, allocator, "leaderboard", "alice", 10);
|
||||
try ZSet.add(&db, allocator, "leaderboard", "bob", 20);
|
||||
try std.testing.expect(try collectionExists(&db, allocator, .zset, "leaderboard"));
|
||||
// 2 members * (1 member row + 1 score-index row) = 4.
|
||||
try std.testing.expectEqual(@as(usize, 4), try dropCollection(&db, allocator, .zset, "leaderboard"));
|
||||
try std.testing.expect(!try collectionExists(&db, allocator, .zset, "leaderboard"));
|
||||
try std.testing.expectEqual(@as(usize, 0), try ZSet.card(&db, allocator, "leaderboard"));
|
||||
}
|
||||
|
||||
test "zset add is a no-op when the score does not change" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
try ZSet.add(&db, allocator, "z", "a", 5.0);
|
||||
try ZSet.add(&db, allocator, "z", "a", 5.0);
|
||||
try std.testing.expectEqual(@as(usize, 1), try ZSet.card(&db, allocator, "z"));
|
||||
try std.testing.expectEqual(@as(f64, 5.0), (try ZSet.score(&db, allocator, "z", "a")).?);
|
||||
}
|
||||
Reference in new issue
Block a user