Files
dbmole-mcp/packages/core/test/unit/config/sources.test.ts
T

125 lines
4.5 KiB
TypeScript

import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import * as z from 'zod'
import { readConfigFile, readEnvConnections } from '../../../src/config/sources.js'
import type { StoreOptions } from '../../../src/config/store.js'
const schema = z.object({ name: z.string(), value: z.number() }).strict()
const opts: StoreOptions = { logLabel: 'testlabel' }
const conn = { name: 'a', value: 1 }
describe('readEnvConnections', () => {
let errSpy: ReturnType<typeof vi.spyOn>
beforeEach(() => {
errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
})
afterEach(() => {
vi.restoreAllMocks()
})
it('returns empty when the env var is unset', () => {
expect(readEnvConnections(schema, {}, 'X_CONNECTIONS', opts)).toEqual([])
expect(errSpy).not.toHaveBeenCalled()
})
it('parses a JSON array of connections', () => {
const env = { X_CONNECTIONS: JSON.stringify([conn]) }
expect(readEnvConnections(schema, env, 'X_CONNECTIONS', opts)).toEqual([conn])
})
it('ignores invalid JSON and logs with the label and var name', () => {
expect(
readEnvConnections(schema, { X_CONNECTIONS: 'nope{' }, 'X_CONNECTIONS', opts)
).toEqual([])
expect(errSpy).toHaveBeenCalledWith('testlabel: X_CONNECTIONS is not valid JSON, ignoring')
})
it('ignores non-array JSON and logs with the label and var name', () => {
expect(readEnvConnections(schema, { X_CONNECTIONS: '{}' }, 'X_CONNECTIONS', opts)).toEqual(
[]
)
expect(errSpy).toHaveBeenCalledWith(
'testlabel: X_CONNECTIONS must be a JSON array, ignoring'
)
})
})
describe('readConfigFile', () => {
let dir: string
let errSpy: ReturnType<typeof vi.spyOn>
beforeEach(() => {
dir = mkdtempSync(join(tmpdir(), 'core-config-'))
errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
})
afterEach(() => {
rmSync(dir, { recursive: true, force: true })
vi.restoreAllMocks()
})
it('returns empty when path is undefined', () => {
expect(readConfigFile(schema, undefined, opts)).toEqual([])
expect(errSpy).not.toHaveBeenCalled()
})
it('warns and returns empty for a missing file', () => {
const path = join(dir, 'absent.json')
expect(readConfigFile(schema, path, opts)).toEqual([])
expect(errSpy).toHaveBeenCalledWith(
'testlabel: config file ' + path + ' not found, ignoring'
)
})
it('reads the { connections: [...] } shape', () => {
const path = join(dir, 'config.json')
writeFileSync(path, JSON.stringify({ connections: [conn] }))
expect(readConfigFile(schema, path, opts)).toEqual([conn])
})
it('warns on invalid JSON', () => {
const path = join(dir, 'config.json')
writeFileSync(path, '{ broken')
expect(readConfigFile(schema, path, opts)).toEqual([])
expect(errSpy).toHaveBeenCalledWith(
'testlabel: config file ' + path + ' is not valid JSON, ignoring'
)
})
it('warns when the top level is an array, not an object', () => {
const path = join(dir, 'config.json')
writeFileSync(path, JSON.stringify([conn]))
expect(readConfigFile(schema, path, opts)).toEqual([])
expect(errSpy).toHaveBeenCalledWith(
'testlabel: config file ' + path + ' must contain { "connections": [...] }, ignoring'
)
})
it('warns on null config', () => {
const path = join(dir, 'config.json')
writeFileSync(path, 'null')
expect(readConfigFile(schema, path, opts)).toEqual([])
expect(errSpy).toHaveBeenCalled()
})
it('warns on primitive config', () => {
const path = join(dir, 'config.json')
writeFileSync(path, '"just a string"')
expect(readConfigFile(schema, path, opts)).toEqual([])
expect(errSpy).toHaveBeenCalled()
})
it('warns when the connections key is missing or not an array', () => {
const path = join(dir, 'config.json')
writeFileSync(path, JSON.stringify({ connections: 'nope' }))
expect(readConfigFile(schema, path, opts)).toEqual([])
expect(errSpy).toHaveBeenCalledWith(
'testlabel: config file ' + path + ' must contain { "connections": [...] }, ignoring'
)
})
})