mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(hooks): reuse compiled Codex matchers
This commit is contained in:
45
packages/hooks/hook-protocol/tests/matcher-lifecycle.spec.ts
Normal file
45
packages/hooks/hook-protocol/tests/matcher-lifecycle.spec.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { RRegex as RustRegex } from 'rregex'
|
||||
|
||||
describe('compileMatchers — native regex lifecycle', () => {
|
||||
it('constructs each unique Codex regex once across repeated matches and frees it once', async () => {
|
||||
const require = createRequire(import.meta.url)
|
||||
const rregex = require('rregex') as { RRegex: new(pattern: string) => RustRegex }
|
||||
const OriginalRRegex = rregex.RRegex
|
||||
const construct = vi.fn<(pattern: string) => void>()
|
||||
const free = vi.fn<() => void>()
|
||||
|
||||
class CountingRRegex extends OriginalRRegex {
|
||||
constructor(pattern: string) {
|
||||
super(pattern)
|
||||
construct(pattern)
|
||||
}
|
||||
|
||||
override free(): void {
|
||||
free()
|
||||
super.free()
|
||||
}
|
||||
}
|
||||
|
||||
rregex.RRegex = CountingRRegex
|
||||
vi.resetModules()
|
||||
try {
|
||||
const { compileMatchers } = await import('@deepseek-ai/dsh-hook-protocol/src/matcher.ts')
|
||||
const matchers = compileMatchers(['(?i)^bash$', '(?i)^bash$', '^write$'], 'codex')
|
||||
expect(construct.mock.calls.map(([pattern]) => pattern)).toEqual(['(?i)^bash$', '^write$'])
|
||||
|
||||
for (let i = 0; i < 1_000; i++) {
|
||||
expect(matchers.matches('(?i)^bash$', 'BASH')).toBe(true)
|
||||
}
|
||||
expect(construct).toHaveBeenCalledTimes(2)
|
||||
|
||||
matchers.dispose()
|
||||
matchers.dispose()
|
||||
expect(free).toHaveBeenCalledTimes(2)
|
||||
} finally {
|
||||
rregex.RRegex = OriginalRRegex
|
||||
vi.resetModules()
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { matcherDiagnostic, matchesMatcher } from '@deepseek-ai/dsh-hook-protocol'
|
||||
import { compileMatchers, matcherDiagnostic, matchesMatcher } from '@deepseek-ai/dsh-hook-protocol'
|
||||
|
||||
describe('matchesMatcher — match-all sentinels (both dialects)', () => {
|
||||
for (const mode of ['claude', 'codex'] as const) {
|
||||
@@ -82,3 +82,28 @@ describe('matcherDiagnostic — parse-time diagnostics', () => {
|
||||
expect(matcherDiagnostic('(?=Bash)', 'codex')).toBe('invalid codex regex matcher "(?=Bash)"')
|
||||
})
|
||||
})
|
||||
|
||||
describe('compileMatchers — config-lifetime reuse', () => {
|
||||
it('compiles a finite set, contains unknown patterns, and stops after disposal', () => {
|
||||
const matchers = compileMatchers([undefined, 'Edit|Write', '(?i)^bash$', '['], 'codex')
|
||||
|
||||
expect(matchers.matches(undefined, 'anything')).toBe(true)
|
||||
expect(matchers.matches('Edit|Write', 'Write')).toBe(true)
|
||||
expect(matchers.matches('Edit|Write', 'WriteFile')).toBe(false)
|
||||
expect(matchers.matches('(?i)^bash$', 'BASH')).toBe(true)
|
||||
expect(matchers.matches('[', 'anything')).toBe(false)
|
||||
expect(matchers.matches('not-compiled', 'not-compiled')).toBe(false)
|
||||
|
||||
matchers.dispose()
|
||||
expect(matchers.matches(undefined, 'anything')).toBe(false)
|
||||
expect(matchers.matches('(?i)^bash$', 'BASH')).toBe(false)
|
||||
expect(() => { matchers.dispose() }).not.toThrow()
|
||||
})
|
||||
|
||||
it('reuses JavaScript regexes too', () => {
|
||||
const matchers = compileMatchers(['^Bash$', '^Bash$'], 'claude')
|
||||
expect(matchers.matches('^Bash$', 'Bash')).toBe(true)
|
||||
expect(matchers.matches('^Bash$', 'BashOutput')).toBe(false)
|
||||
matchers.dispose()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user