Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ if build_client
libsoup_dep,
sodium_dep,
]
if get_option('enable_fact_store').allowed()
test_daemon_http_decide_c_args += '-DWYL_HAS_FACT_STORE'
test_daemon_http_decide_deps += duckdb_dep
endif
if get_option('enable_audit').allowed()
test_daemon_http_decide_c_args += '-DWYL_HAS_AUDIT'
test_daemon_http_decide_deps += duckdb_dep
Expand All @@ -131,6 +135,7 @@ if build_client
'test-daemon-http-decide.c',
'../wyrelog/daemon/http.c',
'../wyrelog/daemon/delta.c',
'../wyrelog/daemon/fact-status.c',
c_args : test_daemon_http_decide_c_args,
include_directories : [wyrelog_inc, include_directories('../wyrelog')],
dependencies : test_daemon_http_decide_deps,
Expand All @@ -143,6 +148,7 @@ if build_client
'test-daemon-http-decide.c',
'../wyrelog/daemon/http.c',
'../wyrelog/daemon/delta.c',
'../wyrelog/daemon/fact-status.c',
c_args : test_daemon_http_decide_c_args + '-DWYL_TEST_VARIANT_AUDIT',
include_directories : [wyrelog_inc, include_directories('../wyrelog')],
dependencies : test_daemon_http_decide_deps,
Expand Down Expand Up @@ -183,22 +189,29 @@ if build_client and host_machine.system() != 'windows'
# so the policy mutation subcommands exercise the full client -> HTTP
# -> handler path, including bearer-only credentials and the privileged
# operator the daemon CLI cannot seed on its own.
test_wyctl_policy_daemon_c_args = [
'-DWYL_HAS_DAEMON_HTTP',
'-DWYL_TEST_TEMPLATE_DIR="' + meson.project_source_root() / 'templates' / 'access' + '"',
'-DWYL_TEST_WYCTL_PATH="' + wyctl_exe.full_path() + '"',
]
test_wyctl_policy_daemon_deps = [
wyrelog_dep,
wyrelog_client_dep,
libsoup_dep,
sodium_dep,
]
if get_option('enable_fact_store').allowed()
test_wyctl_policy_daemon_c_args += '-DWYL_HAS_FACT_STORE'
test_wyctl_policy_daemon_deps += duckdb_dep
endif
test_wyctl_policy_daemon = executable('test-wyctl-policy-daemon',
'test-wyctl-policy-daemon.c',
'../wyrelog/daemon/http.c',
'../wyrelog/daemon/delta.c',
c_args : [
'-DWYL_HAS_DAEMON_HTTP',
'-DWYL_TEST_TEMPLATE_DIR="' + meson.project_source_root() / 'templates' / 'access' + '"',
'-DWYL_TEST_WYCTL_PATH="' + wyctl_exe.full_path() + '"',
],
'../wyrelog/daemon/fact-status.c',
c_args : test_wyctl_policy_daemon_c_args,
include_directories : [wyrelog_inc, include_directories('../wyrelog')],
dependencies : [
wyrelog_dep,
wyrelog_client_dep,
libsoup_dep,
sodium_dep,
],
dependencies : test_wyctl_policy_daemon_deps,
)

