# SPDX-License-Identifier: GPL-3.0-or-later """Registry coverage tests. Asserts that every primitive listed in scratchpad.md's tables has exactly one entry in PRIMITIVE_REGISTRY. Drift-detector — failing this test means scratchpad.md and the registry have diverged. """ from __future__ import annotations import re from pathlib import Path from decnet_behave_shell.spec import PRIMITIVE_REGISTRY, ValueKind # Primitive paths expected by scratchpad.md (hand-extracted; v0.1). EXPECTED_PRIMITIVES = { # motor.* "motor.keystroke_cadence", "motor.motor_stability", "motor.error_correction", "motor.command_chunking", "motor.paste_burst_rate", "motor.input_modality", "motor.shell_mastery.tab_completion", "motor.shell_mastery.shortcut_usage", "motor.shell_mastery.pipe_chaining_depth", # cognitive.* "cognitive.cognitive_load", "cognitive.exploration_style", "cognitive.planning_depth", "cognitive.tool_vocabulary", "cognitive.inter_command_latency_class", "cognitive.inter_command_consistency", "cognitive.command_branch_diversity", "cognitive.feedback_loop_engagement", "cognitive.error_resilience.retry_tactic", "cognitive.error_resilience.frustration_typing", "cognitive.error_resilience.fallback_to_man", # temporal.* "temporal.session_timing", "temporal.session_duration", "temporal.escalation_pattern", "temporal.persistence", "temporal.lifecycle_markers.landing_ritual", "temporal.lifecycle_markers.exit_behavior", "temporal.lifecycle_markers.idle_periodicity", # operational.* "operational.opsec_discipline", "operational.cleanup_behavior", "operational.objective", "operational.multi_actor_indicators", # environmental.* "environmental.keyboard_layout", "environmental.locale", "environmental.numpad_usage", "environmental.terminal_multiplexer", "environmental.shell_type", # cultural.* "cultural.meal_break_gaps", "cultural.periodic_micro_pauses", "cultural.dst_behavior", "cultural.weekend_cadence", "cultural.holiday_gaps", # emotional_valence.* "emotional_valence.valence", "emotional_valence.arousal", "emotional_valence.stress_response", "emotional_valence.frustration_venting", # toolchain.tls.* "toolchain.tls.ja3_client", "toolchain.tls.ja3s_server", "toolchain.tls.ja4_client", "toolchain.tls.ja4s_server", "toolchain.tls.jarm_server", "toolchain.tls.tls_cert_simhash", # toolchain.transport.* "toolchain.transport.tcp_stack", "toolchain.transport.h2_akamai_fingerprint", "toolchain.transport.quic_client", # toolchain.ssh.* "toolchain.ssh.hassh_client", "toolchain.ssh.hassh_server", "toolchain.ssh.ssh_client_banner", "toolchain.ssh.kex_algorithm_order", # toolchain.http.* "toolchain.http.user_agent_tool_class", "toolchain.http.header_order_fingerprint", "toolchain.http.body_oddities", # toolchain.c2.* "toolchain.c2.beacon_family", "toolchain.c2.beacon_interval_ms", "toolchain.c2.beacon_jitter_cv", "toolchain.c2.sleep_skew", "toolchain.c2.c2_callback_endpoint", "toolchain.c2.attack_software_id", # toolchain.protocol_abuse.* "toolchain.protocol_abuse.dns_exfil_tool", "toolchain.protocol_abuse.smb_dialect", "toolchain.protocol_abuse.kerberos_etype_offer", "toolchain.protocol_abuse.ldap_bind_pattern", "toolchain.protocol_abuse.responder_signature", "toolchain.protocol_abuse.mitm6_signature", # toolchain.payload.* "toolchain.payload.payload_simhash", "toolchain.payload.payload_entropy_class", "toolchain.payload.loader_family", } def test_registry_covers_expected_primitives_exactly(): registry_keys = set(PRIMITIVE_REGISTRY.keys()) missing = EXPECTED_PRIMITIVES - registry_keys extra = registry_keys - EXPECTED_PRIMITIVES assert not missing, f"registry missing: {sorted(missing)}" assert not extra, f"registry has unexpected entries: {sorted(extra)}" def test_every_primitive_has_a_valid_spec(): for primitive, spec in PRIMITIVE_REGISTRY.items(): if spec.kind is ValueKind.CATEGORICAL: assert spec.allowed, f"{primitive}: categorical must define `allowed`" assert all(isinstance(v, str) for v in spec.allowed) elif spec.kind is ValueKind.ARRAY: assert spec.array_of is not None, f"{primitive}: array must define `array_of`" assert spec.array_of is not ValueKind.ARRAY, ( f"{primitive}: nested arrays not supported in v0.1" ) def test_primitive_paths_are_dotted_lowercase(): pattern = re.compile(r"^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$") for primitive in PRIMITIVE_REGISTRY: assert pattern.match(primitive), f"malformed primitive path: {primitive!r}"