Skip to content

Commit bd60384

Browse files
committed
test(lint): flag bare abort substring in toThrow assertions
1 parent 1975c27 commit bd60384

5 files changed

Lines changed: 34 additions & 4 deletions

File tree

‎packages/config-eslint/api-provider-hygiene.js‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,21 @@ export const apiProviderHygieneConfig = {
6363
},
6464
}
6565

66+
// Shared by every spec lane, not only src/api: an abort assertion must not pass on a raw SDK error.
67+
export const abortSubstringSelector = {
68+
selector: "CallExpression[callee.property.name='toThrow'][arguments.0.value=/^[Aa]bort(ed)?$/]",
69+
message:
70+
"A bare 'abort' substring also matches a raw SDK error. Assert the AbortError name and the full provider message.",
71+
}
72+
6673
// Spec files under src/api. A loop of expects in one test stops at the first failing case and hides the rest.
6774
export const apiProviderTestSelectors = [
6875
{
6976
selector: "ForOfStatement[right.type='ArrayExpression']:has(CallExpression[callee.name='expect'])",
7077
message:
7178
"Use it.each for repeated cases. A loop of expects in one test stops at the first failing case and does not name it.",
7279
},
80+
abortSubstringSelector,
7381
]
7482

7583
// A new @ts-ignore in a provider spec hides a private-member access. Use bracket notation or a typed double.

‎packages/config-eslint/api-provider-hygiene.test.js‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,22 @@ describe("api provider hygiene: spec loops", () => {
173173
assert.deepEqual(lintSpec(code), [])
174174
})
175175
}
176+
177+
for (const code of ['await expect(p).rejects.toThrow("aborted")', "expect(() => f()).toThrow('abort')"]) {
178+
it(`flags a bare abort substring: ${code}`, () => {
179+
assert.equal(lintSpec(code).length, 1)
180+
})
181+
}
182+
183+
for (const code of [
184+
'await expect(p).rejects.toThrow("The Requesty request was aborted")',
185+
'await expect(p).rejects.toMatchObject({ name: "AbortError" })',
186+
'await expect(p).rejects.toThrow("API Error")',
187+
]) {
188+
it(`allows: ${code}`, () => {
189+
assert.deepEqual(lintSpec(code), [])
190+
})
191+
}
176192
})
177193

178194
describe("api provider hygiene: hand-built AbortError", () => {

‎src/api/providers/__tests__/requesty.spec.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1208,7 +1208,7 @@ describe("RequestyHandler", () => {
12081208
// Aborting mid-flight is the third exit path through finally: the SDK-shaped mock
12091209
// rejects once the signal aborts, so dispose() must detach on that path too.
12101210
outcome: "aborts mid-flight",
1211-
expectRejection: "aborted",
1211+
expectRejection: "The Requesty request was aborted",
12121212
start: (controller: AbortController) => {
12131213
let notifyStarted!: () => void
12141214
const barrier = new Promise<void>((resolve) => {

‎src/core/task/__tests__/ask-queued-message-drain.spec.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ describe("Task.ask queued message drain", () => {
349349
access.abort = true
350350
finishAddingAsk()
351351

352-
await expect(ask).rejects.toThrow("aborted")
352+
await expect(ask).rejects.toThrow(/#ask\] task \S+ aborted$/)
353353
expect(task.messageQueueService.messages).toHaveLength(1)
354354
expect(task.messageQueueService.claimNextMessage()?.text).toBe("Persist me later")
355355
})

‎src/eslint.config.mjs‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { config } from "@roo-code/config-eslint/base"
22
import {
3+
abortSubstringSelector,
34
apiProviderHygieneConfig,
45
apiProviderTestSelectors,
56
apiSpecTsIgnoreConfig,
@@ -123,7 +124,7 @@ export default [
123124
"**/services/__tests__/workflow-self-listen.test.ts",
124125
],
125126
rules: {
126-
"no-restricted-syntax": ["error", ...testPathTraversalSelectors],
127+
"no-restricted-syntax": ["error", ...testPathTraversalSelectors, abortSubstringSelector],
127128
},
128129
},
129130
{
@@ -144,7 +145,12 @@ export default [
144145
"integrations/terminal/**/__tests__/**/*.ts",
145146
],
146147
rules: {
147-
"no-restricted-syntax": ["error", ...testPathTraversalSelectors, testResetHelperSelector],
148+
"no-restricted-syntax": [
149+
"error",
150+
...testPathTraversalSelectors,
151+
abortSubstringSelector,
152+
testResetHelperSelector,
153+
],
148154
},
149155
},
150156
{

0 commit comments

Comments
 (0)