Comments in tracked files pointed readers at AGENTS/specs/*.md, AGENTS.md, and JOB-BRIEF-FINDINGS.md -- all gitignored, transient dev notes not part of the base repo. Rewrote each to be self-contained or point at tracked docs (docs/fish-config.md) instead.
332 lines
12 KiB
Fish
332 lines
12 KiB
Fish
#!/usr/bin/env fish
|
|
# Copyright (C) 2026 Rootiest
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
#
|
|
# Table-driven coverage of the opinionated-component guard system:
|
|
# __fish_variable_check, __fish_config_op_cascade,
|
|
# __fish_config_op_registry_lookup and __fish_config_op_enabled.
|
|
#
|
|
# Runs isolated (no `# MODE:` marker, which means isolated): the driver gives
|
|
# this process temp XDG dirs and --no-config, so there is no loaded config and
|
|
# no universal variables. That is load-bearing in both directions --
|
|
#
|
|
# * these cases `set -e` real guard variable names, including
|
|
# __fish_config_op_logging and __fish_config_opinionated. Under a plain
|
|
# `fish` those erase from the innermost scope holding the variable, which
|
|
# for a user with the variable set universally means destroying a real
|
|
# universal variable. The temp XDG_CONFIG_HOME is what makes that
|
|
# impossible.
|
|
# * the two `set -p` / `source` lines below are what make the FORK's code the
|
|
# code under test. Their failure modes differ and the difference matters:
|
|
# forgetting the fish_function_path prepend fails LOUDLY (the guard
|
|
# functions are unresolvable under --no-config and a call exits 127, which
|
|
# can never equal an expected 0/1/2/3, so every case turns red), while
|
|
# forgetting the registry source fails SILENTLY (__fish_config_op_enabled
|
|
# fail-opens on a missing entry, so a third of the table would pass for
|
|
# entirely the wrong reason). Hence the explicit precondition below.
|
|
|
|
source (realpath (dirname (status filename)))/lib.fish
|
|
set -p fish_function_path $repo_root/functions
|
|
source $repo_root/conf.d/__fish_config_op_registry.fish
|
|
|
|
section "guards: preconditions"
|
|
|
|
check "the fork's registry is loaded" 65 (count $__fish_config_op_registry_keys)
|
|
|
|
section "__fish_variable_check: truthy"
|
|
|
|
for v in 1 true yes on y Y TRUE ON
|
|
set -g __probe_v $v
|
|
__fish_variable_check __probe_v
|
|
check "truthy '$v' -> 0" 0 $status
|
|
end
|
|
|
|
section "__fish_variable_check: falsy"
|
|
|
|
for v in 0 false no off n OFF FALSE
|
|
set -g __probe_v $v
|
|
__fish_variable_check __probe_v
|
|
check "falsy '$v' -> 1" 1 $status
|
|
end
|
|
|
|
section "__fish_variable_check: neither"
|
|
|
|
set -e __probe_v
|
|
__fish_variable_check __probe_v
|
|
check "unset -> 2" 2 $status
|
|
|
|
set -g __probe_v ""
|
|
__fish_variable_check __probe_v
|
|
check "empty string -> 2" 2 $status
|
|
|
|
set -g __probe_v banana
|
|
__fish_variable_check __probe_v
|
|
check "unrecognized -> 3" 3 $status
|
|
|
|
__fish_variable_check
|
|
check "no argument -> 2" 2 $status
|
|
|
|
set -g __probe_v a b
|
|
__fish_variable_check __probe_v
|
|
check "multi-element list -> 3" 3 $status
|
|
set -e __probe_v
|
|
|
|
section "cascade: category only"
|
|
|
|
# Invented variable names the real config can never set, so nothing ambient can
|
|
# perturb these -- only the C5 and master cases below need real names.
|
|
set -e __fish_config_opinionated __probe_cat __probe_cat_sub
|
|
|
|
__fish_config_op_cascade __probe_cat
|
|
check "all unset -> enabled" 0 $status
|
|
|
|
set -g __probe_cat 0
|
|
__fish_config_op_cascade __probe_cat
|
|
check "category falsy -> disabled" 1 $status
|
|
|
|
set -g __probe_cat 1
|
|
__fish_config_op_cascade __probe_cat
|
|
check "category truthy -> enabled" 0 $status
|
|
|
|
set -g __probe_cat garbage
|
|
__fish_config_op_cascade __probe_cat
|
|
check "category unrecognized defers to master -> enabled" 0 $status
|
|
set -e __probe_cat
|
|
|
|
section "cascade: subcategory beats category"
|
|
|
|
set -g __probe_cat 0
|
|
set -g __probe_cat_sub 1
|
|
__fish_config_op_cascade __probe_cat __probe_cat_sub
|
|
check "sub on, category off -> enabled" 0 $status
|
|
|
|
set -g __probe_cat 1
|
|
set -g __probe_cat_sub 0
|
|
__fish_config_op_cascade __probe_cat __probe_cat_sub
|
|
check "sub off, category on -> disabled" 1 $status
|
|
|
|
set -e __probe_cat_sub
|
|
set -g __probe_cat 0
|
|
__fish_config_op_cascade __probe_cat __probe_cat_sub
|
|
check "sub unset, category off -> disabled" 1 $status
|
|
|
|
set -g __probe_cat_sub garbage
|
|
__fish_config_op_cascade __probe_cat __probe_cat_sub
|
|
check "sub unrecognized defers, category off -> disabled" 1 $status
|
|
set -e __probe_cat __probe_cat_sub
|
|
|
|
__fish_config_op_cascade __probe_cat ""
|
|
check "empty subcategory argument -> category-only chain" 0 $status
|
|
|
|
section "cascade: the master is an off switch only"
|
|
|
|
set -g __fish_config_opinionated 0
|
|
__fish_config_op_cascade __probe_cat
|
|
check "master off, category unset -> disabled" 1 $status
|
|
|
|
set -g __probe_cat 1
|
|
__fish_config_op_cascade __probe_cat
|
|
check "master off, category on -> enabled" 0 $status
|
|
set -e __probe_cat
|
|
|
|
set -g __fish_config_opinionated 1
|
|
__fish_config_op_cascade __probe_cat
|
|
check "master on, category unset -> enabled" 0 $status
|
|
|
|
set -g __fish_config_opinionated garbage
|
|
__fish_config_op_cascade __probe_cat
|
|
check "master unrecognized, category unset -> enabled" 0 $status
|
|
set -e __fish_config_opinionated
|
|
|
|
section "cascade: C5 logging is opt-in"
|
|
|
|
# Uses the REAL __fish_config_op_logging name because the opt-in list lives
|
|
# inside the cascade keyed on it. C5 logging is opt-in by design (see
|
|
# docs/fish-config.md's "C5 -- Logging and Capture" section): unset or
|
|
# unrecognized means off, and the master switch cannot enable it. If any of
|
|
# these three fail, that is a regression in the guard itself, not the test.
|
|
set -e __fish_config_op_logging __fish_config_opinionated
|
|
|
|
__fish_config_op_cascade __fish_config_op_logging
|
|
check "C5 unset -> disabled" 1 $status
|
|
|
|
set -g __fish_config_opinionated 1
|
|
__fish_config_op_cascade __fish_config_op_logging
|
|
check "C5 unset + master truthy -> still disabled" 1 $status
|
|
set -e __fish_config_opinionated
|
|
|
|
set -g __fish_config_op_logging garbage
|
|
__fish_config_op_cascade __fish_config_op_logging
|
|
check "C5 unrecognized is not consent -> disabled" 1 $status
|
|
|
|
set -g __fish_config_op_logging on
|
|
__fish_config_op_cascade __fish_config_op_logging
|
|
check "C5 explicit truthy -> enabled" 0 $status
|
|
|
|
set -g __fish_config_op_logging off
|
|
__fish_config_op_cascade __fish_config_op_logging
|
|
check "C5 explicit falsy -> disabled" 1 $status
|
|
set -e __fish_config_op_logging
|
|
|
|
section "cascade: C5 subcategories inherit opt-in"
|
|
|
|
# The opt-in check reads chain[-1], which is always the CATEGORY variable, so
|
|
# nesting inherits "off unless explicit" with no per-subcategory special case.
|
|
set -e __fish_config_op_logging_terminal_capture
|
|
|
|
__fish_config_op_cascade __fish_config_op_logging __fish_config_op_logging_terminal_capture
|
|
check "C5 sub unset, C5 unset -> disabled" 1 $status
|
|
|
|
set -g __fish_config_op_logging_terminal_capture 1
|
|
__fish_config_op_cascade __fish_config_op_logging __fish_config_op_logging_terminal_capture
|
|
check "C5 sub explicit truthy -> enabled" 0 $status
|
|
|
|
set -g __fish_config_op_logging_terminal_capture 0
|
|
set -g __fish_config_op_logging on
|
|
__fish_config_op_cascade __fish_config_op_logging __fish_config_op_logging_terminal_capture
|
|
check "C5 sub falsy, C5 truthy -> disabled" 1 $status
|
|
set -e __fish_config_op_logging_terminal_capture __fish_config_op_logging
|
|
|
|
section "registry lookup"
|
|
|
|
__fish_config_op_registry_lookup cat "" >/dev/null
|
|
check "known unsited key found" 0 $status
|
|
|
|
set -l t (__fish_config_op_registry_lookup cat "")
|
|
check "cat's tags" aliases/filesystem "$t"
|
|
|
|
__fish_config_op_registry_lookup nosuchthing "" >/dev/null
|
|
check "unknown identity -> not found" 1 $status
|
|
|
|
set -l t2 (__fish_config_op_registry_lookup config cdpath)
|
|
check "sited key config:cdpath" overrides/environment "$t2"
|
|
|
|
# The key is the identity:site PAIR, not the identity alone.
|
|
__fish_config_op_registry_lookup cat wrongsite >/dev/null
|
|
check "known identity, wrong site -> not found" 1 $status
|
|
|
|
section "op_enabled: against the real registry"
|
|
|
|
set -e __fish_config_op_aliases __fish_config_op_aliases_filesystem
|
|
set -e __fish_config_opinionated
|
|
|
|
__fish_config_op_enabled cat
|
|
check "cat, nothing set -> enabled" 0 $status
|
|
|
|
__fish_config_op_enabled cat.fish
|
|
check "a .fish suffix is stripped" 0 $status
|
|
|
|
set -g __fish_config_op_aliases 0
|
|
__fish_config_op_enabled cat
|
|
check "cat, aliases off -> disabled" 1 $status
|
|
|
|
set -g __fish_config_op_aliases_filesystem 1
|
|
__fish_config_op_enabled cat
|
|
check "cat, aliases off but its subcategory on -> enabled" 0 $status
|
|
set -e __fish_config_op_aliases __fish_config_op_aliases_filesystem
|
|
|
|
# Fail-open: an unclassified identity, or one whose doc header has no
|
|
# # COMPONENT section, resolves to enabled. This is what keeps user-authored
|
|
# and third-party functions unaffected.
|
|
__fish_config_op_enabled __totally_unregistered somesite
|
|
check "no registry entry -> fail open" 0 $status
|
|
|
|
section "op_enabled: always/* and AND, via a synthetic registry"
|
|
|
|
# Why this fixture exists, so nobody deletes it as redundant:
|
|
#
|
|
# The generated registry has 65 entries, EVERY ONE carrying exactly one tag,
|
|
# and contains no always/on or always/off anywhere (measured 2026-09-07
|
|
# against conf.d/__fish_config_op_registry.fish). So three documented
|
|
# semantics -- always/off, always/on, and AND-across-tags -- have no reachable
|
|
# case in production data and would otherwise go completely untested.
|
|
#
|
|
# The registry is just two global lists, and in isolated mode nothing else in
|
|
# this process reads them, so overriding them is free.
|
|
set -g __fish_config_op_registry_keys "syn_on:" "syn_off:" "syn_and:" "syn_bare:" "syn_multi:"
|
|
set -g __fish_config_op_registry_values \
|
|
"always/on" \
|
|
"always/off" \
|
|
"aliases/filesystem integrations/notifications" \
|
|
"aliases" \
|
|
"always/off always/on"
|
|
|
|
set -e __fish_config_op_aliases __fish_config_op_integrations __fish_config_opinionated
|
|
|
|
__fish_config_op_enabled syn_off
|
|
check "always/off -> disabled" 1 $status
|
|
|
|
set -g __fish_config_op_aliases 1
|
|
__fish_config_op_enabled syn_off
|
|
check "always/off ignores an enabled category" 1 $status
|
|
set -e __fish_config_op_aliases
|
|
|
|
__fish_config_op_enabled syn_on
|
|
check "always/on -> enabled" 0 $status
|
|
|
|
set -g __fish_config_op_aliases 0
|
|
__fish_config_op_enabled syn_on
|
|
check "always/on short-circuits a disabled category" 0 $status
|
|
set -e __fish_config_op_aliases
|
|
|
|
__fish_config_op_enabled syn_multi
|
|
check "always/off beats always/on" 1 $status
|
|
|
|
section "op_enabled: AND across tagged sub-categories"
|
|
|
|
__fish_config_op_enabled syn_and
|
|
check "both categories default -> enabled" 0 $status
|
|
|
|
set -g __fish_config_op_aliases 0
|
|
__fish_config_op_enabled syn_and
|
|
check "first tag's category off -> disabled" 1 $status
|
|
set -e __fish_config_op_aliases
|
|
|
|
set -g __fish_config_op_integrations 0
|
|
__fish_config_op_enabled syn_and
|
|
check "second tag's category off -> disabled" 1 $status
|
|
set -e __fish_config_op_integrations
|
|
|
|
set -g __fish_config_op_aliases 1
|
|
set -g __fish_config_op_integrations 1
|
|
__fish_config_op_enabled syn_and
|
|
check "both explicitly on -> enabled" 0 $status
|
|
set -e __fish_config_op_aliases __fish_config_op_integrations
|
|
|
|
section "op_enabled: a tag with no slash"
|
|
|
|
# Degenerate but harmless: with no '/', $parts[2] is empty and the derived
|
|
# subcategory name gets a trailing underscore. That name is simply always
|
|
# unset, so the chain falls through to the category. Pinned so a future reader
|
|
# does not mistake it for a bug.
|
|
__fish_config_op_enabled syn_bare
|
|
check "bare tag 'aliases' -> enabled by default" 0 $status
|
|
|
|
set -g __fish_config_op_aliases 0
|
|
__fish_config_op_enabled syn_bare
|
|
check "bare tag honors its category" 1 $status
|
|
set -e __fish_config_op_aliases
|
|
|
|
section "op_enabled: C5 through the guard"
|
|
|
|
# The path production code actually takes, as opposed to calling the cascade
|
|
# directly. Same rule: unset means off and the master cannot enable it.
|
|
set -g __fish_config_op_registry_keys "syn_log:"
|
|
set -g __fish_config_op_registry_values "logging/terminal-capture"
|
|
set -e __fish_config_op_logging __fish_config_op_logging_terminal_capture
|
|
|
|
__fish_config_op_enabled syn_log
|
|
check "C5-tagged component, nothing set -> disabled" 1 $status
|
|
|
|
set -g __fish_config_opinionated 1
|
|
__fish_config_op_enabled syn_log
|
|
check "C5-tagged component + master truthy -> still disabled" 1 $status
|
|
set -e __fish_config_opinionated
|
|
|
|
set -g __fish_config_op_logging 1
|
|
__fish_config_op_enabled syn_log
|
|
check "C5-tagged component + explicit C5 on -> enabled" 0 $status
|
|
set -e __fish_config_op_logging
|
|
|
|
report
|