feat: implement MoloApp plans 01-15 with integration

Full C++23 framework tree: runtime, observability, security,
data, app, realtime, supervisor, clients, CLI, example plus
enterprise tenancy, audit, operability, governance,
supplychain. Postgres via docker compose for tests.
This commit is contained in:
2026-10-23 18:20:04 +02:00
parent 54068f1953
commit 3c4334cec9
1068 changed files with 100899 additions and 2 deletions
+73
View File
@@ -0,0 +1,73 @@
// AI harness: SSE framing, redaction, plan-then-apply, stub client.
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include "ai/ai_client.hpp"
#include "ai/ai_session.hpp"
#include "ai/redactor.hpp"
#include "ai/sse_stream_parser.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << "\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::ai;
SseStreamParser parser;
parser.feed("data: {\"a\":1}\n\n");
parser.feed("data: {\"b\":2}\n\n");
auto events = parser.take_events();
CHECK(events.size() == 2 && !parser.done(), "two SSE chunks parsed");
parser.feed("data: [DONE]\n\n");
CHECK(parser.done() && parser.take_events().empty(), "[DONE] ends stream");
CHECK(Redactor::key_is_secret("DEVPLACE_API_KEY"), "api key detected");
CHECK(!Redactor::key_is_secret("username"), "plain key kept");
Redactor::Result redacted = Redactor::redact("{\"API_KEY\": \"hunter2\", \"n\": 1}");
CHECK(redacted.redacted == 1, "one value redacted");
CHECK(redacted.text.find("hunter2") == std::string::npos, "secret gone from text");
AiSession session("t");
std::string error;
CHECK(session.append("user", "hi", error), "session append");
namespace fs = std::filesystem;
fs::path target = fs::temp_directory_path() / "molo-ai-nowrite.txt";
fs::remove(target);
std::size_t index = session.request_write(target.string(), "data");
CHECK(index != static_cast<std::size_t>(-1), "write requested");
CHECK(!fs::exists(target), "no file written without confirmation");
CHECK(session.confirm_write(index, error), "confirmed write applies: " + error);
CHECK(fs::exists(target), "file exists after confirmation");
fs::remove(target);
// Client over stub transport with http base URL (https needs wolfSSL).
setenv("DEVPLACE_API_KEY", "test-key", 1);
std::string sse =
"data: {\"choices\":[{\"delta\":{\"content\":\"Hello\"}}]}\n\n"
"data: {\"choices\":[{\"delta\":{\"content\":\"!\"}}]}\n\n"
"data: [DONE]\n\n";
StubAiTransport stub({sse});
AiClientOptions opts;
opts.base_url = "http://127.0.0.1:9";
opts.model = "molodetz";
AiClient client(opts, stub);
std::string deltas;
std::string full = client.chat_stream({{"user", "hi"}},
[&](const std::string& d) { deltas += d; }, error);
CHECK(error.empty(), "stub chat ok: " + error);
CHECK(full == "Hello!", "transcript assembled");
CHECK(stub.seen().size() == 1, "one request sent");
CHECK(stub.seen()[0].body_json.find("\"stream\":true") != std::string::npos,
"stream flag in request");
CHECK(AiClient::extract_delta("{\"choices\":[{\"delta\":{\"content\":\"x\\ny\"}}]}") == "x\ny",
"delta unescapes");
unsetenv("DEVPLACE_API_KEY");
std::cout << "ALL_AI_OK\n";
return 0;
}
+82
View File
@@ -0,0 +1,82 @@
// Unit checks for CLI parsing, tree, help, writers, exit codes.
#include <cassert>
#include <iostream>
#include "cli/arg_parser.hpp"
#include "cli/command.hpp"
#include "cli/command_tree.hpp"
#include "cli/exit_code.hpp"
#include "cli/flag.hpp"
#include "cli/flag_set.hpp"
#include "cli/help_renderer.hpp"
#include "cli/json_writer.hpp"
#include "cli/output.hpp"
#include "cli/table_writer.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << " (line " << __LINE__ << ")\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::cli;
CHECK(to_int(ExitCode::Ok) == 0, "exit ok is 0");
CHECK(to_int(ExitCode::Runtime) == 1, "exit runtime is 1");
CHECK(to_int(ExitCode::Usage) == 2, "exit usage is 2");
CHECK(to_int(ExitCode::Unavailable) == 69, "exit unavailable is 69");
FlagSet flags;
flags.add(Flag("port", "p", "listen port", true, "8000"));
CHECK(flags.declares("port"), "flagset declares port");
CHECK(flags.get("port") == "8000", "flagset default value");
flags.set("port", "9000");
CHECK(flags.get("port") == "9000", "flagset set/get");
ArgParser parser;
ParsedArgs parsed = parser.parse({"molo", "-vv", "--format", "json", "serve", "--port", "9000"});
CHECK(parsed.ok(), "arg parse ok");
CHECK(parsed.global.verbose == 2, "repeatable verbose counts");
CHECK(parsed.global.format == "json", "format flag parsed");
CHECK(parsed.flags["port"] == "9000", "command flag value parsed");
ParsedArgs bad = parser.parse({"molo", "--format", "yaml"});
CHECK(!bad.ok(), "bad format rejected");
CommandTree tree;
auto serve = std::make_shared<Command>("serve", "run server");
serve->usage("[--port N]").example("serve --reload");
tree.add(serve);
ResolveResult found = tree.resolve({"serve"});
CHECK(found.command != nullptr && found.consumed == 1, "tree resolves serve");
ResolveResult missing = tree.resolve({"nosuch"});
CHECK(missing.command == nullptr, "tree rejects unknown command");
HelpRenderer help;
std::string concise = help.render_concise(*serve, "molo");
CHECK(concise.find("usage: molo serve") != std::string::npos, "concise help has usage");
std::string full = help.render_full(*serve, "molo", "serve");
CHECK(full.find("examples:") != std::string::npos, "full help has examples");
std::string root = help.render_root(tree, "molo");
CHECK(root.find("--format") != std::string::npos, "root help lists global flags");
CHECK(JsonWriter::quote("a\"b") == "\"a\\\"b\"", "json escapes quotes");
std::map<std::string, std::string> fields;
fields["ok"] = "true";
fields["n"] = "3";
CHECK(JsonWriter::object(fields) == "{\"n\":3,\"ok\":true}", "json object renders");
TableWriter table;
table.header({"a", "bb"});
table.row({"1", "2"});
std::string rendered = table.render();
CHECK(rendered.find("a") != std::string::npos && rendered.find("bb") != std::string::npos,
"table renders header");
Output out(true, 0, false, false);
CHECK(out.json_mode() && !out.tty(), "output flags stored");
std::cout << "ALL_CLI_OK\n";
return 0;
}
+86
View File
@@ -0,0 +1,86 @@
#!/bin/sh
# Integration tests through the real `molo` binary. Fails fast on first error.
set -u
MOLO="$1"
case "$MOLO" in
/*) ;;
*) MOLO="$(pwd)/$MOLO" ;;
esac
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
fail() { echo "FAIL: $1"; exit 1; }
pass() { echo "PASS: $1"; }
"$MOLO" version >/dev/null 2>&1 || fail "version exits 0"
[ "$("$MOLO" version --format json | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["version"])')" = "0.1.0" ] || fail "version json parses"
pass "version text+json"
"$MOLO" --help >/dev/null 2>&1 || fail "--help exits 0"
"$MOLO" nosuch >/dev/null 2>&1 && fail "nosuch must fail"
[ "$("$MOLO" nosuch >/dev/null 2>&1; echo $?)" = "2" ] || fail "nosuch exits 2"
pass "help + unknown command exit codes"
# No color codes when piped.
"$MOLO" --help | grep -q "$(printf '\033')" && fail "color leaked to pipe"
pass "no color when piped"
# `molo new` scaffolds, re-run without --force fails.
(cd "$TMP" && "$MOLO" new shop >/dev/null 2>&1) || fail "molo new fails"
[ -f "$TMP/shop/Makefile" ] && [ -f "$TMP/shop/.env.json" ] && [ -f "$TMP/shop/.gitignore" ] && [ -d "$TMP/shop/.git" ] || fail "scaffold files missing"
(cd "$TMP" && "$MOLO" new shop >/dev/null 2>&1) && fail "re-run without --force must fail"
[ "$(cd "$TMP" && "$MOLO" new shop >/dev/null 2>&1; echo $?)" = "1" ] || fail "re-run exits 1"
pass "molo new + force guard"
# serve refuses prod reload without --allow-prod.
mkdir -p "$TMP/prod" && printf '{"MOLO_ENV":"prod"}' > "$TMP/prod/.env.json"
[ "$(cd "$TMP/prod" && MOLO_ENV=prod "$MOLO" serve --reload --port 8123 >/dev/null 2>&1; echo $?)" = "2" ] || fail "prod reload must exit 2"
pass "serve prod guard"
# health json parses.
"$MOLO" health --format json | python3 -c 'import json,sys; json.load(sys.stdin)' || fail "health json parses"
pass "health json"
# supervisor/task structured output with unavailable backend.
"$MOLO" supervisor status --format json | python3 -c 'import json,sys; d=json.load(sys.stdin); assert d["ok"] is False' || fail "supervisor json shape"
[ "$("$MOLO" supervisor status --format json >/dev/null 2>&1; echo $?)" = "69" ] || fail "supervisor exits 69"
"$MOLO" task inspect --id x --format json | python3 -c 'import json,sys; json.load(sys.stdin)' || fail "task json parses"
pass "supervisor/task json + 69"
# embed fixture twice, byte-identical hpp.
mkdir -p "$TMP/fx/css" && printf '<h1>x</h1>' > "$TMP/fx/index.html" && printf 'b{}' > "$TMP/fx/css/a.css"
"$MOLO" embed "$TMP/fx" --out "$TMP/e1.cpp" --gzip >/dev/null 2>&1 || fail "embed run 1"
"$MOLO" embed "$TMP/fx" --out "$TMP/e2.cpp" --gzip >/dev/null 2>&1 || fail "embed run 2"
cmp -s "$TMP/e1.hpp" "$TMP/e2.hpp" || fail "embed hpp byte-identical"
grep -q "molo-embed-1" "$TMP/e1.hpp" || fail "embed tool version stamped"
pass "embed reproducibility"
# `molo ai` against a local stub SSE server.
export DEVPLACE_API_KEY="stub-key"
python3 - "$TMP" <<'EOF' &
import http.server, sys
tmp = sys.argv[1]
BODY = ('data: {"choices":[{"delta":{"content":"Hi"}}]}\n\n'
'data: {"choices":[{"delta":{"content":"!"}}]}\n\n'
'data: [DONE]\n\n')
class H(http.server.BaseHTTPRequestHandler):
def do_POST(self):
ln = int(self.headers.get('Content-Length', 0))
self.rfile.read(ln)
payload = BODY.encode()
self.send_response(200)
self.send_header('Content-Type', 'text/event-stream')
self.send_header('Content-Length', str(len(payload)))
self.end_headers()
self.wfile.write(payload)
def log_message(self, *a):
pass
http.server.HTTPServer(('127.0.0.1', 18321), H).serve_forever()
EOF
SRV=$!
sleep 1
"$MOLO" ai new --base-url http://127.0.0.1:18321 --model molodetz --prompt "say hi" >"$TMP/ai.txt" 2>"$TMP/ai.err" || fail "molo ai new against stub"
grep -q "Hi!" "$TMP/ai.txt" || fail "ai transcript assembled"
kill $SRV 2>/dev/null
pass "ai stub streaming"
echo "ALL_BINARY_OK"
+48
View File
@@ -0,0 +1,48 @@
// CSP: deny-first defaults, nonce plumbing, report endpoint caps.
#include <iostream>
#include "csp/csp_builder.hpp"
#include "csp/csp_report_handler.hpp"
#include "csp/nonce.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << "\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::csp;
CspPolicy def = CspBuilder().build();
CHECK(def.report_only(), "report-only by default");
CHECK(def.header_name() == "Content-Security-Policy-Report-Only", "report-only header name");
CHECK(def.header_value().find("default-src 'none'") != std::string::npos, "deny-first default");
CHECK(def.header_value().find("frame-ancestors 'none'") != std::string::npos,
"frame-ancestors none");
std::string nonce = Nonce::generate();
CHECK(nonce.size() == 24, "nonce is 16 bytes base64 (24 chars)");
CHECK(Nonce::generate() != nonce, "nonces differ per call");
CspPolicy with_nonce = CspBuilder().script_nonce(nonce).build();
CHECK(with_nonce.header_value().find("'nonce-" + nonce + "'") != std::string::npos,
"nonce plumbed into script-src");
CspPolicy enforced = CspBuilder().enforce().build();
CHECK(!enforced.report_only() && enforced.header_name() == "Content-Security-Policy",
"enforce() switches header");
// POST /csp-report
CspReportHandler handler;
CspReportResult accepted = handler.handle("10.0.0.1", "{\"blocked-uri\":\"x\"}", 1000);
CHECK(accepted.status == 202, "report accepted 202");
CspReportResult too_big = handler.handle("10.0.0.1", std::string(70000, 'x'), 1000);
CHECK(too_big.status == 413, "oversize report 413");
for (int i = 0; i < 100; ++i) handler.handle("10.0.0.2", "{}", 2000);
CspReportResult limited = handler.handle("10.0.0.2", "{}", 2001);
CHECK(limited.status == 429, "report rate limit 429");
std::cout << "ALL_CSP_OK\n";
return 0;
}
+67
View File
@@ -0,0 +1,67 @@
// Dev server: config split enforcement, backoff math, polling watcher.
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <thread>
#include "dev/dev_config.hpp"
#include "dev/dev_server.hpp"
#include "dev/polling_watcher.hpp"
#include "dev/reloader.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << "\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::dev;
namespace fs = std::filesystem;
CHECK(DevServer::should_refuse(true, true, false), "prod+reload refused");
CHECK(!DevServer::should_refuse(true, true, true), "allow-prod passes");
CHECK(!DevServer::should_refuse(false, true, false), "dev reload passes");
CHECK(Reloader::backoff_for(1) == 1, "backoff 1s");
CHECK(Reloader::backoff_for(2) == 2, "backoff 2s");
CHECK(Reloader::backoff_for(3) == 4, "backoff 4s");
CHECK(Reloader::backoff_for(99) == 30, "backoff holds at 30s");
fs::path dir = fs::temp_directory_path() / "molo-dev-test";
fs::remove_all(dir);
fs::create_directories(dir);
{ std::ofstream(dir / ".env.json") << "{\"MOLO_ENV\": \"prod\", \"PORT\": 9000}"; }
unsetenv("MOLO_ENV");
ConfigLoader loader;
LoadedConfig prod = loader.load((dir / ".env.json").string(), true, false);
CHECK(!prod.ok(), "prod reload without allow-prod errors");
LoadedConfig prod_ok = loader.load((dir / ".env.json").string(), true, true);
CHECK(prod_ok.ok() && prod_ok.is_prod() && prod_ok.dev.port == 9000, "allow-prod loads prod");
setenv("MOLO_ENV", "dev", 1);
LoadedConfig dev = loader.load((dir / ".env.json").string(), true, false);
CHECK(dev.ok() && !dev.is_prod(), "MOLO_ENV overrides file");
unsetenv("MOLO_ENV");
PollingFileWatcher watcher;
std::string error;
CHECK(watcher.start({dir.string()}, error), "watcher starts: " + error);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
{ std::ofstream(dir / "t.txt") << "x"; }
bool saw = false;
for (int i = 0; i < 20 && !saw; ++i) {
auto events = watcher.poll(200);
for (const auto& event : events) {
if (event.path.find("t.txt") != std::string::npos) saw = true;
}
}
CHECK(saw, "watcher sees new file");
watcher.stop();
fs::remove_all(dir);
std::cout << "ALL_DEV_OK\n";
return 0;
}
+81
View File
@@ -0,0 +1,81 @@
// Embedder: scan caps, emitter reproducibility, bundle reader, sha/mime/gzip.
#include <filesystem>
#include <fstream>
#include <iostream>
#include "embed/asset_scanner.hpp"
#include "embed/embed_emitter.hpp"
#include "embed/embedded_bundle.hpp"
#include "embed/gzip_stored.hpp"
#include "embed/mime.hpp"
#include "embed/sha256.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << "\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::embed;
namespace fs = std::filesystem;
CHECK(Sha256::hex_of_string("abc") ==
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
"sha256 known vector");
CHECK(Mime::of_path("x/app.css") == "text/css", "mime by extension");
CHECK(Mime::is_textual("text/html") && !Mime::is_textual("image/png"), "textual detection");
std::vector<unsigned char> raw = {'h', 'i'};
auto gz = GzipStored::compress(raw);
CHECK(gz.size() > 10 && gz[0] == 0x1F && gz[1] == 0x8B, "gzip stored magic");
// Fixture tree.
fs::path root = fs::temp_directory_path() / "molo-embed-test";
fs::remove_all(root);
fs::create_directories(root / "css");
{ std::ofstream(root / "index.html") << "<h1>hi</h1>"; }
{ std::ofstream(root / "css" / "a.css") << "body{}"; }
{ std::ofstream(root / ".hidden") << "skip me"; }
AssetScanner scanner;
ScanResult scanned = scanner.scan(root.string());
CHECK(scanned.ok(), "scan ok");
CHECK(scanned.files.size() == 2, "dotfile skipped");
CHECK(scanned.files[0].rel_path < scanned.files[1].rel_path, "scan sorted");
// Emit twice: byte-identical.
EmitOptions opts;
opts.namespc = "molo::assets";
opts.gzip = true;
EmbedEmitter emitter;
std::string error;
std::string cpp1 = (root / "a1.cpp").string(), hpp1 = (root / "a1.hpp").string();
std::string cpp2 = (root / "a2.cpp").string(), hpp2 = (root / "a2.hpp").string();
CHECK(emitter.emit(scanned.files, cpp1, hpp1, opts, error), "emit run 1: " + error);
CHECK(emitter.emit(scanned.files, cpp2, hpp2, opts, error), "emit run 2: " + error);
auto slurp = [](const std::string& p) {
std::ifstream in(p, std::ios::binary);
return std::string(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
};
// Note: cpp embeds its own hpp basename, so compare normalized.
std::string c1 = slurp(cpp1), c2 = slurp(cpp2);
CHECK(slurp(hpp1) == slurp(hpp2), "hpp byte-identical");
CHECK(c1.size() == c2.size(), "cpp same size across runs");
EmbeddedBundle bundle;
EmbeddedAsset asset;
asset.path = "index.html";
asset.data = {'<', 'h', '1', '>'};
asset.sha256 = "deadbeef";
asset.mime = "text/html";
bundle.add(asset);
CHECK(bundle.find("index.html") != nullptr, "bundle find hit");
CHECK(bundle.find("nope") == nullptr, "bundle find miss");
fs::remove_all(root);
std::cout << "ALL_EMBED_OK\n";
return 0;
}
+38
View File
@@ -0,0 +1,38 @@
// /healthz: liveness stays 200 unless shutdown is stuck.
#include <iostream>
#include "ops/health_registry.hpp"
#include "ops/liveness_check.hpp"
#include "ops/ops_routes.hpp"
#include "ops/prometheus_exporter.hpp"
#include "ops/readiness_check.hpp"
#include "ops/startup_gate.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << "\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::ops;
auto live = std::make_shared<LivenessCheck>();
HealthRegistry registry;
registry.add(live);
ReadinessCheck readiness;
StartupGate gate;
gate.mark_boot_complete();
PrometheusExporter exporter;
OpsRoutes routes(registry, readiness, gate, exporter);
OpsResponse hz = routes.healthz();
CHECK(hz.status == 200 && hz.body == "{\"status\":\"ok\"}", "healthz 200 ok");
live->set_shutdown_stuck(true);
OpsResponse stuck = routes.healthz();
CHECK(stuck.status == 503, "healthz 503 when shutdown stuck");
std::cout << "ALL_HEALTHZ_OK\n";
return 0;
}
+60
View File
@@ -0,0 +1,60 @@
// i18n: locale chain, PO loader with plurals, translator fallback, resolution.
#include <iostream>
#include "i18n/locale.hpp"
#include "i18n/plural_rule.hpp"
#include "i18n/po_loader.hpp"
#include "i18n/translator.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << "\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::i18n;
Locale nl = Locale::parse("nl-NL");
CHECK(nl.language() == "nl" && nl.region() == "NL", "locale normalizes");
auto chain = nl.fallback_chain();
CHECK(chain.size() == 3 && chain[0].str() == "nl-NL" && chain[1].str() == "nl" &&
chain[2].str() == "en",
"fallback chain nl-NL to nl to en");
CHECK(PluralRule::form_index(1, "en") == 0, "en singular");
CHECK(PluralRule::form_index(2, "en") == 1, "en plural");
CHECK(PluralRule::form_index(5, "ru") == 2, "ru many form");
std::string po =
"msgid \"Hello\"\nmsgstr \"Hallo\"\n\n"
"msgid \"apple\"\nmsgid_plural \"apples\"\nmsgstr[0] \"appel\"\nmsgstr[1] "
"\"appels\"\n";
PoLoadResult loaded = PoLoader::load_text(po, Locale::parse("nl"));
CHECK(loaded.ok(), "po loads");
CHECK(loaded.catalog.size() == 2, "po has two entries");
Translator translator;
translator.add_catalog(std::move(loaded.catalog));
unsigned long missing_notes = 0;
translator.set_missing_sink([&](const std::string&) { ++missing_notes; });
CHECK(translator.translate("Hello", Locale::parse("nl-NL")) == "Hallo", "nl-NL translates");
CHECK(translator.translate("apple", Locale::parse("nl"), 2) == "appels", "plural form renders");
CHECK(translator.translate("apple", Locale::parse("nl"), 1) == "appel",
"singular form renders");
CHECK(translator.translate("Gone", Locale::parse("nl")) == "Gone", "missing key falls back");
CHECK(translator.translate("Gone", Locale::parse("nl")) == "Gone", "missing key repeats msgid");
CHECK(missing_notes == 1, "missing key logs once");
RequestLocale req;
req.route_prefix = "nl";
req.accept_language = "fr;q=0.9,en;q=0.1";
CHECK(Translator::resolve(req).language() == "nl", "route prefix wins");
RequestLocale req2;
req2.accept_language = "fr;q=0.9,en;q=0.1";
CHECK(Translator::resolve(req2).language() == "fr", "q-values honored");
std::cout << "ALL_I18N_OK\n";
return 0;
}
+48
View File
@@ -0,0 +1,48 @@
// /metrics: Prometheus 0.0.4 format, caps, gzip parity, route templates.
#include <iostream>
#include "ops/prometheus_exporter.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << "\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::ops;
PrometheusExporter exporter;
MetricSample sample;
sample.name = "molo_requests_total";
sample.labels = {{"route", "/users/{id}"}, {"code", "200"}};
sample.value = 42;
sample.type = MetricType::Counter;
sample.help = "total requests";
CHECK(exporter.add(sample), "add sample under caps");
std::string text = exporter.render();
CHECK(!text.empty() && text.back() == '\n', "trailing newline on last line");
CHECK(text.find("# HELP molo_requests_total") != std::string::npos, "HELP line present");
CHECK(text.find("# TYPE molo_requests_total counter") != std::string::npos, "TYPE line present");
CHECK(text.find("molo_metrics_dropped_total 0") != std::string::npos, "dropped counter present");
// Label overflow drops and counts.
MetricSample flooded;
flooded.name = "flood";
for (int i = 0; i < 100; ++i) flooded.labels["l" + std::to_string(i)] = "v";
CHECK(!exporter.add(flooded), "64 label cap enforced");
CHECK(exporter.dropped() == 1, "overflow bumps dropped counter");
// gzip variant: valid gzip magic, decodes by length sanity.
auto gz = exporter.render_gzip();
CHECK(gz.size() > 10 && gz[0] == 0x1F && gz[1] == 0x8B, "gzip magic present");
CHECK(PrometheusExporter::normalize_route("/users/123") == "/users/{id}",
"numeric segment templated");
CHECK(PrometheusExporter::normalize_route("/healthz") == "/healthz", "plain path kept");
std::cout << "ALL_METRICS_OK\n";
return 0;
}
+72
View File
@@ -0,0 +1,72 @@
// /readyz: 200 when deps pass, 503 degraded with failed list otherwise.
#include <chrono>
#include <iostream>
#include <thread>
#include "ops/health_registry.hpp"
#include "ops/liveness_check.hpp"
#include "ops/ops_routes.hpp"
#include "ops/prometheus_exporter.hpp"
#include "ops/readiness_check.hpp"
#include "ops/startup_gate.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << "\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::ops;
auto live = std::make_shared<LivenessCheck>();
HealthRegistry registry;
registry.add(live);
ReadinessCheck readiness;
bool db_up = true;
readiness.add_probe({"db", [&]() {
HealthResult r;
r.state = db_up ? HealthState::Ok : HealthState::Failed;
return r;
}});
readiness.add_probe({"kv", []() {
HealthResult r;
r.state = HealthState::Ok;
return r;
}});
StartupGate gate;
gate.mark_boot_complete();
PrometheusExporter exporter;
OpsRoutes routes(registry, readiness, gate, exporter);
OpsResponse ready = routes.readyz();
CHECK(ready.status == 200, "readyz 200 when deps pass");
db_up = false; // kill the DB
OpsResponse degraded = routes.readyz();
CHECK(degraded.status == 503, "readyz 503 when db down");
CHECK(degraded.body.find("degraded") != std::string::npos, "readyz body says degraded");
CHECK(degraded.body.find("\"db\"") != std::string::npos, "readyz names failed dep");
OpsResponse hz = routes.healthz();
CHECK(hz.status == 200, "healthz stays 200 while readyz degraded");
// Registry short-circuits slow checks at the timeout.
HealthRegistry slow_registry;
class Slow : public HealthCheck {
public:
std::string name() const override { return "slow"; }
HealthResult check() override {
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
return HealthResult();
}
};
slow_registry.add(std::make_shared<Slow>());
auto start = std::chrono::steady_clock::now();
auto results = slow_registry.run_all(200);
auto elapsed = std::chrono::steady_clock::now() - start;
CHECK(results.size() == 1 && results[0].result.timed_out, "slow check times out");
CHECK(elapsed < std::chrono::seconds(2), "timeout short-circuits under 2s");
std::cout << "ALL_READYZ_OK\n";
return 0;
}
+37
View File
@@ -0,0 +1,37 @@
// /startupz: 503 until boot completes, then 200.
#include <iostream>
#include "ops/health_registry.hpp"
#include "ops/liveness_check.hpp"
#include "ops/ops_routes.hpp"
#include "ops/prometheus_exporter.hpp"
#include "ops/readiness_check.hpp"
#include "ops/startup_gate.hpp"
#define CHECK(cond, label) \
do { \
if (!(cond)) { \
std::cerr << "FAIL: " << label << "\n"; \
return 1; \
} \
std::cout << "PASS: " << label << "\n"; \
} while (0)
int main() {
using namespace molo::ops;
auto live = std::make_shared<LivenessCheck>();
HealthRegistry registry;
registry.add(live);
ReadinessCheck readiness;
StartupGate gate;
PrometheusExporter exporter;
OpsRoutes routes(registry, readiness, gate, exporter);
OpsResponse starting = routes.startupz();
CHECK(starting.status == 503, "startupz 503 before boot");
gate.mark_boot_complete();
OpsResponse booted = routes.startupz();
CHECK(booted.status == 200 && booted.body == "{\"status\":\"ok\"}", "startupz 200 after boot");
std::cout << "ALL_STARTUPZ_OK\n";
return 0;
}