feat: expand index
This commit is contained in:
1 parent
9f207fd036
commit
5c72201c45
1 file changed
+154
+154
@@ -1423,3 +1423,157 @@ test "ForeignKeySet add remove save load round trip" {
|
||||
try std.testing.expect(!third.has("customer_id"));
|
||||
}
|
||||
|
||||
test "ForeignKeySet.load rejects a corrupted onDelete byte instead of accepting garbage" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
const dbPath = try tmp.dir.realpathAlloc(allocator, ".");
|
||||
defer allocator.free(dbPath);
|
||||
|
||||
var db = try Db.open(allocator, dbPath, .{});
|
||||
defer db.close();
|
||||
|
||||
var set = try ForeignKeySet.load(allocator, &db, "orders");
|
||||
defer set.deinit();
|
||||
try set.add("customer_id", "customers", "__pk", .restrict);
|
||||
try set.save(&db, "orders");
|
||||
|
||||
const key = try foreignKeyMetaKey(allocator, "orders");
|
||||
defer allocator.free(key);
|
||||
const raw = try db.get(allocator, key) orelse return error.TestUnexpectedResult;
|
||||
defer allocator.free(raw);
|
||||
|
||||
// Flip the trailing onDelete byte to a value neither `restrict` (0)
|
||||
// nor `cascade` (1) can ever legitimately be - same "malformed input
|
||||
// is rejected, not silently coerced" contract every other decoder in
|
||||
// this file already gives length-prefixed strings.
|
||||
const corrupted = try allocator.dupe(u8, raw);
|
||||
defer allocator.free(corrupted);
|
||||
corrupted[corrupted.len - 1] = 0xFF;
|
||||
try db.put(key, corrupted);
|
||||
|
||||
try std.testing.expectError(error.Malformed, ForeignKeySet.load(allocator, &db, "orders"));
|
||||
}
|
||||
|
||||
test "ReferencedBySet add replaces an existing (childTable, childField) pair instead of duplicating it" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
const dbPath = try tmp.dir.realpathAlloc(allocator, ".");
|
||||
defer allocator.free(dbPath);
|
||||
|
||||
var db = try Db.open(allocator, dbPath, .{});
|
||||
defer db.close();
|
||||
|
||||
var set = try ReferencedBySet.load(allocator, &db, "customers");
|
||||
defer set.deinit();
|
||||
try std.testing.expectEqual(@as(usize, 0), set.specs.items.len);
|
||||
|
||||
try set.add("orders", "customer_id", "__pk", .restrict);
|
||||
try set.add("orders", "customer_id", "__pk", .restrict);
|
||||
try std.testing.expectEqual(@as(usize, 1), set.specs.items.len);
|
||||
try set.add("line_items", "customer_id", "__pk", .cascade);
|
||||
try std.testing.expectEqual(@as(usize, 2), set.specs.items.len);
|
||||
try set.save(&db, "customers");
|
||||
|
||||
var reloaded = try ReferencedBySet.load(allocator, &db, "customers");
|
||||
defer reloaded.deinit();
|
||||
try std.testing.expectEqual(@as(usize, 2), reloaded.specs.items.len);
|
||||
for (reloaded.specs.items) |spec| {
|
||||
if (std.mem.eql(u8, spec.childTable, "line_items")) {
|
||||
try std.testing.expectEqual(OnDelete.cascade, spec.onDelete);
|
||||
} else {
|
||||
try std.testing.expectEqual(OnDelete.restrict, spec.onDelete);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-adding the same pair under a different refField AND onDelete mode
|
||||
// replaces both in place rather than accumulating a second, now-stale
|
||||
// entry.
|
||||
try reloaded.add("orders", "customer_id", "email", .cascade);
|
||||
try std.testing.expectEqual(@as(usize, 2), reloaded.specs.items.len);
|
||||
var sawOrders = false;
|
||||
for (reloaded.specs.items) |spec| {
|
||||
if (std.mem.eql(u8, spec.childTable, "orders")) {
|
||||
sawOrders = true;
|
||||
try std.testing.expectEqualStrings("email", spec.refField);
|
||||
try std.testing.expectEqual(OnDelete.cascade, spec.onDelete);
|
||||
}
|
||||
}
|
||||
try std.testing.expect(sawOrders);
|
||||
|
||||
try std.testing.expect(reloaded.remove("orders", "customer_id"));
|
||||
try std.testing.expect(!reloaded.remove("orders", "customer_id"));
|
||||
try reloaded.save(&db, "customers");
|
||||
|
||||
var third = try ReferencedBySet.load(allocator, &db, "customers");
|
||||
defer third.deinit();
|
||||
try std.testing.expectEqual(@as(usize, 1), third.specs.items.len);
|
||||
try std.testing.expectEqualStrings("line_items", third.specs.items[0].childTable);
|
||||
}
|
||||
|
||||
test "CheckConstraintSet add remove save load round trip" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
const dbPath = try tmp.dir.realpathAlloc(allocator, ".");
|
||||
defer allocator.free(dbPath);
|
||||
|
||||
var db = try Db.open(allocator, dbPath, .{});
|
||||
defer db.close();
|
||||
|
||||
var set = try CheckConstraintSet.load(allocator, &db, "orders");
|
||||
defer set.deinit();
|
||||
try std.testing.expectEqual(@as(usize, 0), set.specs.items.len);
|
||||
|
||||
try set.add("qty_positive", "qty gt 0");
|
||||
try set.add("qty_positive", "qty gt 100");
|
||||
try std.testing.expectEqual(@as(usize, 1), set.specs.items.len);
|
||||
try set.save(&db, "orders");
|
||||
|
||||
var reloaded = try CheckConstraintSet.load(allocator, &db, "orders");
|
||||
defer reloaded.deinit();
|
||||
try std.testing.expect(reloaded.has("qty_positive"));
|
||||
try std.testing.expect(!reloaded.has("missing"));
|
||||
const spec = reloaded.find("qty_positive").?;
|
||||
try std.testing.expectEqualStrings("qty gt 0", spec.source);
|
||||
|
||||
try std.testing.expect(reloaded.remove("qty_positive"));
|
||||
try std.testing.expect(!reloaded.remove("qty_positive"));
|
||||
try reloaded.save(&db, "orders");
|
||||
|
||||
var third = try CheckConstraintSet.load(allocator, &db, "orders");
|
||||
defer third.deinit();
|
||||
try std.testing.expect(!third.has("qty_positive"));
|
||||
}
|
||||
|
||||
test "ForeignKeySet, UniqueSet, and IndexSet on the same table never collide" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
const dbPath = try tmp.dir.realpathAlloc(allocator, ".");
|
||||
defer allocator.free(dbPath);
|
||||
|
||||
var db = try Db.open(allocator, dbPath, .{});
|
||||
defer db.close();
|
||||
|
||||
var indexes = try IndexSet.load(allocator, &db, "orders");
|
||||
defer indexes.deinit();
|
||||
try indexes.add("customer_id");
|
||||
try indexes.save(&db, "orders");
|
||||
|
||||
var unique = try UniqueSet.load(allocator, &db, "orders");
|
||||
defer unique.deinit();
|
||||
try unique.add("order_number");
|
||||
try unique.save(&db, "orders");
|
||||
|
||||
var fkeys = try ForeignKeySet.load(allocator, &db, "orders");
|
||||
defer fkeys.deinit();
|
||||
try fkeys.add("customer_id", "customers", "__pk", .restrict);
|
||||
try fkeys.save(&db, "orders");
|
||||
|
||||
var checks = try CheckConstraintSet.load(allocator, &db, "orders");
|
||||
defer checks.deinit();
|
||||
try checks.add("qty_positive", "qty gt 0");
|
||||
try checks.save(&db, "orders");
|
||||
|
||||
Reference in new issue
Block a user