milestone 33: contract closure — samples, file-authority enumeration, dead code, bundle ceiling
Gates / frontend (push) Successful in 1m34s
Gates / test (push) Successful in 2m3s
Gates / test-aarch64 (push) Failing after 3h13m33s
Gates / package (push) Successful in 5m20s
Gates / container (push) Successful in 15s
CI / gates (push) Failing after 6h30m45s

This commit is contained in:
2026-08-22 23:31:37 +02:00
parent 5da4652e89
commit cc23c97218
49 changed files with 397 additions and 113 deletions
+43 -13
View File
@@ -1174,7 +1174,7 @@ const managed_body = "{\"error\":\"configuration is managed by " ++ managed_path
/// do produce paths like this, and the old code answered them in `text/plain`.
const long_managed_path = "/mnt/" ++ ("deeply-nested-bind-mount/" ** 24) ++ "config.zon";
fn fileModeClasses(io: std.Io, env: *Env) anyerror!void {
fn fileModeConfigWrites(io: std.Io, env: *Env) anyerror!void {
var body_buf: [8192]u8 = undefined;
var conn: Conn = undefined;
try conn.connect(io, env.addr);
@@ -1185,21 +1185,34 @@ fn fileModeClasses(io: std.Io, env: *Env) anyerror!void {
var response = try conn.receive(&body_buf);
try testing.expectEqual(@as(u16, 200), response.status);
// Every class of configuration write answers the one envelope.
const writes = [_]struct { method: []const u8, target: []const u8, body: ?[]const u8 }{
.{ .method = "POST", .target = "/api/groups", .body = "{\"name\":\"kids\"}" },
.{ .method = "PUT", .target = "/api/settings", .body = "{\"dns\":{\"port\":5353}}" },
.{ .method = "PUT", .target = "/api/clients/1", .body = "{\"name\":\"x\",\"group_id\":1}" },
.{ .method = "DELETE", .target = "/api/upstreams/1", .body = null },
};
for (writes) |write| {
try conn.request(write.method, write.target, null, write.body);
// Every configuration write answers the one envelope — enumerated, not
// sampled. The cases come from the contract table because each entry
// carries a target and a body the handler would accept: a request the
// handler would reject anyway could answer 400 and still look like a pass.
var enumerated: usize = 0;
for (contract) |entry| {
if (entry.policy != .config_write) continue;
enumerated += 1;
try conn.request(@tagName(entry.method), entry.target, null, entry.body);
response = try conn.receive(&body_buf);
errdefer std.debug.print(
"{t} {s}: {d} {s}\n",
.{ entry.method, entry.target, response.status, response.body },
);
try testing.expectEqual(@as(u16, 403), response.status);
try testing.expectEqualStrings(managed_body, response.body);
try testing.expectEqualStrings("application/json", response.header("content-type").?);
}
// The served table is the authority on what a configuration write is, so a
// route added there cannot ship without a case here.
var served: usize = 0;
for (router.routes) |route| {
if (route.policy == .config_write) served += 1;
}
try testing.expectEqual(served, enumerated);
// Rejected before the handler, not after it: the group was never created.
try conn.request("GET", "/api/groups", null, null);
response = try conn.receive(&body_buf);
@@ -1377,7 +1390,7 @@ test "W10 milestone 20: file authority rejects configuration writes and spares t
var env = try Env.create(gpa, .{ .authority = .{ .managed_file = managed_path } });
defer env.destroy();
try bounded(env.io(), default_budget, fileModeClasses, .{ env.io(), env });
try bounded(env.io(), default_budget, fileModeConfigWrites, .{ env.io(), env });
}
fn fileModeClientDelete(io: std.Io, env: *Env) anyerror!void {
@@ -3610,8 +3623,10 @@ test "drift guard c: the stats schemas match the structs that serialize them" {
test "drift guard c: the query-log schemas match the structs that serialize them" {
const gpa = testing.allocator;
try expectSchemaMatches(gpa, queries_repo.QueryRow, "QueryRow");
// `Provenance` has no schema of its own: it is `QueryDetail` without the
// row id, and the live stream documents it in prose rather than a `$ref`
// no path could honestly point at.
try expectSchemaMatches(gpa, provenance_view.QueryDetail, "QueryDetail");
try expectSchemaMatches(gpa, provenance_view.Provenance, "Provenance");
try expectSchemaMatches(gpa, coverage_mod.Coverage, "Coverage");
}
@@ -3641,6 +3656,7 @@ test "drift guard c bites: a renamed, retyped or newly optional field fails it"
// The same drift behind a `$ref`, where the property carries no `type:` of
// its own: a nested object the server may now omit.
const NullableObject = struct {
id: i64,
request: provenance_view.Request,
group: ?provenance_view.Group,
policy: provenance_view.Policy,
@@ -3648,7 +3664,7 @@ test "drift guard c bites: a renamed, retyped or newly optional field fails it"
route: provenance_view.Route,
response: provenance_view.Response,
};
try testing.expectError(error.TestUnexpectedResult, expectSchemaMatches(gpa, NullableObject, "Provenance"));
try testing.expectError(error.TestUnexpectedResult, expectSchemaMatches(gpa, NullableObject, "QueryDetail"));
// And behind a `$ref` to an enum, whose values would still line up.
const NullableEnum = struct {
@@ -3704,6 +3720,13 @@ const ContractSample = struct {
/// route runs after the create that gave it a row (an empty array witnesses no
/// field at all), and `POST /api/blocklists/update` runs while the only source
/// row is disabled, so the pass syncs its status without fetching anything.
///
/// Every successful `.json` route is sampled except three, which carry no JSON
/// contract this file could pin: `/metrics` answers Prometheus text,
/// `/api/openapi.yaml` is served verbatim from the repo and guarded by the
/// drift tests below, and `/api/queries/live` is an open SSE stream rather than
/// one byte-comparable body — its frame payload is `QueryDetail` without the
/// id, already sampled through `get_query_detail`.
const contract_sample_walk = [_]ContractSample{
.{ .name = "get_health", .ts_type = "Health", .method = "GET", .target = "/api/health", .status = 200 },
.{ .name = "get_version", .ts_type = "Version", .method = "GET", .target = "/api/version", .status = 200 },
@@ -3722,6 +3745,7 @@ const contract_sample_walk = [_]ContractSample{
// Blocklists. The row is created disabled so the refresh below has a status
// to report and still downloads nothing.
.{ .name = "create_blocklist", .ts_type = "BlocklistEcho", .method = "POST", .target = "/api/blocklists", .body = "{\"url\":\"https://lists.example/ads.txt\",\"name\":\"ads\",\"enabled\":false}", .status = 201 },
.{ .name = "get_blocklist", .ts_type = "Blocklist", .method = "GET", .target = "/api/blocklists/1", .status = 200 },
.{ .name = "list_blocklists", .ts_type = "{ blocklists: Blocklist[] }", .method = "GET", .target = "/api/blocklists", .status = 200 },
.{ .name = "update_blocklist", .ts_type = "BlocklistEcho", .method = "PUT", .target = "/api/blocklists/1", .body = "{\"url\":\"https://lists.example/ads.txt\",\"name\":\"ads2\",\"enabled\":false}", .status = 200 },
.{ .name = "update_blocklists_now", .ts_type = "{ sources: SourceStatus[] }", .method = "POST", .target = "/api/blocklists/update", .body = "{}", .status = 202 },
@@ -3729,28 +3753,33 @@ const contract_sample_walk = [_]ContractSample{
// Groups. The migrated schema seeds `default` as id 1; the POST creates 2.
.{ .name = "list_groups", .ts_type = "{ groups: Group[] }", .method = "GET", .target = "/api/groups", .status = 200 },
.{ .name = "create_group", .ts_type = "Group", .method = "POST", .target = "/api/groups", .body = "{\"name\":\"kids\"}", .status = 201 },
.{ .name = "get_group", .ts_type = "Group", .method = "GET", .target = "/api/groups/2", .status = 200 },
.{ .name = "update_group", .ts_type = "Group", .method = "PUT", .target = "/api/groups/2", .body = "{\"name\":\"teens\",\"safe_search\":true}", .status = 200 },
.{ .name = "put_group_sources", .ts_type = "{ source_ids: number[] }", .method = "PUT", .target = "/api/groups/1/sources", .body = "{\"source_ids\":[1]}", .status = 200 },
.{ .name = "get_group_sources", .ts_type = "{ source_ids: number[] }", .method = "GET", .target = "/api/groups/1/sources", .status = 200 },
// Rules, then the lookup that the rule makes answer `blocked`.
.{ .name = "create_rule", .ts_type = "RuleEcho", .method = "POST", .target = "/api/rules", .body = "{\"group_id\":1,\"pattern\":\"ads.example\",\"kind\":\"exact\",\"action\":\"block\"}", .status = 201 },
.{ .name = "get_rule", .ts_type = "Rule", .method = "GET", .target = "/api/rules/1", .status = 200 },
.{ .name = "list_rules", .ts_type = "{ rules: Rule[] }", .method = "GET", .target = "/api/rules", .status = 200 },
.{ .name = "update_rule", .ts_type = "RuleEcho", .method = "PUT", .target = "/api/rules/1", .body = "{\"group_id\":1,\"pattern\":\"*.ads.example\",\"kind\":\"wildcard\",\"action\":\"block\"}", .status = 200 },
.{ .name = "get_lookup", .ts_type = "LookupResult", .method = "GET", .target = "/api/lookup?domain=sub.ads.example", .status = 200 },
// Local records.
.{ .name = "create_local_record", .ts_type = "LocalRecord", .method = "POST", .target = "/api/local-records", .body = "{\"name\":\"nas.lan\",\"rtype\":\"A\",\"value\":\"192.168.1.10\"}", .status = 201 },
.{ .name = "get_local_record", .ts_type = "LocalRecord", .method = "GET", .target = "/api/local-records/1", .status = 200 },
.{ .name = "list_local_records", .ts_type = "{ local_records: LocalRecord[] }", .method = "GET", .target = "/api/local-records", .status = 200 },
.{ .name = "update_local_record", .ts_type = "LocalRecord", .method = "PUT", .target = "/api/local-records/1", .body = "{\"name\":\"nas.lan\",\"rtype\":\"A\",\"value\":\"192.168.1.11\",\"ttl\":120}", .status = 200 },
// Forward zones.
.{ .name = "create_forward_zone", .ts_type = "ForwardZone", .method = "POST", .target = "/api/forward-zones", .body = "{\"zone\":\"lan\",\"resolver\":\"udp://10.0.0.1:53\"}", .status = 201 },
.{ .name = "get_forward_zone", .ts_type = "ForwardZone", .method = "GET", .target = "/api/forward-zones/1", .status = 200 },
.{ .name = "list_forward_zones", .ts_type = "{ forward_zones: ForwardZone[] }", .method = "GET", .target = "/api/forward-zones", .status = 200 },
.{ .name = "update_forward_zone", .ts_type = "ForwardZone", .method = "PUT", .target = "/api/forward-zones/1", .body = "{\"zone\":\"lan\",\"resolver\":\"udp://10.0.0.2:53\"}", .status = 200 },
// Clients (row id 1 is seeded — clients have no POST, ruling 9).
.{ .name = "list_clients", .ts_type = "{ clients: Client[] }", .method = "GET", .target = "/api/clients", .status = 200 },
.{ .name = "get_client", .ts_type = "Client", .method = "GET", .target = "/api/clients/1", .status = 200 },
.{ .name = "update_client", .ts_type = "Client", .method = "PUT", .target = "/api/clients/1", .body = "{\"name\":\"laptop-renamed\",\"group_id\":1}", .status = 200 },
.{ .name = "put_client_prefixes", .ts_type = "{ client_prefixes: ClientPrefix[] }", .method = "PUT", .target = "/api/client-prefixes", .body = "{\"client_prefixes\":[{\"prefix\":\"192.168.1.0/24\",\"group_id\":1}]}", .status = 200 },
.{ .name = "list_client_prefixes", .ts_type = "{ client_prefixes: ClientPrefix[] }", .method = "GET", .target = "/api/client-prefixes", .status = 200 },
@@ -3759,6 +3788,7 @@ const contract_sample_walk = [_]ContractSample{
// conflict sample below can collide with it.
.{ .name = "list_upstreams", .ts_type = "{ upstreams: Upstream[] }", .method = "GET", .target = "/api/upstreams", .status = 200 },
.{ .name = "create_upstream", .ts_type = "UpstreamEcho", .method = "POST", .target = "/api/upstreams", .body = "{\"url\":\"https://dns2.example/dns-query\"}", .status = 201 },
.{ .name = "get_upstream", .ts_type = "Upstream", .method = "GET", .target = "/api/upstreams/1", .status = 200 },
.{ .name = "update_upstream", .ts_type = "UpstreamEcho", .method = "PUT", .target = "/api/upstreams/1", .body = "{\"url\":\"https://dns.example/dns-query\",\"priority\":5}", .status = 200 },
// Query log and stats. `limit=5` reaches seeded row 21, the blocked one, so