feat(pipeline): the pure pipeline, checked against its goldens
The modules that turn a frozen range into passes and passes into a verdict: :Schema, :Tools (the DENY, git and base lists, a leaf, so the config and the pool both read them), :Plan, :Artifact, :Inventory, :Dispositions, :State, :Normalize, :Stream, :Prompt, :Config, and the URL half of :PgWire. Units, jobs, results, the config and the claude stream stay JSON from end to end, so every key keeps its place; :Text gives JSON values truthiness, get, ==, membership and Str(), and throws Dyn::Error under a name for each kind of unexpected data. Exactness where the bytes are seen: - the stream classifiers are hand-written scanners with case-insensitive folds (the Kelvin sign is a k), Unicode \b and \s, and `.` stopping at \n; the SQL call detection takes `<exe> sql` with any path before it; - config problems carry the JSON scanner's and the UTF-8 decoder's own texts, positions included; - a float total is Neumaier's compensated sum, and the build passes -ffp-contract=off: -march=native would fuse a*b+c; - `crafter-review inventory` and `config` print what the goldens record, byte for byte (the fixture and 14 configs, broken ones included); a message names this binary as the command to run. tests/Goldens: the recorded answers over the fixtures next to them (the commits fixture, a rich versioned series with CRLF, a folded subject, Markdown headings in and out of fences, the code-key rewrites); the suite compares 2304 results as DumpFile writes them, so a key out of order fails too. All agree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b02de3ceff
commit
741e3a656d
70 changed files with 30565 additions and 21 deletions
60
implementations/Crafter.Review-PgWire.cpp
Normal file
60
implementations/Crafter.Review-PgWire.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module Crafter.Review:PgWire_impl;
|
||||
import :PgWire;
|
||||
import :Json;
|
||||
import :Text;
|
||||
import std;
|
||||
|
||||
namespace Crafter::Review::PgWire {
|
||||
Url ParseUrl(std::string_view text) {
|
||||
const Dyn::SplitUrl split = Dyn::UrlSplit(text);
|
||||
Url url;
|
||||
url.scheme = split.scheme;
|
||||
url.host = Dyn::Lower(split.Hostname().value_or(""));
|
||||
const std::optional<std::int32_t> port = split.Port();
|
||||
url.port = port && *port != 0 ? *port : 5432;
|
||||
const std::optional<std::string> user = split.Username();
|
||||
url.user = Dyn::Unquote(user && !user->empty() ? *user : "postgres");
|
||||
url.password = Dyn::Unquote(split.Password().value_or(""));
|
||||
const std::string path = split.path.empty() ? "/" : split.path;
|
||||
const std::string database = Dyn::LStrip(path, "/");
|
||||
url.database = Dyn::Unquote(database.empty() ? "postgres" : database);
|
||||
return url;
|
||||
}
|
||||
|
||||
std::optional<std::string> CheckUrl(const Json& url) {
|
||||
if (!url.is_string() || url.get_ref<const std::string&>().empty()) return "no database URL";
|
||||
Url parsed;
|
||||
try {
|
||||
parsed = ParseUrl(url.get_ref<const std::string&>());
|
||||
} catch (const std::invalid_argument& error) {
|
||||
return std::format("not a URL ({})", error.what());
|
||||
}
|
||||
if (parsed.scheme != "postgres" && parsed.scheme != "postgresql") return "not a postgres:// or postgresql:// URL";
|
||||
if (!std::ranges::contains(Loopback, parsed.host)) {
|
||||
const std::vector<std::string> names(Loopback.begin(), Loopback.end());
|
||||
return std::format("host {} is not this machine ({}): the database under test is a local stack, never a hosted one",
|
||||
parsed.host.empty() ? "(none)" : parsed.host, Dyn::Join(names, ", "));
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string Redact(std::string_view url) {
|
||||
try {
|
||||
const Dyn::SplitUrl split = Dyn::UrlSplit(url);
|
||||
if (!split.Password()) return std::string(url);
|
||||
const std::string host = split.netloc.substr(split.netloc.rfind('@') + 1);
|
||||
const std::string netloc = std::format("{}:***@{}", split.Username().value_or(""), host);
|
||||
return Dyn::UrlUnsplit(split.scheme, netloc, split.path, split.query, split.fragment);
|
||||
} catch (const std::invalid_argument&) {
|
||||
return "(unparsable URL)";
|
||||
}
|
||||
}
|
||||
|
||||
std::string Where(std::string_view url) {
|
||||
const Url parsed = ParseUrl(url);
|
||||
return std::format("{} on {}:{} as {}", parsed.database, parsed.host, parsed.port, parsed.user);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue