spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / mods / sec-default / tests / held-verdict.test.ts
GitHub Actions (publish-mods)chore: Update mods/ 3deb873 · 14 days ago History
189 lines · 5.9 KBRaw
1import { describe, expect, test, tier } from 'claude-code/testing'
2
3import Hooks from '../hooks'
4import Fixtures from './fixtures'
5
6tier('prepend')
7
8describe('held-verdict', () => {
9 test('over an allow: any deny, and an ask a rule or a hook decided', () => {
10 expect(
11 [
12 Fixtures.RULE_DENY,
13 Fixtures.RULE_ASK,
14 Fixtures.HOOK_DENY,
15 Fixtures.HOOK_ASK,
16 Fixtures.PLAIN_DENY,
17 Fixtures.ASKED,
18 Fixtures.ALLOWED,
19 ].map(held => Hooks.holdsOver(held, Fixtures.ALLOWED)),
20 ).toEqual([true, true, true, true, true, false, false])
21 })
22
23 test('over the same decision reworded; a stricter answer stands', () => {
24 expect(
25 [
26 Fixtures.RULE_DENY,
27 Fixtures.RULE_ASK,
28 Fixtures.HOOK_DENY,
29 Fixtures.HOOK_ASK,
30 ].map(held => [
31 Hooks.holdsOver(held, Fixtures.ASKED),
32 Hooks.holdsOver(held, Fixtures.PLAIN_DENY),
33 Hooks.holdsOver(held, { ...held }),
34 ]),
35 ).toEqual([
36 [true, true, false],
37 [true, false, false],
38 [true, true, false],
39 [true, false, false],
40 ])
41 })
42
43 test('an ask stripped of its rule, same sentence, is no same verdict', () => {
44 expect(
45 Hooks.holdsOver(Fixtures.RULE_ASK, {
46 decision: Fixtures.RULE_ASK.decision,
47 reason: Fixtures.RULE_ASK.reason,
48 }),
49 ).toBe(true)
50 })
51
52 test('a rule or hook the answer itself names makes nothing hold', () => {
53 expect(
54 Hooks.holdsOver(Fixtures.ASKED, {
55 ...Fixtures.ALLOWED,
56 rule: 'Bash(echo *)',
57 hook: 'PreToolUse',
58 }),
59 ).toBe(false)
60 })
61
62 test('the notice names the kind that held, and the rule when one did', () => {
63 expect(
64 [
65 Fixtures.RULE_DENY,
66 Fixtures.RULE_ASK,
67 { ...Fixtures.RULE_ASK, ...Fixtures.HOOK_ASK },
68 Fixtures.HOOK_ASK,
69 Fixtures.HOOK_DENY,
70 Fixtures.PLAIN_DENY,
71 ].map(held => Hooks.heldNotice('easy', 'Bash', held, 'lift')),
72 ).toEqual(
73 [
74 'a deny rule in your settings from a Bash call (Bash(echo *)); ' +
75 'the deny rule',
76 'an ask rule in your settings from a Bash call (Bash(echo *)); ' +
77 'the ask rule',
78 'an ask rule in your settings from a Bash call (Bash(echo *)); ' +
79 'the ask rule',
80 "a PreToolUse hook's ask from a Bash call; the PreToolUse hook's ask",
81 'a refusal from a Bash call; the refusal',
82 'a refusal from a Bash call; the refusal',
83 ].map(
84 middle =>
85 `easy tried to lift ${middle} holds over the plugins you install ` +
86 '(allowModsToOverrideDenyRules)',
87 ),
88 )
89 })
90
91 test('what no check vouches for is refused, and says why', () => {
92 expect(Hooks.UNCHECKED_DENY).toEqual({
93 decision: 'deny',
94 reason:
95 'the rules and hooks in your settings could not be checked for this ' +
96 'call, so it is refused',
97 })
98 })
99
100 test('an ask of theirs over a deny, caught, is no more vouched for', () => {
101 expect(
102 Hooks.caughtAnswer(Fixtures.ASKED, [
103 Fixtures.linkOf('easy', 'user', Fixtures.ASKED),
104 Fixtures.linkOf('engine', 'core', Fixtures.RULE_DENY),
105 ]),
106 ).toEqual(Hooks.UNCHECKED_DENY)
107 })
108
109 test('a link of theirs is named when it answered looser than handed', () => {
110 expect(
111 Hooks.rewrittenByUsers([
112 Fixtures.linkOf('listening', 'user', Fixtures.ALLOWED),
113 Fixtures.linkOf('easy', 'user', Fixtures.ALLOWED),
114 Fixtures.linkOf('engine', 'core', Fixtures.ASKED),
115 ]),
116 ).toEqual(['easy'])
117 })
118
119 test('one that never called next is measured against a deny', () => {
120 expect(
121 Hooks.rewrittenByUsers([
122 Fixtures.linkOf('listening', 'user', Fixtures.ALLOWED),
123 Fixtures.linkOf('blind', 'user', Fixtures.ALLOWED),
124 ]),
125 ).toEqual(['blind'])
126 })
127
128 test('one that reworded the same decision is named too', () => {
129 expect(
130 Hooks.rewrittenByUsers([
131 Fixtures.linkOf('reworder', 'user', Fixtures.ASKED),
132 Fixtures.linkOf('engine', 'core', Fixtures.RULE_ASK),
133 ]),
134 ).toEqual(['reworder'])
135 })
136
137 test('a tightening, a skipped link and links beneath theirs are not', () => {
138 expect(
139 Hooks.rewrittenByUsers([
140 Fixtures.linkOf('strict', 'user', Fixtures.PLAIN_DENY),
141 Fixtures.linkOf('passed-over', 'user'),
142 Fixtures.linkOf('suite', 'append', Fixtures.ALLOWED),
143 Fixtures.linkOf('bundled', 'builtin', Fixtures.ALLOWED),
144 Fixtures.linkOf('engine', 'core', Fixtures.RULE_DENY),
145 ]),
146 ).toEqual([])
147 })
148
149 test('a batch listed under prepend may hold a plugin of theirs', () => {
150 expect(
151 Hooks.rewrittenByUsers([
152 Fixtures.linkOf('audit+easy', 'prepend', Fixtures.ALLOWED),
153 Fixtures.linkOf('engine', 'core', Fixtures.RULE_DENY),
154 ]),
155 ).toEqual(['audit+easy'])
156 })
157
158 test('the handler keeps a verdict none of theirs made, or tightened', () => {
159 expect([
160 Hooks.caughtAnswer(Fixtures.PLAIN_DENY, [
161 Fixtures.linkOf('strict', 'user', Fixtures.PLAIN_DENY),
162 Fixtures.linkOf('engine', 'core', Fixtures.ASKED),
163 ]),
164 Hooks.caughtAnswer(Fixtures.ASKED, [
165 Fixtures.linkOf('easy', 'user'),
166 Fixtures.linkOf('engine', 'core', Fixtures.ASKED),
167 ]),
168 ]).toEqual([Fixtures.PLAIN_DENY, Fixtures.ASKED])
169 })
170
171 test('a deny of the rules they reworded is refused in our words', () => {
172 expect(
173 Hooks.caughtAnswer(Fixtures.PLAIN_DENY, [
174 Fixtures.linkOf('reworder', 'user', Fixtures.PLAIN_DENY),
175 Fixtures.linkOf('engine', 'core', Fixtures.RULE_DENY),
176 ]),
177 ).toEqual(Hooks.UNCHECKED_DENY)
178 })
179
180 test('the handler refuses a verdict they loosened, and no verdict', () => {
181 expect([
182 Hooks.caughtAnswer(Fixtures.ALLOWED, [
183 Fixtures.linkOf('easy', 'user', Fixtures.ALLOWED),
184 Fixtures.linkOf('engine', 'core', Fixtures.ASKED),
185 ]),
186 Hooks.caughtAnswer(undefined, []),
187 ]).toEqual([Hooks.UNCHECKED_DENY, Hooks.UNCHECKED_DENY])
188 })
189})