module-eval: assert the severity table once, not once per tier

Both journald receivers import one PRIORITY mapping, and each tier's
suite was asserting the whole table against its own receiver. That checks
one file twice: invert the table and two cases fail saying the same
thing, which tells you nothing about which of the two possible defects
you have.

Split by subject instead. The table's contents — the inverted direction,
overwrite_text, parse_from/on_error — belong to the file that holds them,
so they get a suite of their own reading that file directly, with no
fixture at all. Each tier keeps a case, reduced to the question only it
can answer: does MY receiver carry the shared mapping.

Both tier cases stay. They cover different receivers over different
journals — the agent container's own and the swarm collector's host
journal — and one tier quietly losing its parser while the other keeps
one is exactly the half-fixed state worth catching.

Membership rather than equality of the whole operator list, so a tier
that later grows an unrelated operator of its own still passes.

Checked against seven defect scenarios: each fails exactly one case, and
names the right one.
This commit is contained in:
atlas 2026-09-20 13:32:00 +02:00 • committed by mara
commit 33a7ca6118
5 changed files with 174 additions and 88 deletions

View file

@ -18,8 +18,7 @@ let
;
})
agent
journaldSeverityOf
journaldSeverityOverwritesText
carriesJournaldSeverity
runGroup
;
@ -59,37 +58,28 @@ let
ok = ((agentSettings agentOtel).receivers.journald.directory or null) == "/var/log/journal";
}
{
# ⚠️ The direction, and the only part of this mapping a reviewer cannot
# check by looking: syslog's PRIORITY counts DOWN in urgency (0 = emerg,
# 7 = debug) where the OTEL severity counts UP, so a copy wired across
# renders debug as critical while every line still arrives and every
# field is populated. Asserted at BOTH ENDS of the table — an inverted
# mapping still maps every value to something, so "a severity parser is
# configured" passes on the exact defect. The swarm tier's receiver gets
# the same case over in ./swarm-otel-core.nix; the two import one file,
# and these are what say they still do.
name = "the agent forwarder maps PRIORITY to severity the right way up";
ok =
let
sev = journaldSeverityOf (agentSettings agentOtel).receivers.journald;
in
sev "0" == "fatal"
&& sev "3" == "error"
&& sev "4" == "warn"
&& sev "6" == "info"
&& sev "7" == "debug";
}
{
# The other half of the same operator, and the half that looks already
# handled. Without `overwrite_text` the parser sets the severity NUMBER
# from the mapping and leaves the severity TEXT as the raw value it
# matched — so `severity_text` reaches the log store as the literal "6".
# That is a populated field which passes any check asking merely whether
# severity is set, and which nothing renders as a level. VictoriaLogs has
# no ingest parameter naming a level field, so the text is the whole
# interface.
name = "the agent forwarder writes a level name, not the raw priority digit";
ok = journaldSeverityOverwritesText (agentSettings agentOtel).receivers.journald;
# Wiring, not contents. A journald record arrives with its level in
# `PRIORITY` and nothing downstream reads that field, so a receiver with
# no severity parser ships every line to the log store labelled
# `Unspecified` — healthy, complete, and unqueryable by level.
#
# What this asks is only whether THIS receiver carries the shared
# mapping. ⚠️ Whether that mapping is RIGHT — the inverted direction,
# which is the part of it a reviewer cannot check by looking — is
# asserted once, in ./journald-severity.nix, against the file both tiers
# import. Re-asserting the table here would test that file twice and
# report one defect as two failures.
#
# The swarm collector's own receiver gets the sibling of this case in
# ./swarm-otel-core.nix. Deliberately NOT folded together with it: they
# cover different journals — this container's own, and the host's — and
# one tier quietly losing its parser while the other keeps one is
# precisely the half-fixed state worth catching.
#
# Membership rather than equality of the whole operator list, so a tier
# that later grows an unrelated operator of its own still passes.
name = "the agent forwarder's journald receiver carries the shared PRIORITY mapping";
ok = carriesJournaldSeverity (agentSettings agentOtel).receivers.journald;
}
{
# The hop's two ends: what it reads, and where what it reads goes.