Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions src/__tests__/globalConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* Global config tests.
*
* Tests global config loading and saving with piece_overrides,
* including empty array round-trip behavior.
*/

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import type { PersistedGlobalConfig } from '../core/models/persisted-global-config.js';

// Mock the getGlobalConfigPath to use a test directory
let testConfigPath: string;
vi.mock('../infra/config/paths.js', () => ({
getGlobalConfigPath: () => testConfigPath,
getGlobalTaktDir: () => join(testConfigPath, '..'),
getProjectTaktDir: vi.fn(),
getProjectCwd: vi.fn(),
}));

import { GlobalConfigManager } from '../infra/config/global/globalConfig.js';

describe('globalConfig', () => {
let testDir: string;

beforeEach(() => {
testDir = mkdtempSync(join(tmpdir(), 'takt-test-global-config-'));
mkdirSync(testDir, { recursive: true });
testConfigPath = join(testDir, 'config.yaml');
GlobalConfigManager.resetInstance();
});

afterEach(() => {
GlobalConfigManager.resetInstance();
if (testDir) {
rmSync(testDir, { recursive: true, force: true });
}
});

describe('piece_overrides empty array round-trip', () => {
it('should preserve empty quality_gates array in save/load cycle', () => {
// Write config with empty quality_gates array
const configContent = `
piece_overrides:
quality_gates: []
`;
writeFileSync(testConfigPath, configContent, 'utf-8');

// Load config
const manager = GlobalConfigManager.getInstance();
const loaded = manager.load();
expect(loaded.pieceOverrides?.qualityGates).toEqual([]);

// Save config
manager.save(loaded);

// Reset and reload to verify empty array is preserved
GlobalConfigManager.resetInstance();
const reloadedManager = GlobalConfigManager.getInstance();
const reloaded = reloadedManager.load();
expect(reloaded.pieceOverrides?.qualityGates).toEqual([]);
});

it('should preserve empty quality_gates in movements', () => {
const configContent = `
piece_overrides:
movements:
implement:
quality_gates: []
`;
writeFileSync(testConfigPath, configContent, 'utf-8');

const manager = GlobalConfigManager.getInstance();
const loaded = manager.load();
expect(loaded.pieceOverrides?.movements?.implement?.qualityGates).toEqual([]);

manager.save(loaded);

GlobalConfigManager.resetInstance();
const reloadedManager = GlobalConfigManager.getInstance();
const reloaded = reloadedManager.load();
expect(reloaded.pieceOverrides?.movements?.implement?.qualityGates).toEqual([]);
});

it('should distinguish undefined from empty array', () => {
// Test with undefined (not specified)
writeFileSync(testConfigPath, 'piece_overrides: {}\n', 'utf-8');

const manager1 = GlobalConfigManager.getInstance();
const loaded1 = manager1.load();
expect(loaded1.pieceOverrides?.qualityGates).toBeUndefined();

// Test with empty array (explicitly disabled)
GlobalConfigManager.resetInstance();
writeFileSync(testConfigPath, 'piece_overrides:\n quality_gates: []\n', 'utf-8');

const manager2 = GlobalConfigManager.getInstance();
const loaded2 = manager2.load();
expect(loaded2.pieceOverrides?.qualityGates).toEqual([]);
});

it('should preserve non-empty quality_gates array', () => {
const config: PersistedGlobalConfig = {
pieceOverrides: {
qualityGates: ['Test 1', 'Test 2'],
},
};

const manager = GlobalConfigManager.getInstance();
manager.save(config);

GlobalConfigManager.resetInstance();
const reloadedManager = GlobalConfigManager.getInstance();
const reloaded = reloadedManager.load();

expect(reloaded.pieceOverrides?.qualityGates).toEqual(['Test 1', 'Test 2']);
});
});
});
99 changes: 99 additions & 0 deletions src/__tests__/projectConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Project config tests.
*
* Tests project config loading and saving with piece_overrides,
* including empty array round-trip behavior.
*/

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { loadProjectConfig, saveProjectConfig } from '../infra/config/project/projectConfig.js';
import type { ProjectLocalConfig } from '../infra/config/types.js';

describe('projectConfig', () => {
let testDir: string;

beforeEach(() => {
testDir = mkdtempSync(join(tmpdir(), 'takt-test-project-config-'));
mkdirSync(join(testDir, '.takt'), { recursive: true });
});

afterEach(() => {
if (testDir) {
rmSync(testDir, { recursive: true, force: true });
}
});

describe('piece_overrides empty array round-trip', () => {
it('should preserve empty quality_gates array in save/load cycle', () => {
// Write config with empty quality_gates array
const configPath = join(testDir, '.takt', 'config.yaml');
const configContent = `
piece_overrides:
quality_gates: []
`;
writeFileSync(configPath, configContent, 'utf-8');

// Load config
const loaded = loadProjectConfig(testDir);
expect(loaded.pieceOverrides?.qualityGates).toEqual([]);

// Save config
saveProjectConfig(testDir, loaded);

// Reload and verify empty array is preserved
const reloaded = loadProjectConfig(testDir);
expect(reloaded.pieceOverrides?.qualityGates).toEqual([]);
});

it('should preserve empty quality_gates in movements', () => {
const configPath = join(testDir, '.takt', 'config.yaml');
const configContent = `
piece_overrides:
movements:
implement:
quality_gates: []
`;
writeFileSync(configPath, configContent, 'utf-8');

const loaded = loadProjectConfig(testDir);
expect(loaded.pieceOverrides?.movements?.implement?.qualityGates).toEqual([]);

saveProjectConfig(testDir, loaded);

const reloaded = loadProjectConfig(testDir);
expect(reloaded.pieceOverrides?.movements?.implement?.qualityGates).toEqual([]);
});

it('should distinguish undefined from empty array', () => {
// Test with undefined (not specified)
const configPath1 = join(testDir, '.takt', 'config.yaml');
writeFileSync(configPath1, 'piece_overrides: {}\n', 'utf-8');

const loaded1 = loadProjectConfig(testDir);
expect(loaded1.pieceOverrides?.qualityGates).toBeUndefined();

// Test with empty array (explicitly disabled)
const configPath2 = join(testDir, '.takt', 'config.yaml');
writeFileSync(configPath2, 'piece_overrides:\n quality_gates: []\n', 'utf-8');

const loaded2 = loadProjectConfig(testDir);
expect(loaded2.pieceOverrides?.qualityGates).toEqual([]);
});

it('should preserve non-empty quality_gates array', () => {
const config: ProjectLocalConfig = {
pieceOverrides: {
qualityGates: ['Test 1', 'Test 2'],
},
};

saveProjectConfig(testDir, config);
const reloaded = loadProjectConfig(testDir);

expect(reloaded.pieceOverrides?.qualityGates).toEqual(['Test 1', 'Test 2']);
});
});
});
Loading
Loading