test: add freebsd-timezones and env-audit tests

54 new tests: pure timezone helpers (getTimezoneOptions, prioritizeTimezones,
findTimezoneOption, isValidTimezone) with no mocks, plus auditEnvFile covering
defaults, missing key detection, feature-flag warnings, GIT_MIRROR_URLS parsing,
and value masking — using real temp files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---
Build: FAIL | Tests: pass — Tests  1363 passed (1363)
This commit is contained in:
Clawdie AI 2026-04-15 05:12:49 +00:00
parent 01a501a045
commit abe3be71fd
2 changed files with 433 additions and 0 deletions

271
setup/env-audit.test.ts Normal file
View file

@ -0,0 +1,271 @@
/**
* setup/env-audit.test.ts auditEnvFile unit tests.
*
* Tests defaults, missing keys, warnings, and feature-flag interactions.
* Uses real temp files (simpler than mocking fs for a function that
* reads env file content line-by-line).
*
* Run with: npx vitest run setup/env-audit.test.ts
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { vi } from 'vitest';
import fs from 'fs';
import os from 'os';
import path from 'path';
// ---------------------------------------------------------------------------
// Mock non-auditEnvFile deps (only used by run(), not by auditEnvFile)
// ---------------------------------------------------------------------------
vi.mock('./status.js', () => ({ emitStatus: vi.fn() }));
vi.mock('./platform.js', () => ({ getPlatform: vi.fn().mockReturnValue('freebsd') }));
import { auditEnvFile } from './env-audit.js';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
let tmpDir: string;
let envFile: string;
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'env-audit-test-'));
envFile = path.join(tmpDir, '.env');
});
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
function writeEnv(content: string): void {
fs.writeFileSync(envFile, content, 'utf-8');
}
// ---------------------------------------------------------------------------
// Missing env file
// ---------------------------------------------------------------------------
describe('auditEnvFile — no env file', () => {
it('uses defaults when file does not exist', () => {
const result = auditEnvFile(path.join(tmpDir, 'nonexistent.env'));
expect(result.values.AGENT_NAME).toBe('clawdie');
expect(result.values.AGENT_SUBNET_BASE).toBe('10.0.0');
});
it('ASSISTANT_NAME is in missing list when file is absent', () => {
const result = auditEnvFile(path.join(tmpDir, 'nonexistent.env'));
expect(result.missing).toContain('ASSISTANT_NAME');
});
});
// ---------------------------------------------------------------------------
// Defaults
// ---------------------------------------------------------------------------
describe('auditEnvFile — defaults', () => {
it('uses "clawdie" as default AGENT_NAME', () => {
writeEnv('');
const result = auditEnvFile(envFile);
expect(result.values.AGENT_NAME).toBe('clawdie');
});
it('defaults AGENT_SUBNET_BASE to 10.0.0', () => {
writeEnv('');
const result = auditEnvFile(envFile);
expect(result.values.AGENT_SUBNET_BASE).toBe('10.0.0');
});
it('derives WARDEN_GATEWAY from subnet base', () => {
writeEnv('AGENT_SUBNET_BASE=192.168.1\n');
const result = auditEnvFile(envFile);
expect(result.values.WARDEN_GATEWAY).toBe('192.168.1.1');
});
it('defaults WARDEN_DB_IP to subnet.3', () => {
writeEnv('AGENT_SUBNET_BASE=10.1.2\n');
const result = auditEnvFile(envFile);
expect(result.values.WARDEN_DB_IP).toBe('10.1.2.3');
});
it('defaults CODE_HOSTING_MODE to "git"', () => {
writeEnv('');
const result = auditEnvFile(envFile);
expect(result.values.CODE_HOSTING_MODE).toBe('git');
});
it('defaults DB_RUNTIME to "jail"', () => {
writeEnv('');
const result = auditEnvFile(envFile);
expect(result.values.DB_RUNTIME).toBe('jail');
});
it('lowercases AGENT_NAME value', () => {
writeEnv('AGENT_NAME=MyBot\n');
const result = auditEnvFile(envFile);
expect(result.values.AGENT_NAME).toBe('mybot');
});
});
// ---------------------------------------------------------------------------
// Missing key detection
// ---------------------------------------------------------------------------
describe('auditEnvFile — missing keys', () => {
it('adds ASSISTANT_NAME to missing when absent', () => {
writeEnv('AGENT_NAME=clawdie\n');
const result = auditEnvFile(envFile);
expect(result.missing).toContain('ASSISTANT_NAME');
});
it('ASSISTANT_NAME is not missing when set', () => {
writeEnv('ASSISTANT_NAME=Clawdie\n');
const result = auditEnvFile(envFile);
expect(result.missing).not.toContain('ASSISTANT_NAME');
});
it('returns empty missing array for a complete minimal env', () => {
writeEnv('ASSISTANT_NAME=Clawdie\n');
const result = auditEnvFile(envFile);
expect(result.missing).toHaveLength(0);
});
});
// ---------------------------------------------------------------------------
// Warnings
// ---------------------------------------------------------------------------
describe('auditEnvFile — warnings', () => {
it('warns when DISPLAY_LOCALE is not set', () => {
writeEnv('ASSISTANT_NAME=Clawdie\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.includes('DISPLAY_LOCALE'))).toBe(true);
});
it('no DISPLAY_LOCALE warning when set', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nDISPLAY_LOCALE=en-GB\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.startsWith('DISPLAY_LOCALE') || w.includes('DISPLAY_LOCALE not set'))).toBe(false);
});
it('warns when FEATURE_TAILSCALE=YES but TAILSCALE_AUTHKEY not set', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nFEATURE_TAILSCALE=YES\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.includes('TAILSCALE_AUTHKEY'))).toBe(true);
});
it('no tailscale authkey warning when authkey is set', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nFEATURE_TAILSCALE=YES\nTAILSCALE_AUTHKEY=tskey-abc\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.includes('TAILSCALE_AUTHKEY'))).toBe(false);
});
it('warns when FEATURE_GITEA=YES but FEATURE_GIT is not enabled', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nFEATURE_GITEA=YES\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.includes('FEATURE_GITEA'))).toBe(true);
});
it('warns when LOCAL_LLM_PROVIDER=ollama but FEATURE_OLLAMA is not enabled', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nLOCAL_LLM_PROVIDER=ollama\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.includes('FEATURE_OLLAMA'))).toBe(true);
});
it('warns when LOCAL_LLM_PROVIDER=llama_cpp but FEATURE_LLAMA_CPP is not enabled', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nLOCAL_LLM_PROVIDER=llama_cpp\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.includes('FEATURE_LLAMA_CPP'))).toBe(true);
});
it('no LLM provider warning when provider=none', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nLOCAL_LLM_PROVIDER=none\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.includes('FEATURE_OLLAMA') || w.includes('FEATURE_LLAMA_CPP'))).toBe(false);
});
it('warns when SSH_PUBLIC_KEY not set', () => {
writeEnv('ASSISTANT_NAME=Clawdie\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.includes('SSH_PUBLIC_KEY'))).toBe(true);
});
it('no SSH warning when SSH_PUBLIC_KEY is set', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nSSH_PUBLIC_KEY=ssh-ed25519 AAAA...\n');
const result = auditEnvFile(envFile);
expect(result.warnings.some((w) => w.includes('SSH_PUBLIC_KEY'))).toBe(false);
});
});
// ---------------------------------------------------------------------------
// Feature flags (isTruthyFlag)
// ---------------------------------------------------------------------------
describe('auditEnvFile — feature flags', () => {
it('recognises YES as truthy', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nFEATURE_GIT=YES\n');
const result = auditEnvFile(envFile);
expect(result.values.FEATURE_GIT).toBe('YES');
});
it('recognises true as truthy', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nFEATURE_GIT=true\n');
const result = auditEnvFile(envFile);
expect(result.values.FEATURE_GIT).toBe('YES');
});
it('recognises 1 as truthy', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nFEATURE_GIT=1\n');
const result = auditEnvFile(envFile);
expect(result.values.FEATURE_GIT).toBe('YES');
});
it('treats NO as falsy for feature flags', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nFEATURE_GIT=NO\n');
const result = auditEnvFile(envFile);
expect(result.values.FEATURE_GIT).toBe('NO');
});
});
// ---------------------------------------------------------------------------
// GIT_MIRROR_URLS parsing
// ---------------------------------------------------------------------------
describe('auditEnvFile — GIT_MIRROR_URLS', () => {
it('parses comma-separated mirror URLs', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nGIT_MIRROR_URLS=https://a.com,https://b.com\n');
const result = auditEnvFile(envFile);
expect(result.values.GIT_MIRROR_URLS).toContain('https://a.com');
expect(result.values.GIT_MIRROR_URLS).toContain('https://b.com');
});
it('shows (unset) when GIT_MIRROR_URLS is empty', () => {
writeEnv('ASSISTANT_NAME=Clawdie\n');
const result = auditEnvFile(envFile);
expect(result.values.GIT_MIRROR_URLS).toBe('(unset)');
});
});
// ---------------------------------------------------------------------------
// TAILSCALE_AUTHKEY masking
// ---------------------------------------------------------------------------
describe('auditEnvFile — value masking', () => {
it('shows "(set)" for TAILSCALE_AUTHKEY when present', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nTAILSCALE_AUTHKEY=tskey-secret\n');
const result = auditEnvFile(envFile);
expect(result.values.TAILSCALE_AUTHKEY).toBe('(set)');
});
it('shows "(unset)" for TAILSCALE_AUTHKEY when absent', () => {
writeEnv('ASSISTANT_NAME=Clawdie\n');
const result = auditEnvFile(envFile);
expect(result.values.TAILSCALE_AUTHKEY).toBe('(unset)');
});
it('shows "(set)" for SSH_PUBLIC_KEY when present', () => {
writeEnv('ASSISTANT_NAME=Clawdie\nSSH_PUBLIC_KEY=ssh-ed25519 AAAA...\n');
const result = auditEnvFile(envFile);
expect(result.values.SSH_PUBLIC_KEY).toBe('(set)');
});
});

View file

@ -0,0 +1,162 @@
/**
* setup/freebsd-timezones.test.ts pure timezone helper tests.
*
* No mocks needed all functions are pure.
*
* Run with: npx vitest run setup/freebsd-timezones.test.ts
*/
import { describe, it, expect } from 'vitest';
import {
getTimezoneOptions,
formatTimezoneLabel,
prioritizeTimezones,
findTimezoneOption,
isValidTimezone,
} from './freebsd-timezones.js';
// ---------------------------------------------------------------------------
// getTimezoneOptions
// ---------------------------------------------------------------------------
describe('getTimezoneOptions', () => {
it('returns a non-empty array', () => {
expect(getTimezoneOptions().length).toBeGreaterThan(0);
});
it('includes Europe/Ljubljana', () => {
const opts = getTimezoneOptions();
expect(opts.some((o) => o.timezone === 'Europe/Ljubljana')).toBe(true);
});
it('includes UTC', () => {
const opts = getTimezoneOptions();
expect(opts.some((o) => o.timezone === 'UTC')).toBe(true);
});
it('every entry has timezone, label, and region fields', () => {
for (const opt of getTimezoneOptions()) {
expect(typeof opt.timezone).toBe('string');
expect(typeof opt.label).toBe('string');
expect(typeof opt.region).toBe('string');
}
});
it('returns the same reference each call (stable array)', () => {
// Both calls should return equal content (not necessarily same reference)
expect(getTimezoneOptions()).toEqual(getTimezoneOptions());
});
});
// ---------------------------------------------------------------------------
// formatTimezoneLabel
// ---------------------------------------------------------------------------
describe('formatTimezoneLabel', () => {
it('returns the label field of the option', () => {
const opt = { timezone: 'UTC', label: 'UTC (Coordinated Universal Time)', region: 'Etc' };
expect(formatTimezoneLabel(opt)).toBe('UTC (Coordinated Universal Time)');
});
});
// ---------------------------------------------------------------------------
// prioritizeTimezones
// ---------------------------------------------------------------------------
describe('prioritizeTimezones', () => {
const opts = getTimezoneOptions();
it('returns list unchanged when preferred is null', () => {
const result = prioritizeTimezones(opts, null);
expect(result).toEqual(opts);
});
it('returns list unchanged when preferred is undefined', () => {
const result = prioritizeTimezones(opts);
expect(result).toEqual(opts);
});
it('moves preferred timezone to first position', () => {
const result = prioritizeTimezones(opts, 'UTC');
expect(result[0].timezone).toBe('UTC');
});
it('keeps preferred timezone in list (no duplication)', () => {
const result = prioritizeTimezones(opts, 'UTC');
const count = result.filter((o) => o.timezone === 'UTC').length;
expect(count).toBe(1);
});
it('preserves total length when preferred is found', () => {
const result = prioritizeTimezones(opts, 'America/New_York');
expect(result).toHaveLength(opts.length);
});
it('preserves total length when preferred is not found', () => {
const result = prioritizeTimezones(opts, 'Unknown/Timezone');
expect(result).toHaveLength(opts.length);
});
it('returns list unchanged when preferred is not in list', () => {
const result = prioritizeTimezones(opts, 'Unknown/Timezone');
expect(result).toEqual(opts);
});
it('does not mutate the original array', () => {
const original = [...opts];
prioritizeTimezones(opts, 'UTC');
expect(opts).toEqual(original);
});
it('places Ljubljana first when preferred', () => {
const result = prioritizeTimezones(opts, 'Europe/Ljubljana');
expect(result[0].timezone).toBe('Europe/Ljubljana');
});
});
// ---------------------------------------------------------------------------
// findTimezoneOption
// ---------------------------------------------------------------------------
describe('findTimezoneOption', () => {
it('returns the option for a known timezone', () => {
const result = findTimezoneOption('UTC');
expect(result).not.toBeUndefined();
expect(result?.timezone).toBe('UTC');
});
it('returns undefined for an unknown timezone', () => {
expect(findTimezoneOption('Mars/Olympus')).toBeUndefined();
});
it('returns the correct label for America/New_York', () => {
const result = findTimezoneOption('America/New_York');
expect(result?.label).toContain('New York');
});
});
// ---------------------------------------------------------------------------
// isValidTimezone
// ---------------------------------------------------------------------------
describe('isValidTimezone', () => {
it('returns true for UTC', () => {
expect(isValidTimezone('UTC')).toBe(true);
});
it('returns true for Europe/Ljubljana', () => {
expect(isValidTimezone('Europe/Ljubljana')).toBe(true);
});
it('returns false for an unknown timezone string', () => {
expect(isValidTimezone('Fake/Zone')).toBe(false);
});
it('returns false for empty string', () => {
expect(isValidTimezone('')).toBe(false);
});
it('is case-sensitive (lowercase fails)', () => {
expect(isValidTimezone('utc')).toBe(false);
});
});