feat(doubles): add an any-call matcher for spy arguments #897

Description

@Chemaclass

Context

assert_have_been_called_with <spy> <expected> checks the last recorded call
(tail -n 1, src/test_doubles.sh:159). There is no way to assert "this spy was
called with these arguments at some point", which is the common intent when the
command under test invokes the same helper several times.

The workaround is to know the exact index and use
assert_have_been_called_with <spy> <expected> <n> or
assert_have_been_called_nth_with <n> <spy> <expected> — which couples the test
to call ordering that is usually irrelevant.

Evidence

functiontest_called_with_only_checks_last_call() {
bashunit::spy touch
touch first
touch second
assert_have_been_called_with touch "first"# FAILS: Expected 'first' but got 'second'
}

Nothing in the message says "checked the last call only", so the failure reads
like the call never happened.

Proposal

Add an any-call assertion, e.g.
assert_have_been_called_with_any <spy> <expected> (name open to discussion),
that scans every recorded line and passes if any matches.

Two smaller alternatives, if a new assertion is judged too much surface:

  • accept a literal any in the index position:
    assert_have_been_called_with touch "first" any
  • keep the assertion count as-is and only fix the message to state which call was
    compared (but got 'second' (last of 2 calls))

Recommendation: ship the new assertion and the clearer message. The message
fix is worth having regardless of which matcher the user picked.

Why this matters for agentic coding

Order-coupled assertions are a maintenance tax an agent pays repeatedly: it adds
a log line to a function, an unrelated assert_have_been_called_with breaks, and
the failure text points at neither the cause nor the fix. An any-call matcher lets
an agent write the assertion that expresses the actual requirement, which is
almost always "this side effect happened", not "this side effect happened last".

Acceptance criteria

  • Any-call matcher passes when a non-last call matches
  • Existing last-call and indexed behaviour unchanged
  • Failure message of assert_have_been_called_with states which call it
    compared
  • Tests in tests/functional/doubles_test.sh for match-on-first,
    match-on-middle, and no-match
  • Documented in docs/test-doubles.md, docs/public/bashunit-skill.md and
    docs/ai-agents.md (the argument-order table)
  • Bash 3.0+ compatible, make sa + make lint green

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(doubles): add an any-call matcher for spy arguments #897

Description

@Chemaclass

Context

assert_have_been_called_with <spy> <expected> checks the last recorded call
(tail -n 1, src/test_doubles.sh:159). There is no way to assert "this spy was
called with these arguments at some point", which is the common intent when the
command under test invokes the same helper several times.

The workaround is to know the exact index and use
assert_have_been_called_with <spy> <expected> <n> or
assert_have_been_called_nth_with <n> <spy> <expected> — which couples the test
to call ordering that is usually irrelevant.

Evidence

functiontest_called_with_only_checks_last_call() {
bashunit::spy touch
touch first
touch second
assert_have_been_called_with touch "first"# FAILS: Expected 'first' but got 'second'
}

Nothing in the message says "checked the last call only", so the failure reads
like the call never happened.

Proposal

Add an any-call assertion, e.g.
assert_have_been_called_with_any <spy> <expected> (name open to discussion),
that scans every recorded line and passes if any matches.

Two smaller alternatives, if a new assertion is judged too much surface:

  • accept a literal any in the index position:
    assert_have_been_called_with touch "first" any
  • keep the assertion count as-is and only fix the message to state which call was
    compared (but got 'second' (last of 2 calls))

Recommendation: ship the new assertion and the clearer message. The message
fix is worth having regardless of which matcher the user picked.

Why this matters for agentic coding

Order-coupled assertions are a maintenance tax an agent pays repeatedly: it adds
a log line to a function, an unrelated assert_have_been_called_with breaks, and
the failure text points at neither the cause nor the fix. An any-call matcher lets
an agent write the assertion that expresses the actual requirement, which is
almost always "this side effect happened", not "this side effect happened last".

Acceptance criteria

  • Any-call matcher passes when a non-last call matches
  • Existing last-call and indexed behaviour unchanged
  • Failure message of assert_have_been_called_with states which call it
    compared
  • Tests in tests/functional/doubles_test.sh for match-on-first,
    match-on-middle, and no-match
  • Documented in docs/test-doubles.md, docs/public/bashunit-skill.md and
    docs/ai-agents.md (the argument-order table)
  • Bash 3.0+ compatible, make sa + make lint green

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(doubles): add an any-call matcher for spy arguments #897

