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 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 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' ) }) })