milestone 27: diagnostics — operational failures land in one curated log, resolved history purgeable
Gates / frontend (push) Successful in 1m33s
Gates / test (push) Successful in 1m48s
Gates / test-aarch64 (push) Successful in 7m10s
Gates / package (push) Successful in 5m31s
Gates / container (push) Successful in 15s
CI / gates (push) Successful in 14m51s
Gates / frontend (push) Successful in 1m33s
Gates / test (push) Successful in 1m48s
Gates / test-aarch64 (push) Successful in 7m10s
Gates / package (push) Successful in 5m31s
Gates / container (push) Successful in 15s
CI / gates (push) Successful in 14m51s
This commit is contained in:
@@ -151,6 +151,7 @@ fn reportDeletes(diags: *validate.Diagnostics, summary: reconcile.Summary) error
|
||||
|
||||
const testing = std.testing;
|
||||
const config_schema = @import("../storage/config_schema.zig");
|
||||
const export_mod = @import("export.zig");
|
||||
const migrations = @import("../storage/migrations.zig");
|
||||
|
||||
fn openMigrated() !db.Db {
|
||||
@@ -282,6 +283,55 @@ test "importSource converges a migrated database and group 'default' keeps id 1"
|
||||
);
|
||||
}
|
||||
|
||||
test "an export and re-import leaves the operational_events log untouched" {
|
||||
// `operational_events` is runtime state, not configuration: it is out of
|
||||
// `table_names` and out of `delete_order`, so an export must not emit it and
|
||||
// an import's wipe must not reach it. A diagnostics log destroyed by a
|
||||
// routine `nxdns import` would take the record of what the box has been
|
||||
// doing with it.
|
||||
var threaded: std.Io.Threaded = .init(testing.allocator, .{});
|
||||
defer threaded.deinit();
|
||||
const io = threaded.io();
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var database = try openMigrated();
|
||||
defer database.close();
|
||||
try importText(io, &database, full_source, .{});
|
||||
try database.exec(
|
||||
\\INSERT INTO operational_events
|
||||
\\ (code, subject_key, subject_label, severity, first_seen, last_seen, occurrences, resolved_at)
|
||||
\\VALUES ('disk.space', 'data', 'data', 'warning', 100, 200, 3, NULL),
|
||||
\\ ('blocklist.refresh', 'https://a.example', 'A', 'warning', 100, 150, 1, 300);
|
||||
);
|
||||
|
||||
var rendered: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer rendered.deinit();
|
||||
try export_mod.writeToWriter(gpa, &database, &rendered.writer);
|
||||
const source = try gpa.dupeZ(u8, rendered.written());
|
||||
defer gpa.free(source);
|
||||
|
||||
// The export is the whole declared configuration and says nothing about
|
||||
// the log.
|
||||
try testing.expect(!std.mem.containsAtLeast(u8, source, 1, "operational_events"));
|
||||
try testing.expect(!std.mem.containsAtLeast(u8, source, 1, "disk.space"));
|
||||
|
||||
// A round trip is by definition delete-free for the config tables, and the
|
||||
// events survive it untouched, resolved and active alike.
|
||||
try importText(io, &database, source, .{ .allow_delete = true });
|
||||
var stmt = try database.prepare(
|
||||
"SELECT code, occurrences, resolved_at FROM operational_events ORDER BY id",
|
||||
);
|
||||
defer stmt.deinit();
|
||||
try testing.expect(try stmt.step());
|
||||
try testing.expectEqualStrings("disk.space", stmt.columnText(0));
|
||||
try testing.expectEqual(@as(i64, 3), stmt.columnInt(1));
|
||||
try testing.expect(stmt.isNull(2));
|
||||
try testing.expect(try stmt.step());
|
||||
try testing.expectEqualStrings("blocklist.refresh", stmt.columnText(0));
|
||||
try testing.expectEqual(@as(i64, 300), stmt.columnInt(2));
|
||||
try testing.expect(!try stmt.step());
|
||||
}
|
||||
|
||||
test "an import whose diff deletes rows is refused, names the tables, and changes nothing" {
|
||||
// Ruling 6: the emptiness guard is gone, so this is what stops
|
||||
// `nxdns import ./wrong.zon` from emptying a configured database.
|
||||
|
||||
Reference in New Issue
Block a user