kitstore-cli 1.0.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.
Files changed (71) hide show
  1. package/.env.test +4 -0
  2. package/.eslintrc.js +29 -0
  3. package/dist/__tests__/commands/init.test.js +76 -0
  4. package/dist/__tests__/commands/install.test.js +422 -0
  5. package/dist/__tests__/commands/list.test.js +173 -0
  6. package/dist/__tests__/commands/login.test.js +281 -0
  7. package/dist/__tests__/commands/rule-check.test.js +72 -0
  8. package/dist/__tests__/commands/search.test.js +175 -0
  9. package/dist/__tests__/commands/upload.test.js +367 -0
  10. package/dist/__tests__/config.test.js +179 -0
  11. package/dist/__tests__/setup.js +8 -0
  12. package/dist/api/client.js +18 -0
  13. package/dist/api/generated/api.js +912 -0
  14. package/dist/api/generated/base.js +48 -0
  15. package/dist/api/generated/common.js +108 -0
  16. package/dist/api/generated/configuration.js +48 -0
  17. package/dist/api/generated/index.js +31 -0
  18. package/dist/commands/init.js +79 -0
  19. package/dist/commands/install.js +150 -0
  20. package/dist/commands/list.js +70 -0
  21. package/dist/commands/login.js +64 -0
  22. package/dist/commands/rule-check.js +81 -0
  23. package/dist/commands/search.js +59 -0
  24. package/dist/commands/upload.js +138 -0
  25. package/dist/config.js +84 -0
  26. package/dist/index.js +71 -0
  27. package/e2e/install.e2e.test.ts +237 -0
  28. package/e2e/integration.e2e.test.ts +346 -0
  29. package/e2e/login.e2e.test.ts +188 -0
  30. package/jest.config.js +24 -0
  31. package/openapitools.json +7 -0
  32. package/package.json +41 -0
  33. package/src/__tests__/commands/init.test.ts +52 -0
  34. package/src/__tests__/commands/install.test.ts +449 -0
  35. package/src/__tests__/commands/list.test.ts +164 -0
  36. package/src/__tests__/commands/login.test.ts +293 -0
  37. package/src/__tests__/commands/rule-check.test.ts +52 -0
  38. package/src/__tests__/commands/search.test.ts +168 -0
  39. package/src/__tests__/commands/upload.test.ts +404 -0
  40. package/src/__tests__/config.test.ts +181 -0
  41. package/src/__tests__/setup.ts +11 -0
  42. package/src/api/client.ts +20 -0
  43. package/src/api/generated/.openapi-generator/FILES +17 -0
  44. package/src/api/generated/.openapi-generator/VERSION +1 -0
  45. package/src/api/generated/.openapi-generator-ignore +23 -0
  46. package/src/api/generated/api.ts +1171 -0
  47. package/src/api/generated/base.ts +62 -0
  48. package/src/api/generated/common.ts +113 -0
  49. package/src/api/generated/configuration.ts +121 -0
  50. package/src/api/generated/docs/AuthApi.md +158 -0
  51. package/src/api/generated/docs/AuthResponseDto.md +22 -0
  52. package/src/api/generated/docs/AuthUserDto.md +24 -0
  53. package/src/api/generated/docs/HealthApi.md +183 -0
  54. package/src/api/generated/docs/LoginDto.md +22 -0
  55. package/src/api/generated/docs/RegisterDto.md +24 -0
  56. package/src/api/generated/docs/RuleAuthorDto.md +22 -0
  57. package/src/api/generated/docs/RuleResponseDto.md +36 -0
  58. package/src/api/generated/docs/RulesApi.md +289 -0
  59. package/src/api/generated/git_push.sh +57 -0
  60. package/src/api/generated/index.ts +18 -0
  61. package/src/commands/init.ts +46 -0
  62. package/src/commands/install.ts +129 -0
  63. package/src/commands/list.ts +71 -0
  64. package/src/commands/login.ts +65 -0
  65. package/src/commands/rule-check.ts +49 -0
  66. package/src/commands/search.ts +66 -0
  67. package/src/commands/upload.ts +117 -0
  68. package/src/config.ts +66 -0
  69. package/src/index.ts +79 -0
  70. package/test-cli-config.js +118 -0
  71. package/tsconfig.json +24 -0
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const list_1 = require("../../commands/list");
37
+ const config = __importStar(require("../../config"));
38
+ // Mock dependencies
39
+ jest.mock('../../config', () => ({
40
+ getConfig: jest.fn(),
41
+ }));
42
+ jest.mock('../../api/client', () => ({
43
+ createApi: jest.fn(),
44
+ }));
45
+ describe('List Command', () => {
46
+ const mockGetConfig = config.getConfig;
47
+ const mockCreateApi = require('../../api/client').createApi;
48
+ beforeEach(() => {
49
+ jest.clearAllMocks();
50
+ jest.spyOn(console, 'log').mockImplementation();
51
+ jest.spyOn(console, 'error').mockImplementation();
52
+ // Re-establish process.exit mock for each test
53
+ jest.spyOn(process, 'exit').mockImplementation((code) => {
54
+ throw new Error(`Process exit with code ${code}`);
55
+ });
56
+ });
57
+ afterEach(() => {
58
+ jest.restoreAllMocks();
59
+ });
60
+ describe('list success', () => {
61
+ it('should list all rules when no filters provided', async () => {
62
+ mockGetConfig.mockResolvedValue({
63
+ server: 'http://localhost:3000',
64
+ token: 'test-token',
65
+ user: { id: 'user-id', username: 'testuser' },
66
+ lastLogin: new Date().toISOString(),
67
+ });
68
+ const mockRulesApi = {
69
+ rulesControllerFindAll: jest.fn().mockResolvedValue({
70
+ data: [
71
+ {
72
+ id: 'rule-1',
73
+ name: 'Test Rule 1',
74
+ tag: 'test',
75
+ type: 'rule',
76
+ description: 'First test rule',
77
+ version: '1.0.0',
78
+ author: { username: 'user1' },
79
+ createdAt: '2024-01-01T00:00:00.000Z',
80
+ },
81
+ {
82
+ id: 'rule-2',
83
+ name: 'Test Rule 2',
84
+ tag: 'util',
85
+ type: 'command',
86
+ description: 'Second test rule',
87
+ version: '2.0.0',
88
+ author: { username: 'user2' },
89
+ createdAt: '2024-01-02T00:00:00.000Z',
90
+ },
91
+ ],
92
+ }),
93
+ };
94
+ mockCreateApi.mockResolvedValue({ rulesApi: mockRulesApi });
95
+ await (0, list_1.listCommand)({});
96
+ expect(mockCreateApi).toHaveBeenCalledWith({ server: 'http://localhost:3000' });
97
+ expect(mockRulesApi.rulesControllerFindAll).toHaveBeenCalledWith({});
98
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('📋 Found 2 rules/commands:'));
99
+ });
100
+ it('should apply filters when provided', async () => {
101
+ mockGetConfig.mockResolvedValue({
102
+ server: 'http://localhost:3000',
103
+ token: 'test-token',
104
+ user: { id: 'user-id', username: 'testuser' },
105
+ lastLogin: new Date().toISOString(),
106
+ });
107
+ const mockRulesApi = {
108
+ rulesControllerFindAll: jest.fn().mockResolvedValue({
109
+ data: [
110
+ {
111
+ id: 'rule-1',
112
+ name: 'Filtered Rule',
113
+ tag: 'test',
114
+ type: 'rule',
115
+ description: 'Filtered test rule',
116
+ version: '1.0.0',
117
+ author: { username: 'user1' },
118
+ createdAt: '2024-01-01T00:00:00.000Z',
119
+ },
120
+ ],
121
+ }),
122
+ };
123
+ mockCreateApi.mockResolvedValue({ rulesApi: mockRulesApi });
124
+ await (0, list_1.listCommand)({ type: 'rule', tag: 'test', limit: '10' });
125
+ expect(mockRulesApi.rulesControllerFindAll).toHaveBeenCalledWith({
126
+ type: 'rule',
127
+ tag: 'test',
128
+ limit: 10,
129
+ });
130
+ });
131
+ it('should handle empty results', async () => {
132
+ mockGetConfig.mockResolvedValue({
133
+ server: 'http://localhost:3000',
134
+ token: 'test-token',
135
+ user: { id: 'user-id', username: 'testuser' },
136
+ lastLogin: new Date().toISOString(),
137
+ });
138
+ const mockRulesApi = {
139
+ rulesControllerFindAll: jest.fn().mockResolvedValue({
140
+ data: [],
141
+ }),
142
+ };
143
+ mockCreateApi.mockResolvedValue({ rulesApi: mockRulesApi });
144
+ await (0, list_1.listCommand)({});
145
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('📭 No rules or commands found.'));
146
+ });
147
+ });
148
+ describe('list failure', () => {
149
+ it('should exit with error when not logged in', async () => {
150
+ mockGetConfig.mockResolvedValue({
151
+ server: 'http://localhost:3000',
152
+ token: undefined,
153
+ user: undefined,
154
+ });
155
+ await expect((0, list_1.listCommand)({})).rejects.toThrow('Process exit with code 1');
156
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('❌ Not logged in. Please run `kitstore login` first.'));
157
+ });
158
+ it('should handle API errors gracefully', async () => {
159
+ mockGetConfig.mockResolvedValue({
160
+ server: 'http://localhost:3000',
161
+ token: 'test-token',
162
+ user: { id: 'user-id', username: 'testuser' },
163
+ lastLogin: new Date().toISOString(),
164
+ });
165
+ const mockRulesApi = {
166
+ rulesControllerFindAll: jest.fn().mockRejectedValue(new Error('API Error')),
167
+ };
168
+ mockCreateApi.mockResolvedValue({ rulesApi: mockRulesApi });
169
+ await expect((0, list_1.listCommand)({})).rejects.toThrow('Process exit with code 1');
170
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('❌ Failed to list rules:'));
171
+ });
172
+ });
173
+ });
@@ -0,0 +1,281 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const login_1 = require("../../commands/login");
40
+ const config = __importStar(require("../../config"));
41
+ const apiClient = __importStar(require("../../api/client"));
42
+ const axios_1 = __importDefault(require("axios"));
43
+ // Mock dependencies
44
+ jest.mock('../../config', () => ({
45
+ getConfig: jest.fn(),
46
+ saveConfig: jest.fn(),
47
+ }));
48
+ jest.mock('../../api/client', () => ({
49
+ createApi: jest.fn(),
50
+ }));
51
+ jest.mock('axios');
52
+ jest.mock('inquirer', () => ({
53
+ prompt: jest.fn(),
54
+ }));
55
+ const mockInquirer = require('inquirer');
56
+ // TC-UNIT-CLI-001 to TC-UNIT-CLI-002
57
+ describe('Login Command', () => {
58
+ const mockGetConfig = config.getConfig;
59
+ const mockSaveConfig = config.saveConfig;
60
+ const mockCreateApi = apiClient.createApi;
61
+ beforeEach(() => {
62
+ jest.clearAllMocks();
63
+ axios_1.default.isAxiosError.mockImplementation((err) => err?.isAxiosError === true);
64
+ jest.spyOn(console, 'log').mockImplementation();
65
+ jest.spyOn(console, 'error').mockImplementation();
66
+ jest.spyOn(process, 'exit').mockImplementation((code) => {
67
+ throw new Error(`Process exit with code ${code}`);
68
+ });
69
+ });
70
+ afterEach(() => {
71
+ jest.restoreAllMocks();
72
+ });
73
+ // TC-UNIT-CLI-001: Login command (success)
74
+ describe('login success', () => {
75
+ it('should login successfully and save token', async () => {
76
+ const mockAuthApi = {
77
+ authControllerLogin: jest.fn().mockResolvedValue({
78
+ data: {
79
+ token: 'test-token-123',
80
+ user: {
81
+ id: 'user-id',
82
+ username: 'testuser',
83
+ email: 'test@example.com',
84
+ },
85
+ },
86
+ }),
87
+ };
88
+ mockGetConfig.mockResolvedValue({
89
+ server: 'http://localhost:3000',
90
+ token: undefined,
91
+ user: undefined,
92
+ lastLogin: undefined,
93
+ });
94
+ mockCreateApi.mockResolvedValue({
95
+ authApi: mockAuthApi,
96
+ rulesApi: {},
97
+ });
98
+ await (0, login_1.loginCommand)({
99
+ email: 'test@example.com',
100
+ password: 'Password123!',
101
+ });
102
+ expect(mockCreateApi).toHaveBeenCalledWith({ server: 'http://localhost:3000' });
103
+ expect(mockAuthApi.authControllerLogin).toHaveBeenCalledWith({
104
+ email: 'test@example.com',
105
+ password: 'Password123!',
106
+ });
107
+ expect(mockSaveConfig).toHaveBeenCalledWith({
108
+ token: 'test-token-123',
109
+ server: 'http://localhost:3000',
110
+ user: {
111
+ id: 'user-id',
112
+ username: 'testuser',
113
+ email: 'test@example.com',
114
+ },
115
+ lastLogin: expect.any(String),
116
+ });
117
+ });
118
+ });
119
+ // TC-UNIT-CLI-002: Login command (failure)
120
+ describe('login failure', () => {
121
+ it('should handle 401 error', async () => {
122
+ const axiosError = {
123
+ isAxiosError: true,
124
+ response: {
125
+ status: 401,
126
+ data: { error: 'Invalid credentials' },
127
+ },
128
+ message: 'Request failed with status code 401',
129
+ };
130
+ const mockAuthApi = {
131
+ authControllerLogin: jest.fn().mockRejectedValue(axiosError),
132
+ };
133
+ mockGetConfig.mockResolvedValue({
134
+ server: 'http://localhost:3000',
135
+ token: undefined,
136
+ user: undefined,
137
+ lastLogin: undefined,
138
+ });
139
+ mockCreateApi.mockResolvedValue({
140
+ authApi: mockAuthApi,
141
+ rulesApi: {},
142
+ });
143
+ await expect((0, login_1.loginCommand)({
144
+ email: 'test@example.com',
145
+ password: 'WrongPassword',
146
+ })).rejects.toThrow();
147
+ expect(mockSaveConfig).not.toHaveBeenCalled();
148
+ });
149
+ // TC-UNIT-CLI-003: Interactive login prompts
150
+ it('should prompt for email and password when not provided', async () => {
151
+ mockInquirer.prompt
152
+ .mockResolvedValueOnce({ email: 'prompted@example.com' })
153
+ .mockResolvedValueOnce({ password: 'promptedpassword' });
154
+ const mockAuthApi = {
155
+ authControllerLogin: jest.fn().mockResolvedValue({
156
+ data: {
157
+ token: 'test-token-123',
158
+ user: {
159
+ id: 'user-id',
160
+ username: 'testuser',
161
+ email: 'prompted@example.com',
162
+ },
163
+ },
164
+ }),
165
+ };
166
+ mockGetConfig.mockResolvedValue({
167
+ server: 'http://localhost:3000',
168
+ token: undefined,
169
+ user: undefined,
170
+ lastLogin: undefined,
171
+ });
172
+ mockCreateApi.mockResolvedValue({
173
+ authApi: mockAuthApi,
174
+ rulesApi: {},
175
+ });
176
+ await (0, login_1.loginCommand)({});
177
+ expect(mockInquirer.prompt).toHaveBeenCalledTimes(2);
178
+ expect(mockInquirer.prompt).toHaveBeenNthCalledWith(1, [
179
+ { type: 'input', name: 'email', message: 'Email:' }
180
+ ]);
181
+ expect(mockInquirer.prompt).toHaveBeenNthCalledWith(2, [
182
+ { type: 'password', name: 'password', message: 'Password:' }
183
+ ]);
184
+ expect(mockAuthApi.authControllerLogin).toHaveBeenCalledWith({
185
+ email: 'prompted@example.com',
186
+ password: 'promptedpassword',
187
+ });
188
+ });
189
+ // TC-UNIT-CLI-004: Empty credentials after prompts
190
+ it('should exit with error when email/password are empty after prompts', async () => {
191
+ mockInquirer.prompt
192
+ .mockResolvedValueOnce({ email: '' })
193
+ .mockResolvedValueOnce({ password: '' });
194
+ mockGetConfig.mockResolvedValue({
195
+ server: 'http://localhost:3000',
196
+ token: undefined,
197
+ user: undefined,
198
+ lastLogin: undefined,
199
+ });
200
+ await expect((0, login_1.loginCommand)({})).rejects.toThrow('Process exit with code 1');
201
+ expect(console.error).toHaveBeenCalledWith('❌ Login failed: Email and password are required');
202
+ });
203
+ // TC-UNIT-CLI-005: Login response without token
204
+ it('should handle login response without token', async () => {
205
+ const mockAuthApi = {
206
+ authControllerLogin: jest.fn().mockResolvedValue({
207
+ data: {
208
+ user: {
209
+ id: 'user-id',
210
+ username: 'testuser',
211
+ email: 'test@example.com',
212
+ },
213
+ },
214
+ }),
215
+ };
216
+ mockGetConfig.mockResolvedValue({
217
+ server: 'http://localhost:3000',
218
+ token: undefined,
219
+ user: undefined,
220
+ lastLogin: undefined,
221
+ });
222
+ mockCreateApi.mockResolvedValue({
223
+ authApi: mockAuthApi,
224
+ rulesApi: {},
225
+ });
226
+ await expect((0, login_1.loginCommand)({ email: 'test@example.com', password: 'password123' }))
227
+ .rejects.toThrow('Process exit with code 1');
228
+ expect(console.error).toHaveBeenCalledWith('❌ Login failed');
229
+ });
230
+ // TC-UNIT-CLI-006: Other axios errors
231
+ it('should handle other axios errors', async () => {
232
+ const axiosError = {
233
+ isAxiosError: true,
234
+ response: {
235
+ status: 500,
236
+ data: { error: 'Internal server error' },
237
+ },
238
+ message: 'Request failed with status code 500',
239
+ };
240
+ const mockAuthApi = {
241
+ authControllerLogin: jest.fn().mockRejectedValue(axiosError),
242
+ };
243
+ mockGetConfig.mockResolvedValue({
244
+ server: 'http://localhost:3000',
245
+ token: undefined,
246
+ user: undefined,
247
+ lastLogin: undefined,
248
+ });
249
+ mockCreateApi.mockResolvedValue({
250
+ authApi: mockAuthApi,
251
+ rulesApi: {},
252
+ });
253
+ await expect((0, login_1.loginCommand)({
254
+ email: 'test@example.com',
255
+ password: 'Password123!',
256
+ })).rejects.toThrow('Process exit with code 1');
257
+ expect(console.error).toHaveBeenCalledWith('❌ Login failed: Internal server error');
258
+ });
259
+ // TC-UNIT-CLI-007: Non-axios errors
260
+ it('should handle non-axios errors', async () => {
261
+ const mockAuthApi = {
262
+ authControllerLogin: jest.fn().mockRejectedValue(new Error('Network error')),
263
+ };
264
+ mockGetConfig.mockResolvedValue({
265
+ server: 'http://localhost:3000',
266
+ token: undefined,
267
+ user: undefined,
268
+ lastLogin: undefined,
269
+ });
270
+ mockCreateApi.mockResolvedValue({
271
+ authApi: mockAuthApi,
272
+ rulesApi: {},
273
+ });
274
+ await expect((0, login_1.loginCommand)({
275
+ email: 'test@example.com',
276
+ password: 'Password123!',
277
+ })).rejects.toThrow('Process exit with code 1');
278
+ expect(console.error).toHaveBeenCalledWith('❌ Login failed: Network error');
279
+ });
280
+ });
281
+ });
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const rule_check_1 = require("../../commands/rule-check");
37
+ const fs = __importStar(require("fs-extra"));
38
+ jest.mock('fs-extra');
39
+ describe('Rule Check Command', () => {
40
+ beforeEach(() => {
41
+ jest.clearAllMocks();
42
+ jest.spyOn(console, 'log').mockImplementation();
43
+ jest.spyOn(console, 'error').mockImplementation();
44
+ jest.spyOn(console, 'warn').mockImplementation();
45
+ jest.spyOn(process, 'exit').mockImplementation((code) => {
46
+ throw new Error(`Process exit with code ${code}`);
47
+ });
48
+ });
49
+ it('should pass for a valid rule file', async () => {
50
+ fs.pathExists.mockResolvedValue(true);
51
+ fs.readFile.mockResolvedValue('@requirement TC-UNIT-001\nValid rule content');
52
+ await (0, rule_check_1.ruleCheckCommand)('valid-rule.md');
53
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Rule validation passed!'));
54
+ });
55
+ it('should exit if file not found', async () => {
56
+ fs.pathExists.mockResolvedValue(false);
57
+ await expect((0, rule_check_1.ruleCheckCommand)('non-existent.md')).rejects.toThrow('Process exit with code 1');
58
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('File not found'));
59
+ });
60
+ it('should warn about missing @requirement tag', async () => {
61
+ fs.pathExists.mockResolvedValue(true);
62
+ fs.readFile.mockResolvedValue('Rule without requirement tag');
63
+ await (0, rule_check_1.ruleCheckCommand)('no-tag.md');
64
+ expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('Warning: Missing @requirement tag'));
65
+ });
66
+ it('should error if file is empty', async () => {
67
+ fs.pathExists.mockResolvedValue(true);
68
+ fs.readFile.mockResolvedValue('');
69
+ await (0, rule_check_1.ruleCheckCommand)('empty.md');
70
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Error: File is empty'));
71
+ });
72
+ });