test('wyctl-policy-daemon', test_wyctl_policy_daemon,
Expand Down Expand Up @@ -298,6 +311,7 @@ if get_option('enable_fact_store').allowed()

test_fact_replay = executable('test-fact-replay',
'test-fact-replay.c',
'../wyrelog/daemon/fact-status.c',
c_args : ['-DWYL_HAS_FACT_STORE'],
include_directories : include_directories('../wyrelog'),
dependencies : [wyrelog_dep, duckdb_dep, sqlite_dep],
Expand Down
12 changes: 12 additions & 0 deletions tests/test-daemon-http-decide.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,18 @@ check_readyz_runtime_liveness_contract (const gchar *base_url,
return 1923;
if (status != 200 || strstr (body, "\"status\":\"ready\"") == NULL)
return 1924;
if (strstr (body, "\"subsystems\"") == NULL
|| strstr (body, "\"facts\"") == NULL
|| strstr (body, "\"graphs_total\"") == NULL)
return 1929;

g_clear_pointer (&body, g_free);
if (send_raw_path (session, "GET", base_url, "/facts/status", &status,
&body) != 0)
return 1930;
if (status != 200 || strstr (body, "\"graphs_total\"") == NULL
|| strstr (body, "\"graphs\"") == NULL)
return 1931;

g_atomic_int_set (&runtime->delta_session_live, FALSE);
g_clear_pointer (&body, g_free);
Expand Down
59 changes: 59 additions & 0 deletions tests/test-fact-replay.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <glib.h>
#include <glib/gstdio.h>

#include "wyrelog/daemon/fact-status.h"
#include "wyrelog/fact/compound-private.h"
#include "wyrelog/fact/replay-private.h"
#include "wyrelog/fact/store-private.h"
Expand Down Expand Up @@ -381,6 +382,62 @@ assert_replayed_order_b_only (WylEngine *engine)
g_assert_true (probe.saw_order_b);
}

typedef struct
{
guint total;
guint ready;
guint unavailable;
gboolean saw_tenant_a_ready;
gboolean saw_tenant_b_unavailable;
} FactStatusProbe;

static wyrelog_error_t
fact_status_cb (const wyl_fact_graph_status_t *status, gpointer user_data)
{
FactStatusProbe *probe = user_data;
probe->total++;
if (status->state == WYL_FACT_GRAPH_STATE_READY)
probe->ready++;
if (status->state == WYL_FACT_GRAPH_STATE_STORE_UNAVAILABLE)
probe->unavailable++;
if (g_strcmp0 (status->tenant_id, "tenant-a") == 0
&& g_strcmp0 (status->graph_id, "orders") == 0
&& status->state == WYL_FACT_GRAPH_STATE_READY
&& status->queryable && status->last_error_class == NULL)
probe->saw_tenant_a_ready = TRUE;
if (g_strcmp0 (status->tenant_id, "tenant-b") == 0
&& g_strcmp0 (status->graph_id, "orders") == 0
&& status->state == WYL_FACT_GRAPH_STATE_STORE_UNAVAILABLE
&& !status->queryable
&& g_strcmp0 (status->last_error_class, "store_unavailable") == 0)
probe->saw_tenant_b_unavailable = TRUE;
return WYRELOG_E_OK;
}

static void
assert_handle_fact_status (WylHandle *handle)
{
FactStatusProbe probe = { 0 };
g_assert_cmpint (wyl_handle_foreach_fact_graph_status (handle,
fact_status_cb, &probe), ==, WYRELOG_E_OK);
g_assert_cmpuint (probe.total, ==, 2);
g_assert_cmpuint (probe.ready, ==, 1);
g_assert_cmpuint (probe.unavailable, ==, 1);
g_assert_true (probe.saw_tenant_a_ready);
g_assert_true (probe.saw_tenant_b_unavailable);

g_autofree gchar *json = wyl_daemon_fact_status_json (handle, TRUE);
g_assert_nonnull (json);
g_assert_nonnull (strstr (json, "\"status\":\"degraded\""));
g_assert_nonnull (strstr (json, "\"tenant_id\":\"tenant-a\""));
g_assert_nonnull (strstr (json, "\"tenant_id\":\"tenant-b\""));
g_assert_nonnull (strstr (json, "\"graph_id\":\"orders\""));
g_assert_nonnull (strstr (json,
"\"last_error_class\":\"store_unavailable\""));
g_assert_null (strstr (json, "facts.duckdb"));
g_assert_null (strstr (json, "storage_path"));
}

typedef struct
{
const gchar *relation;
Expand Down Expand Up @@ -588,6 +645,7 @@ test_handle_replay_is_idempotent_and_graph_local (void)
g_assert_nonnull (tenant_a);
g_assert_null (tenant_b);
assert_replayed_order_b_only (tenant_a);
assert_handle_fact_status (handle);

wyl_fact_replay_summary_t summary = { 0 };
g_assert_cmpint (wyl_handle_replay_fact_graphs (handle, &summary), ==,
Expand All @@ -598,6 +656,7 @@ test_handle_replay_is_idempotent_and_graph_local (void)
tenant_a = wyl_handle_get_fact_graph_engine (handle, "tenant-a", "orders");
g_assert_nonnull (tenant_a);
assert_replayed_order_b_only (tenant_a);
assert_handle_fact_status (handle);
remove_tree (root);
#else
g_test_skip ("fact graph path isolation is Unix-only in this build");
Expand Down
116 changes: 116 additions & 0 deletions wyrelog/daemon/fact-status.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
#include "daemon/fact-status.h"

#include "wyrelog/wyl-handle-private.h"

typedef struct
{
GString *graphs;
guint total;
guint ready;
guint degraded;
} FactStatusJsonCtx;

static void
append_json_string (GString *json, const gchar *value)
{
g_string_append_c (json, '"');
for (const guchar * p = (const guchar *)value; p != NULL && *p != '\0'; p++) {
switch (*p) {
case '"':
g_string_append (json, "\\\"");
break;
case '\\':
g_string_append (json, "\\\\");
break;
case '\b':
g_string_append (json, "\\b");
break;
case '\f':
g_string_append (json, "\\f");
break;
case '\n':
g_string_append (json, "\\n");
break;
case '\r':
g_string_append (json, "\\r");
break;
case '\t':
g_string_append (json, "\\t");
break;
default:
if (*p < 0x20)
g_string_append_printf (json, "\\u%04x", *p);
else
g_string_append_c (json, (gchar) * p);
break;
}
}
g_string_append_c (json, '"');
}

#ifdef WYL_HAS_FACT_STORE
static wyrelog_error_t
append_graph_status_json (const wyl_fact_graph_status_t *status,
gpointer user_data)
{
FactStatusJsonCtx *ctx = user_data;
ctx->total++;
if (status->state == WYL_FACT_GRAPH_STATE_READY)
ctx->ready++;
else
ctx->degraded++;

if (ctx->graphs != NULL) {
if (ctx->graphs->len > 0)
g_string_append_c (ctx->graphs, ',');
g_string_append (ctx->graphs, "{\"tenant_id\":");
append_json_string (ctx->graphs, status->tenant_id);
g_string_append (ctx->graphs, ",\"graph_id\":");
append_json_string (ctx->graphs, status->graph_id);
g_string_append (ctx->graphs, ",\"state\":");
append_json_string (ctx->graphs, wyl_fact_graph_state_name (status->state));
g_string_append_printf (ctx->graphs, ",\"queryable\":%s",
status->queryable ? "true" : "false");
g_string_append (ctx->graphs, ",\"last_error_class\":");
if (status->last_error_class == NULL)
g_string_append (ctx->graphs, "null");
else
append_json_string (ctx->graphs, status->last_error_class);
g_string_append_c (ctx->graphs, '}');
}
return WYRELOG_E_OK;
}
#endif

gchar *
wyl_daemon_fact_status_json (WylHandle *handle, gboolean include_graphs)
{
FactStatusJsonCtx ctx = { 0 };
g_autoptr (GString) graphs = include_graphs ? g_string_new (NULL) : NULL;
ctx.graphs = graphs;

#ifdef WYL_HAS_FACT_STORE
if (handle != NULL)
(void) wyl_handle_foreach_fact_graph_status (handle,
append_graph_status_json, &ctx);
const gchar *status = ctx.degraded > 0 ? "degraded" : "ready";
#else
(void) handle;
const gchar *status = "disabled";
#endif

g_autoptr (GString) body = g_string_new ("{\"status\":");
append_json_string (body, status);
g_string_append_printf (body,
",\"graphs_total\":%u,\"graphs_ready\":%u,\"graphs_degraded\":%u",
ctx.total, ctx.ready, ctx.degraded);
if (include_graphs) {
g_string_append (body, ",\"graphs\":[");
if (graphs != NULL)
g_string_append (body, graphs->str);
g_string_append_c (body, ']');
}
g_string_append_c (body, '}');
return g_string_free (g_steal_pointer (&body), FALSE);
}
13 changes: 13 additions & 0 deletions wyrelog/daemon/fact-status.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
#pragma once

#include <glib.h>

#include "wyrelog/wyrelog.h"

G_BEGIN_DECLS;

gchar *wyl_daemon_fact_status_json (WylHandle * handle,
gboolean include_graphs);

G_END_DECLS;
Loading
Loading