import test from 'brittle'
import { tmpDir, fs, path } from './helpers.js'
import { parseIgnoreFile, createMatcher, loadIgnoreFile, getDefaultDenyList } from '../lib/ignore.js'
// ── parseIgnoreFile ───────────────────────────────────────────
test('parseIgnoreFile — basic patterns', (t) => {
const patterns = parseIgnoreFile('*.log\nbuild/\n\n# comment\ntemp')
t.alike(patterns, ['*.log', 'build/', 'temp'])
})
test('parseIgnoreFile — handles empty', (t) => {
t.alike(parseIgnoreFile(''), [])
t.alike(parseIgnoreFile('\n\n\n'), [])
})
test('parseIgnoreFile — trims whitespace', (t) => {
const patterns = parseIgnoreFile(' *.tmp \n build ')
t.alike(patterns, ['*.tmp', 'build'])
})
// ── createMatcher ─────────────────────────────────────────────
test('matcher — blocks default deny list', (t) => {
const isIgnored = createMatcher()
t.ok(isIgnored('.env', '.env'), '.env blocked')
t.ok(isIgnored('.env.local', '.env.local'), '.env.local blocked')
t.ok(isIgnored('.ssh', '.ssh'), '.ssh blocked')
t.ok(isIgnored('.gnupg', '.gnupg'), '.gnupg blocked')
t.ok(isIgnored('server.key', 'server.key'), '*.key blocked')
t.ok(isIgnored('cert.pem', 'cert.pem'), '*.pem blocked')
t.ok(isIgnored('.git', '.git'), '.git blocked')
t.ok(isIgnored('node_modules', 'node_modules'), 'node_modules blocked')
t.ok(isIgnored('.DS_Store', '.DS_Store'), '.DS_Store blocked')
})
test('matcher — allows normal files', (t) => {
const isIgnored = createMatcher()
t.absent(isIgnored('app.js', 'app.js'), 'app.js allowed')
t.absent(isIgnored('docs/readme.md', 'readme.md'), 'readme.md allowed')
t.absent(isIgnored('photo.jpg', 'photo.jpg'), 'photo.jpg allowed')
})
test('matcher — custom patterns', (t) => {
const isIgnored = createMatcher(['*.log', 'build/', 'temp*'])
t.ok(isIgnored('debug.log', 'debug.log'), '*.log matched')
t.ok(isIgnored('build', 'build'), 'build/ matched')
t.ok(isIgnored('tempfile', 'tempfile'), 'temp* matched')
t.absent(isIgnored('app.js', 'app.js'), 'app.js not matched')
})
test('matcher — negation patterns', (t) => {
const isIgnored = createMatcher(['*.log', '!important.log'])
t.ok(isIgnored('debug.log', 'debug.log'), 'debug.log blocked')
t.absent(isIgnored('important.log', 'important.log'), 'important.log allowed by negation')
})
test('matcher — ** glob matches deep paths', (t) => {
const isIgnored = createMatcher(['build/**'])
t.ok(isIgnored('build/output.js', 'output.js'), 'build/output.js matched')
t.ok(isIgnored('build/sub/deep/file.js', 'file.js'), 'deep path matched')
t.absent(isIgnored('src/build.js', 'build.js'), 'src/build.js not matched')
})
// ── loadIgnoreFile ────────────────────────────────────────────
test('loadIgnoreFile — reads .overlayignore', (t) => {
const dir = tmpDir('ignore')
fs.writeFileSync(path.join(dir, '.overlayignore'), '*.tmp\nbuild/\n# comment\n')
const patterns = loadIgnoreFile(dir)
t.alike(patterns, ['*.tmp', 'build/'])
})
test('loadIgnoreFile — returns empty for missing file', (t) => {
const dir = tmpDir('ignore-empty')
const patterns = loadIgnoreFile(dir)
t.alike(patterns, [])
})
// ── getDefaultDenyList ────────────────────────────────────────
test('getDefaultDenyList — returns a copy', (t) => {
const list = getDefaultDenyList()
t.ok(list.length > 0)
t.ok(list.includes('.env'))
t.ok(list.includes('.ssh'))
// Modifying the returned list shouldn't affect future calls
list.push('custom')
const list2 = getDefaultDenyList()
t.absent(list2.includes('custom'))
})
// (v1 watch-manager integration test removed with the watch-manager subsystem —
// hyper-overlay 0.3.0 is a pure content-addressed distribution library.)