Description

@Chemaclass

Context

assert_have_been_called_with <spy> <expected> checks the last recorded call
(tail -n 1, src/test_doubles.sh:159). There is no way to assert "this spy was
called with these arguments at some point", which is the common intent when the
command under test invokes the same helper several times.

The workaround is to know the exact index and use
assert_have_been_called_with <spy> <expected> <n> or
assert_have_been_called_nth_with <n> <spy> <expected> — which couples the test
to call ordering that is usually irrelevant.

Evidence

functiontest_called_with_only_checks_last_call() {
bashunit::spy touch
touch first
touch second
assert_have_been_called_with touch "first"# FAILS: Expected 'first' but got 'second'
}

Nothing in the message says "checked the last call only", so the failure reads
like the call never happened.

Proposal

Add an any-call assertion, e.g.
assert_have_been_called_with_any <spy> <expected> (name open to discussion),
that scans every recorded line and passes if any matches.

Two smaller alternatives, if a new assertion is judged too much surface:

  • accept a literal any in the index position:
    assert_have_been_called_with touch "first" any
  • keep the assertion count as-is and only fix the message to state which call was
    compared (but got 'second' (last of 2 calls))

Recommendation: ship the new assertion and the clearer message. The message
fix is worth having regardless of which matcher the user picked.

Why this matters for agentic coding

Order-coupled assertions are a maintenance tax an agent pays repeatedly: it adds
a log line to a function, an unrelated assert_have_been_called_with breaks, and
the failure text points at neither the cause nor the fix. An any-call matcher lets
an agent write the assertion that expresses the actual requirement, which is
almost always "this side effect happened", not "this side effect happened last".

Acceptance criteria

  • Any-call matcher passes when a non-last call matches
  • Existing last-call and indexed behaviour unchanged
  • Failure message of assert_have_been_called_with states which call it
    compared
  • Tests in tests/functional/doubles_test.sh for match-on-first,
    match-on-middle, and no-match
  • Documented in docs/test-doubles.md, docs/public/bashunit-skill.md and
    docs/ai-agents.md (the argument-order table)
  • Bash 3.0+ compatible, make sa + make lint green

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(doubles): add an any-call matcher for spy arguments #897

Description

@Chemaclass

Context

assert_have_been_called_with <spy> <expected> checks the last recorded call
(tail -n 1, src/test_doubles.sh:159). There is no way to assert "this spy was
called with these arguments at some point", which is the common intent when the
command under test invokes the same helper several times.

The workaround is to know the exact index and use
assert_have_been_called_with <spy> <expected> <n> or
assert_have_been_called_nth_with <n> <spy> <expected> — which couples the test
to call ordering that is usually irrelevant.

Evidence

functiontest_called_with_only_checks_last_call() {
bashunit::spy touch
touch first
touch second
assert_have_been_called_with touch "first"# FAILS: Expected 'first' but got 'second'
}

Nothing in the message says "checked the last call only", so the failure reads
like the call never happened.

Proposal

Add an any-call assertion, e.g.
assert_have_been_called_with_any <spy> <expected> (name open to discussion),
that scans every recorded line and passes if any matches.

Two smaller alternatives, if a new assertion is judged too much surface:

  • accept a literal any in the index position:
    assert_have_been_called_with touch "first" any
  • keep the assertion count as-is and only fix the message to state which call was
    compared (but got 'second' (last of 2 calls))

Recommendation: ship the new assertion and the clearer message. The message
fix is worth having regardless of which matcher the user picked.

Why this matters for agentic coding

Order-coupled assertions are a maintenance tax an agent pays repeatedly: it adds
a log line to a function, an unrelated assert_have_been_called_with breaks, and
the failure text points at neither the cause nor the fix. An any-call matcher lets
an agent write the assertion that expresses the actual requirement, which is
almost always "this side effect happened", not "this side effect happened last".

Acceptance criteria

  • Any-call matcher passes when a non-last call matches
  • Existing last-call and indexed behaviour unchanged
  • Failure message of assert_have_been_called_with states which call it
    compared
  • Tests in tests/functional/doubles_test.sh for match-on-first,
    match-on-middle, and no-match
  • Documented in docs/test-doubles.md, docs/public/bashunit-skill.md and
    docs/ai-agents.md (the argument-order table)
  • Bash 3.0+ compatible, make sa + make lint green

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(doubles): add an any-call matcher for spy arguments #897

