cli4ai 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Tests for config.ts
3
+ */
4
+
5
+ import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
6
+ import { mkdtempSync, rmSync, writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { tmpdir } from 'os';
9
+ import {
10
+ ensureLocalDir,
11
+ getGlobalPackages,
12
+ getLocalPackages,
13
+ findPackage,
14
+ LOCAL_DIR,
15
+ LOCAL_PACKAGES_DIR,
16
+ DEFAULT_CONFIG
17
+ } from './config.js';
18
+
19
+ describe('config', () => {
20
+ let tempDir: string;
21
+
22
+ beforeEach(() => {
23
+ tempDir = mkdtempSync(join(tmpdir(), 'cli4ai-config-test-'));
24
+ });
25
+
26
+ afterEach(() => {
27
+ rmSync(tempDir, { recursive: true, force: true });
28
+ });
29
+
30
+ describe('DEFAULT_CONFIG', () => {
31
+ test('has expected defaults', () => {
32
+ expect(DEFAULT_CONFIG.registry).toBe('https://registry.cliforai.com');
33
+ expect(DEFAULT_CONFIG.localRegistries).toEqual([]);
34
+ expect(DEFAULT_CONFIG.defaultRuntime).toBe('bun');
35
+ expect(DEFAULT_CONFIG.mcp.transport).toBe('stdio');
36
+ expect(DEFAULT_CONFIG.telemetry).toBe(false);
37
+ });
38
+ });
39
+
40
+ describe('ensureLocalDir', () => {
41
+ test('creates .cli4ai directory', () => {
42
+ ensureLocalDir(tempDir);
43
+
44
+ expect(existsSync(join(tempDir, LOCAL_DIR))).toBe(true);
45
+ });
46
+
47
+ test('creates packages subdirectory', () => {
48
+ ensureLocalDir(tempDir);
49
+
50
+ expect(existsSync(join(tempDir, LOCAL_PACKAGES_DIR))).toBe(true);
51
+ });
52
+
53
+ test('is idempotent', () => {
54
+ ensureLocalDir(tempDir);
55
+ ensureLocalDir(tempDir);
56
+
57
+ expect(existsSync(join(tempDir, LOCAL_PACKAGES_DIR))).toBe(true);
58
+ });
59
+ });
60
+
61
+ describe('getLocalPackages', () => {
62
+ test('returns empty array when no packages dir', () => {
63
+ const packages = getLocalPackages(tempDir);
64
+ expect(packages).toEqual([]);
65
+ });
66
+
67
+ test('returns empty array when packages dir empty', () => {
68
+ ensureLocalDir(tempDir);
69
+
70
+ const packages = getLocalPackages(tempDir);
71
+ expect(packages).toEqual([]);
72
+ });
73
+
74
+ test('returns installed packages', () => {
75
+ ensureLocalDir(tempDir);
76
+
77
+ // Create a mock package
78
+ const pkgDir = join(tempDir, LOCAL_PACKAGES_DIR, 'test-tool');
79
+ mkdirSync(pkgDir);
80
+ writeFileSync(
81
+ join(pkgDir, 'cli4ai.json'),
82
+ JSON.stringify({
83
+ name: 'test-tool',
84
+ version: '1.0.0',
85
+ entry: 'run.ts'
86
+ })
87
+ );
88
+
89
+ const packages = getLocalPackages(tempDir);
90
+
91
+ expect(packages).toHaveLength(1);
92
+ expect(packages[0].name).toBe('test-tool');
93
+ expect(packages[0].version).toBe('1.0.0');
94
+ });
95
+
96
+ test('ignores directories without manifest', () => {
97
+ ensureLocalDir(tempDir);
98
+
99
+ // Create a directory without cli4ai.json
100
+ const invalidDir = join(tempDir, LOCAL_PACKAGES_DIR, 'invalid');
101
+ mkdirSync(invalidDir);
102
+ writeFileSync(join(invalidDir, 'readme.txt'), 'not a package');
103
+
104
+ const packages = getLocalPackages(tempDir);
105
+ expect(packages).toEqual([]);
106
+ });
107
+
108
+ test('ignores files in packages dir', () => {
109
+ ensureLocalDir(tempDir);
110
+
111
+ // Create a file instead of directory
112
+ writeFileSync(
113
+ join(tempDir, LOCAL_PACKAGES_DIR, 'somefile.txt'),
114
+ 'not a package'
115
+ );
116
+
117
+ const packages = getLocalPackages(tempDir);
118
+ expect(packages).toEqual([]);
119
+ });
120
+
121
+ test('handles invalid JSON gracefully', () => {
122
+ ensureLocalDir(tempDir);
123
+
124
+ const pkgDir = join(tempDir, LOCAL_PACKAGES_DIR, 'bad-json');
125
+ mkdirSync(pkgDir);
126
+ writeFileSync(join(pkgDir, 'cli4ai.json'), 'invalid json');
127
+
128
+ const packages = getLocalPackages(tempDir);
129
+ expect(packages).toEqual([]);
130
+ });
131
+
132
+ test('returns multiple packages', () => {
133
+ ensureLocalDir(tempDir);
134
+
135
+ // Create multiple packages
136
+ for (const name of ['pkg-a', 'pkg-b', 'pkg-c']) {
137
+ const pkgDir = join(tempDir, LOCAL_PACKAGES_DIR, name);
138
+ mkdirSync(pkgDir);
139
+ writeFileSync(
140
+ join(pkgDir, 'cli4ai.json'),
141
+ JSON.stringify({
142
+ name,
143
+ version: '1.0.0',
144
+ entry: 'run.ts'
145
+ })
146
+ );
147
+ }
148
+
149
+ const packages = getLocalPackages(tempDir);
150
+ expect(packages).toHaveLength(3);
151
+ expect(packages.map(p => p.name).sort()).toEqual(['pkg-a', 'pkg-b', 'pkg-c']);
152
+ });
153
+ });
154
+
155
+ describe('findPackage', () => {
156
+ test('finds local package', () => {
157
+ ensureLocalDir(tempDir);
158
+
159
+ const pkgDir = join(tempDir, LOCAL_PACKAGES_DIR, 'local-tool');
160
+ mkdirSync(pkgDir);
161
+ writeFileSync(
162
+ join(pkgDir, 'cli4ai.json'),
163
+ JSON.stringify({
164
+ name: 'local-tool',
165
+ version: '1.0.0',
166
+ entry: 'run.ts'
167
+ })
168
+ );
169
+
170
+ const pkg = findPackage('local-tool', tempDir);
171
+
172
+ expect(pkg).not.toBeNull();
173
+ expect(pkg?.name).toBe('local-tool');
174
+ });
175
+
176
+ test('returns null when package not found', () => {
177
+ const pkg = findPackage('nonexistent', tempDir);
178
+ expect(pkg).toBeNull();
179
+ });
180
+
181
+ test('searches without project dir', () => {
182
+ // Should not throw, just search global
183
+ const pkg = findPackage('test');
184
+ // May or may not find depending on global state
185
+ expect(pkg === null || typeof pkg === 'object').toBe(true);
186
+ });
187
+ });
188
+ });