Description

@Chemaclass

Context

assert_have_been_called_with <spy> <expected> checks the last recorded call
(tail -n 1, src/test_doubles.sh:159). There is no way to assert "this spy was
called with these arguments at some point", which is the common intent when the
command under test invokes the same helper several times.

The workaround is to know the exact index and use
assert_have_been_called_with <spy> <expected> <n> or
assert_have_been_called_nth_with <n> <spy> <expected> — which couples the test
to call ordering that is usually irrelevant.

Evidence

functiontest_called_with_only_checks_last_call() {
bashunit::spy touch
touch first
touch second
assert_have_been_called_with touch "first"# FAILS: Expected 'first' but got 'second'
}

Nothing in the message says "checked the last call only", so the failure reads
like the call never happened.

Proposal

Add an any-call assertion, e.g.
assert_have_been_called_with_any <spy> <expected> (name open to discussion),
that scans every recorded line and passes if any matches.

Two smaller alternatives, if a new assertion is judged too much surface:

  • accept a literal any in the index position:
    assert_have_been_called_with touch "first" any
  • keep the assertion count as-is and only fix the message to state which call was
    compared (but got 'second' (last of 2 calls))

Recommendation: ship the new assertion and the clearer message. The message
fix is worth having regardless of which matcher the user picked.

Why this matters for agentic coding

Order-coupled assertions are a maintenance tax an agent pays repeatedly: it adds
a log line to a function, an unrelated assert_have_been_called_with breaks, and
the failure text points at neither the cause nor the fix. An any-call matcher lets
an agent write the assertion that expresses the actual requirement, which is
almost always "this side effect happened", not "this side effect happened last".

Acceptance criteria

  • Any-call matcher passes when a non-last call matches
  • Existing last-call and indexed behaviour unchanged
  • Failure message of assert_have_been_called_with states which call it
    compared
  • Tests in tests/functional/doubles_test.sh for match-on-first,
    match-on-middle, and no-match
  • Documented in docs/test-doubles.md, docs/public/bashunit-skill.md and
    docs/ai-agents.md (the argument-order table)
  • Bash 3.0+ compatible, make sa + make lint green

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(doubles): add an any-call matcher for spy arguments #897

Description

@Chemaclass

Context

assert_have_been_called_with <spy> <expected> checks the last recorded call
(tail -n 1, src/test_doubles.sh:159). There is no way to assert "this spy was
called with these arguments at some point", which is the common intent when the
command under test invokes the same helper several times.

The workaround is to know the exact index and use
assert_have_been_called_with <spy> <expected> <n> or
assert_have_been_called_nth_with <n> <spy> <expected> — which couples the test
to call ordering that is usually irrelevant.

Evidence

functiontest_called_with_only_checks_last_call() {
bashunit::spy touch
touch first
touch second
assert_have_been_called_with touch "first"# FAILS: Expected 'first' but got 'second'
}

Nothing in the message says "checked the last call only", so the failure reads
like the call never happened.

Proposal

Add an any-call assertion, e.g.
assert_have_been_called_with_any <spy> <expected> (name open to discussion),
that scans every recorded line and passes if any matches.

Two smaller alternatives, if a new assertion is judged too much surface:

  • accept a literal any in the index position:
    assert_have_been_called_with touch "first" any
  • keep the assertion count as-is and only fix the message to state which call was
    compared (but got 'second' (last of 2 calls))

Recommendation: ship the new assertion and the clearer message. The message
fix is worth having regardless of which matcher the user picked.

Why this matters for agentic coding

Order-coupled assertions are a maintenance tax an agent pays repeatedly: it adds
a log line to a function, an unrelated assert_have_been_called_with breaks, and
the failure text points at neither the cause nor the fix. An any-call matcher lets
an agent write the assertion that expresses the actual requirement, which is
almost always "this side effect happened", not "this side effect happened last".

Acceptance criteria

  • Any-call matcher passes when a non-last call matches
  • Existing last-call and indexed behaviour unchanged
  • Failure message of assert_have_been_called_with states which call it
    compared
  • Tests in tests/functional/doubles_test.sh for match-on-first,
    match-on-middle, and no-match
  • Documented in docs/test-doubles.md, docs/public/bashunit-skill.md and
    docs/ai-agents.md (the argument-order table)
  • Bash 3.0+ compatible, make sa + make lint green

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(doubles): add an any-call matcher for spy arguments #897

Description

@Chemaclass

Context

assert_have_been_called_with <spy> <expected> checks the last recorded call
(tail -n 1, src/test_doubles.sh:159). There is no way to assert "this spy was
called with these arguments at some point", which is the common intent when the
command under test invokes the same helper several times.

The workaround is to know the exact index and use
assert_have_been_called_with <spy> <expected> <n> or
assert_have_been_called_nth_with <n> <spy> <expected> — which couples the test
to call ordering that is usually irrelevant.

Evidence

functiontest_called_with_only_checks_last_call() {
bashunit::spy touch
touch first
touch second
assert_have_been_called_with touch "first"# FAILS: Expected 'first' but got 'second'
}

Nothing in the message says "checked the last call only", so the failure reads
like the call never happened.

Proposal

Add an any-call assertion, e.g.
assert_have_been_called_with_any <spy> <expected> (name open to discussion),
that scans every recorded line and passes if any matches.

Two smaller alternatives, if a new assertion is judged too much surface:

  • accept a literal any in the index position:
    assert_have_been_called_with touch "first" any
  • keep the assertion count as-is and only fix the message to state which call was
    compared (but got 'second' (last of 2 calls))

Recommendation: ship the new assertion and the clearer message. The message
fix is worth having regardless of which matcher the user picked.

Why this matters for agentic coding

Order-coupled assertions are a maintenance tax an agent pays repeatedly: it adds
a log line to a function, an unrelated assert_have_been_called_with breaks, and
the failure text points at neither the cause nor the fix. An any-call matcher lets
an agent write the assertion that expresses the actual requirement, which is
almost always "this side effect happened", not "this side effect happened last".

Acceptance criteria

  • Any-call matcher passes when a non-last call matches
  • Existing last-call and indexed behaviour unchanged
  • Failure message of assert_have_been_called_with states which call it
    compared
  • Tests in tests/functional/doubles_test.sh for match-on-first,
    match-on-middle, and no-match
  • Documented in docs/test-doubles.md, docs/public/bashunit-skill.md and
    docs/ai-agents.md (the argument-order table)
  • Bash 3.0+ compatible, make sa + make lint green

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(doubles): add an any-call matcher for spy arguments #897

Description

@Chemaclass

Context

assert_have_been_called_with <spy> <expected> checks the last recorded call
(tail -n 1, src/test_doubles.sh:159). There is no way to assert "this spy was
called with these arguments at some point", which is the common intent when the
command under test invokes the same helper several times.

The workaround is to know the exact index and use
assert_have_been_called_with <spy> <expected> <n> or
assert_have_been_called_nth_with <n> <spy> <expected> — which couples the test
to call ordering that is usually irrelevant.

Evidence

functiontest_called_with_only_checks_last_call() {
bashunit::spy touch
touch first
touch second
assert_have_been_called_with touch "first"# FAILS: Expected 'first' but got 'second'
}

Nothing in the message says "checked the last call only", so the failure reads
like the call never happened.

Proposal

Add an any-call assertion, e.g.
assert_have_been_called_with_any <spy> <expected> (name open to discussion),
that scans every recorded line and passes if any matches.

Two smaller alternatives, if a new assertion is judged too much surface:

  • accept a literal any in the index position:
    assert_have_been_called_with touch "first" any
  • keep the assertion count as-is and only fix the message to state which call was
    compared (but got 'second' (last of 2 calls))

Recommendation: ship the new assertion and the clearer message. The message
fix is worth having regardless of which matcher the user picked.

Why this matters for agentic coding

Order-coupled assertions are a maintenance tax an agent pays repeatedly: it adds
a log line to a function, an unrelated assert_have_been_called_with breaks, and
the failure text points at neither the cause nor the fix. An any-call matcher lets
an agent write the assertion that expresses the actual requirement, which is
almost always "this side effect happened", not "this side effect happened last".

Acceptance criteria

  • Any-call matcher passes when a non-last call matches
  • Existing last-call and indexed behaviour unchanged
  • Failure message of assert_have_been_called_with states which call it
    compared
  • Tests in tests/functional/doubles_test.sh for match-on-first,
    match-on-middle, and no-match
  • Documented in docs/test-doubles.md, docs/public/bashunit-skill.md and
    docs/ai-agents.md (the argument-order table)
  • Bash 3.0+ compatible, make sa + make lint green

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions