ntropi 1.0.0-beta
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.
Potentially problematic release.
This version of ntropi might be problematic. Click here for more details.
- package/README.md +298 -0
- package/package.json +22 -0
- package/src/app.js +38 -0
- package/src/libs/backend.js +171 -0
- package/src/libs/cli_parser.js +111 -0
- package/src/libs/config_generator.js +36 -0
- package/src/libs/config_state_management.js +183 -0
- package/src/libs/config_updater.js +56 -0
- package/src/libs/config_validator.js +109 -0
- package/tests/cli_parser.test.js +200 -0
- package/tests/config_genrator.test.js +23 -0
- package/tests/config_state_management.test.js +198 -0
- package/tests/config_updater.test.js +253 -0
- package/tests/config_validator.test.js +401 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { validateConfig } from './config_validator.js';
|
|
3
|
+
import { updateConfig } from './config_updater.js';
|
|
4
|
+
|
|
5
|
+
class ConfigStateManager {
|
|
6
|
+
constructor(configPath = 'ntropi-config.json') {
|
|
7
|
+
this.configPath = configPath;
|
|
8
|
+
this.currentState = null;
|
|
9
|
+
this.lastWorkingState = null;
|
|
10
|
+
this.fileWatcher = null;
|
|
11
|
+
this.subscribers = [];
|
|
12
|
+
this.isInitialized = false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Initialize the state manager and load initial config
|
|
16
|
+
initialize() {
|
|
17
|
+
if (this.isInitialized) {
|
|
18
|
+
console.warn('Config state manager already initialized');
|
|
19
|
+
return { success: true, state: this.currentState };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const result = this.loadConfig();
|
|
23
|
+
if (result.success) {
|
|
24
|
+
this.isInitialized = true;
|
|
25
|
+
this.lastWorkingState = JSON.parse(JSON.stringify(this.currentState));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Load and validate config file
|
|
32
|
+
loadConfig() {
|
|
33
|
+
try {
|
|
34
|
+
if (!fs.existsSync(this.configPath)) {
|
|
35
|
+
console.log('Config file not found, creating default config');
|
|
36
|
+
updateConfig({ config: this.configPath });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const fileContent = fs.readFileSync(this.configPath, 'utf-8');
|
|
40
|
+
const parsedConfig = JSON.parse(fileContent);
|
|
41
|
+
|
|
42
|
+
const validation = validateConfig(parsedConfig);
|
|
43
|
+
if (!validation.valid) {
|
|
44
|
+
console.error('Config validation failed:', validation.error);
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: 'CONFIG_VALIDATION_FAILED',
|
|
48
|
+
message: 'Config validation failed',
|
|
49
|
+
validationError: validation.error
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.currentState = validation.config;
|
|
54
|
+
return { success: true, state: this.currentState };
|
|
55
|
+
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (error instanceof SyntaxError) {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
error: 'CONFIG_PARSE_ERROR',
|
|
61
|
+
message: 'Invalid JSON in config file'
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
error: 'CONFIG_LOAD_ERROR',
|
|
67
|
+
message: error.message
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Enable hot reload with file watching
|
|
73
|
+
enableHotReload() {
|
|
74
|
+
if (!this.isInitialized) {
|
|
75
|
+
throw new Error('Config state manager must be initialized before enabling hot reload');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (this.fileWatcher) {
|
|
79
|
+
console.warn('Hot reload already enabled');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.fileWatcher = fs.watch(this.configPath, (eventType) => {
|
|
84
|
+
if (eventType === 'change') {
|
|
85
|
+
this.handleConfigChange();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
console.log(`Hot reload enabled for ${this.configPath}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Handle config file changes
|
|
93
|
+
handleConfigChange() {
|
|
94
|
+
console.log('Config file change detected, reloading...');
|
|
95
|
+
|
|
96
|
+
const result = this.loadConfig();
|
|
97
|
+
|
|
98
|
+
if (result.success) {
|
|
99
|
+
this.lastWorkingState = JSON.parse(JSON.stringify(this.currentState));
|
|
100
|
+
console.log('Config reloaded successfully');
|
|
101
|
+
this.notifySubscribers('updated', this.currentState);
|
|
102
|
+
} else {
|
|
103
|
+
console.error('Config reload failed, falling back to last working state');
|
|
104
|
+
this.currentState = this.lastWorkingState;
|
|
105
|
+
this.notifySubscribers('fallback', this.lastWorkingState, result);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Subscribe to config changes
|
|
110
|
+
subscribe(callback) {
|
|
111
|
+
if (typeof callback !== 'function') {
|
|
112
|
+
throw new Error('Callback must be a function');
|
|
113
|
+
}
|
|
114
|
+
this.subscribers.push(callback);
|
|
115
|
+
return () => this.unsubscribe(callback);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Unsubscribe from config changes
|
|
119
|
+
unsubscribe(callback) {
|
|
120
|
+
this.subscribers = this.subscribers.filter(cb => cb !== callback);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Notify all subscribers of state changes
|
|
124
|
+
notifySubscribers(event, state, details = null) {
|
|
125
|
+
this.subscribers.forEach(callback => {
|
|
126
|
+
try {
|
|
127
|
+
callback(event, state, details);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error('Error in subscriber callback:', error);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Get current config state as JSON
|
|
135
|
+
getState() {
|
|
136
|
+
if (!this.isInitialized) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return JSON.parse(JSON.stringify(this.currentState));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Get last working config state as JSON
|
|
143
|
+
getLastWorkingState() {
|
|
144
|
+
if (!this.lastWorkingState) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return JSON.parse(JSON.stringify(this.lastWorkingState));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Disable hot reload
|
|
151
|
+
disableHotReload() {
|
|
152
|
+
if (this.fileWatcher) {
|
|
153
|
+
this.fileWatcher.close();
|
|
154
|
+
this.fileWatcher = null;
|
|
155
|
+
console.log('Hot reload disabled');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Cleanup and shutdown
|
|
160
|
+
shutdown() {
|
|
161
|
+
this.disableHotReload();
|
|
162
|
+
this.subscribers = [];
|
|
163
|
+
this.isInitialized = false;
|
|
164
|
+
console.log('Config state manager shutdown complete');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Singleton instance
|
|
169
|
+
let configStateManagerInstance = null;
|
|
170
|
+
|
|
171
|
+
export function getConfigStateManager(configPath = 'ntropi-config.json') {
|
|
172
|
+
if (!configStateManagerInstance) {
|
|
173
|
+
configStateManagerInstance = new ConfigStateManager(configPath);
|
|
174
|
+
}
|
|
175
|
+
return configStateManagerInstance;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function resetConfigStateManager() {
|
|
179
|
+
if (configStateManagerInstance) {
|
|
180
|
+
configStateManagerInstance.shutdown();
|
|
181
|
+
configStateManagerInstance = null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { validateConfig } from "./config_validator.js";
|
|
3
|
+
import { generateConfig } from "./config_generator.js";
|
|
4
|
+
|
|
5
|
+
export function updateConfig(configuration_data) {
|
|
6
|
+
const config_file = configuration_data.config;
|
|
7
|
+
const delay = configuration_data.delay;
|
|
8
|
+
const failureRate = configuration_data.failureRate;
|
|
9
|
+
const apis = configuration_data.apis;
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(config_file)) {
|
|
12
|
+
return generateConfig(delay, failureRate, apis, config_file);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const config_data = fs.readFileSync(config_file, "utf-8");
|
|
17
|
+
const result = validateConfig(JSON.parse(config_data));
|
|
18
|
+
|
|
19
|
+
if (!result.valid) {
|
|
20
|
+
return {
|
|
21
|
+
success: false,
|
|
22
|
+
error: result.error
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let updatedConfig = result.config;
|
|
27
|
+
|
|
28
|
+
// Append new APIs to existing config
|
|
29
|
+
for (let api of apis) {
|
|
30
|
+
// Check if API already exists in config
|
|
31
|
+
const existingEndpoint = updatedConfig.endpoints.find(endpoint => endpoint.path === `/api/${api}`);
|
|
32
|
+
if (!existingEndpoint) {
|
|
33
|
+
updatedConfig.endpoints.push({
|
|
34
|
+
path: `/api/${api}`,
|
|
35
|
+
method: "GET",
|
|
36
|
+
data: [`Response from ${api}`],
|
|
37
|
+
delay: delay,
|
|
38
|
+
failureRate: failureRate
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Write updated config back to file
|
|
44
|
+
fs.writeFileSync(config_file, JSON.stringify(updatedConfig, null, 2), "utf-8");
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
success: true
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
error: "Error reading or parsing config file: " + error.message
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export function validateConfig(config) {
|
|
2
|
+
if (!config || typeof config !== 'object') {
|
|
3
|
+
return { valid: false, error: 'Config must be a valid JSON object' };
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (!config.endpoints) {
|
|
7
|
+
return { valid: false, error: 'Config must have an "endpoints" property' };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!Array.isArray(config.endpoints)) {
|
|
11
|
+
return { valid: false, error: 'Endpoints must be an array' };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (config.endpoints.length === 0) {
|
|
15
|
+
return { valid: false, error: 'Endpoints array cannot be empty' };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const validatedConfig = {
|
|
19
|
+
endpoints: []
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < config.endpoints.length; i++) {
|
|
23
|
+
const endpoint = config.endpoints[i];
|
|
24
|
+
const validationResult = validateEndpoint(endpoint, i);
|
|
25
|
+
|
|
26
|
+
if (!validationResult.valid) {
|
|
27
|
+
return validationResult;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
validatedConfig.endpoints.push(validationResult.endpoint);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
valid: true,
|
|
35
|
+
config: validatedConfig
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function validateEndpoint(endpoint, index) {
|
|
40
|
+
if (!endpoint || typeof endpoint !== 'object') {
|
|
41
|
+
return { valid: false, error: `Endpoint at index ${index} must be a valid object` };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!endpoint.path) {
|
|
45
|
+
return { valid: false, error: `Endpoint at index ${index} is missing required property: path` };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof endpoint.path !== 'string') {
|
|
49
|
+
return { valid: false, error: `Endpoint at index ${index} has invalid path: must be a string` };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!endpoint.path.startsWith('/')) {
|
|
53
|
+
return { valid: false, error: `Endpoint at index ${index} has invalid path: must start with /` };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!endpoint.method) {
|
|
57
|
+
return { valid: false, error: `Endpoint at index ${index} is missing required property: method` };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof endpoint.method !== 'string') {
|
|
61
|
+
return { valid: false, error: `Endpoint at index ${index} has invalid method: must be a string` };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const validMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
|
65
|
+
const normalizedMethod = endpoint.method.toUpperCase();
|
|
66
|
+
|
|
67
|
+
if (!validMethods.includes(normalizedMethod)) {
|
|
68
|
+
return { valid: false, error: `Endpoint at index ${index} has invalid method: ${endpoint.method}` };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!endpoint.data) {
|
|
72
|
+
return { valid: false, error: `Endpoint at index ${index} is missing required property: data` };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!Array.isArray(endpoint.data)) {
|
|
76
|
+
return { valid: false, error: `Endpoint at index ${index} has invalid data: must be an array` };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (endpoint.data.length === 0) {
|
|
80
|
+
return { valid: false, error: `Endpoint at index ${index} has invalid data: array cannot be empty` };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const validatedEndpoint = {
|
|
84
|
+
path: endpoint.path,
|
|
85
|
+
method: normalizedMethod,
|
|
86
|
+
data: endpoint.data,
|
|
87
|
+
delay: 0,
|
|
88
|
+
failureRate: 0
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (endpoint.delay !== undefined) {
|
|
92
|
+
if (typeof endpoint.delay !== 'number' || endpoint.delay < 0) {
|
|
93
|
+
return { valid: false, error: `Endpoint at index ${index} has invalid delay: delay must be a non-negative number` };
|
|
94
|
+
}
|
|
95
|
+
validatedEndpoint.delay = endpoint.delay;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (endpoint.failureRate !== undefined) {
|
|
99
|
+
if (typeof endpoint.failureRate !== 'number' || endpoint.failureRate < 0 || endpoint.failureRate > 100) {
|
|
100
|
+
return { valid: false, error: `Endpoint at index ${index} has invalid failureRate: must be between 0 and 100` };
|
|
101
|
+
}
|
|
102
|
+
validatedEndpoint.failureRate = endpoint.failureRate;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
valid: true,
|
|
107
|
+
endpoint: validatedEndpoint
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parseArgs } from '../src/libs/cli_parser.js';
|
|
3
|
+
|
|
4
|
+
describe('CLI Parser', () => {
|
|
5
|
+
// No args
|
|
6
|
+
it('Should return default config for no arguments', () => {
|
|
7
|
+
const args = [];
|
|
8
|
+
const result = parseArgs(args);
|
|
9
|
+
expect(result).toEqual({
|
|
10
|
+
delay: 0,
|
|
11
|
+
failureRate: 0,
|
|
12
|
+
config: 'ntropi-config.json',
|
|
13
|
+
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Delay only
|
|
18
|
+
it('Should return delay when --delay is passed', () => {
|
|
19
|
+
const args = ['--delay', '5'];
|
|
20
|
+
const result = parseArgs(args);
|
|
21
|
+
expect(result).toEqual({
|
|
22
|
+
delay: 5,
|
|
23
|
+
failureRate: 0,
|
|
24
|
+
config: 'ntropi-config.json',
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Error if delay is negative
|
|
30
|
+
it('Should return error for negative delay', () => {
|
|
31
|
+
const args = ['--delay', '-3'];
|
|
32
|
+
const result = parseArgs(args);
|
|
33
|
+
expect(result).toEqual({ error: 'Delay must be a non-negative integer' });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Error if delay is not a number
|
|
37
|
+
it('Should return error for non-numeric delay', () => {
|
|
38
|
+
const args = ['--delay', 'abc'];
|
|
39
|
+
const result = parseArgs(args);
|
|
40
|
+
expect(result).toEqual({ error: 'Delay must be a non-negative integer' });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Failure Rate
|
|
44
|
+
it('Should return failure rate when --failure-rate is passed', () => {
|
|
45
|
+
const args = ['--failure-rate', '10'];
|
|
46
|
+
const result = parseArgs(args);
|
|
47
|
+
expect(result).toEqual({
|
|
48
|
+
delay: 0,
|
|
49
|
+
failureRate: 10,
|
|
50
|
+
config: "ntropi-config.json",
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Error if failure rate is less than 0 or greater than 1
|
|
55
|
+
it('Should return error for invalid failure rate', () => {
|
|
56
|
+
const args = ['--failure-rate', '150'];
|
|
57
|
+
const result = parseArgs(args);
|
|
58
|
+
expect(result).toEqual({ error: 'Failure rate must be between 0 and 100' });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Error if failure rate is not a number
|
|
62
|
+
it('Should return error for non-numeric failure rate', () => {
|
|
63
|
+
const args = ['--failure-rate', 'xyz'];
|
|
64
|
+
const result = parseArgs(args);
|
|
65
|
+
expect(result).toEqual({ error: 'Failure rate must be between 0 and 100' });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Both delay and failure rate
|
|
69
|
+
it('Should return both delay and failure rate when both are passed', () => {
|
|
70
|
+
const args = ['--delay', '5', '--failure-rate', '10'];
|
|
71
|
+
const result = parseArgs(args);
|
|
72
|
+
expect(result).toEqual({
|
|
73
|
+
delay: 5,
|
|
74
|
+
failureRate: 10,
|
|
75
|
+
config: "ntropi-config.json",
|
|
76
|
+
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Config File only
|
|
81
|
+
it('Should return config file when --config is passed', () => {
|
|
82
|
+
const args = ['--config', 'config.json'];
|
|
83
|
+
const result = parseArgs(args);
|
|
84
|
+
expect(result).toEqual({
|
|
85
|
+
config: 'config.json',
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Config and Delay
|
|
90
|
+
it('Should return config and delay when both --config and --delay are passed', () => {
|
|
91
|
+
const args = ['--config', 'config.json', '--delay', '5'];
|
|
92
|
+
const result = parseArgs(args);
|
|
93
|
+
expect(result).toEqual({ config: 'config.json', delay: 5});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Config and Failure Rate
|
|
97
|
+
it('Should return config and failure rate when both --config and --failure-rate are passed', () => {
|
|
98
|
+
const args = ['--config', 'config.json', '--failure-rate', '10'];
|
|
99
|
+
const result = parseArgs(args);
|
|
100
|
+
expect(result).toEqual({ config: 'config.json', failureRate: 10 });
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// API only
|
|
104
|
+
it('Should return all APIS after --api flag', () => {
|
|
105
|
+
const args = ['--api', 'users', 'posts', 'comments'];
|
|
106
|
+
const result = parseArgs(args);
|
|
107
|
+
expect(result).toEqual({ config: 'ntropi-config.json', failureRate: 0, delay: 0, apis: ['users', 'posts', 'comments'], });
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
// All three
|
|
112
|
+
it('Should return config, delay, and failure rate when all are passed', () => {
|
|
113
|
+
const args = ['--config', 'config.json', '--delay', '5', '--failure-rate', '10'];
|
|
114
|
+
const result = parseArgs(args);
|
|
115
|
+
expect(result).toEqual({ config: 'config.json', delay: 5, failureRate: 10 });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Help flag
|
|
119
|
+
it('Should return help flag when --help is passed', () => {
|
|
120
|
+
const args = ['--help'];
|
|
121
|
+
const result = parseArgs(args);
|
|
122
|
+
expect(result).toEqual({ help: true });
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Bad Arguments
|
|
126
|
+
it("Should throw error for unknown arguments", () => {
|
|
127
|
+
const args = ['--unknown'];
|
|
128
|
+
const result = parseArgs(args);
|
|
129
|
+
expect(result).toEqual({
|
|
130
|
+
error: `Unknown argument: ${args}`
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Shorthand tests
|
|
135
|
+
it('Should return delay when -d shorthand is passed', () => {
|
|
136
|
+
const args = ['-d', '5'];
|
|
137
|
+
const result = parseArgs(args);
|
|
138
|
+
expect(result).toEqual({
|
|
139
|
+
delay: 5,
|
|
140
|
+
failureRate: 0,
|
|
141
|
+
config: 'ntropi-config.json',
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('Should return failure rate when -f shorthand is passed', () => {
|
|
146
|
+
const args = ['-f', '10'];
|
|
147
|
+
const result = parseArgs(args);
|
|
148
|
+
expect(result).toEqual({
|
|
149
|
+
delay: 0,
|
|
150
|
+
failureRate: 10,
|
|
151
|
+
config: "ntropi-config.json",
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('Should return config file when -c shorthand is passed', () => {
|
|
156
|
+
const args = ['-c', 'config.json'];
|
|
157
|
+
const result = parseArgs(args);
|
|
158
|
+
expect(result).toEqual({
|
|
159
|
+
config: 'config.json',
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('Should return all APIs after -a shorthand flag', () => {
|
|
164
|
+
const args = ['-a', 'users', 'posts', 'comments'];
|
|
165
|
+
const result = parseArgs(args);
|
|
166
|
+
expect(result).toEqual({ config: 'ntropi-config.json', failureRate: 0, delay: 0, apis: ['users', 'posts', 'comments'], });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('Should work with mixed shorthand and full arguments', () => {
|
|
170
|
+
const args = ['-c', 'config.json', '--delay', '5', '-f', '10'];
|
|
171
|
+
const result = parseArgs(args);
|
|
172
|
+
expect(result).toEqual({ config: 'config.json', delay: 5, failureRate: 10 });
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('Should return all arguments when all shorthands are passed', () => {
|
|
176
|
+
const args = ['-c', 'config.json', '-d', '5', '-f', '10'];
|
|
177
|
+
const result = parseArgs(args);
|
|
178
|
+
expect(result).toEqual({ config: 'config.json', delay: 5, failureRate: 10 });
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Run argument only
|
|
182
|
+
it("should return run and default config file", () => {
|
|
183
|
+
const args = ['run'];
|
|
184
|
+
const result = parseArgs(args);
|
|
185
|
+
expect(result).toEqual({run: true, config: 'ntropi-config.json'});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Run argument with config file
|
|
189
|
+
it("should return run and user config file", () => {
|
|
190
|
+
const args = ['run', '--config', 'config.json'];
|
|
191
|
+
const result = parseArgs(args);
|
|
192
|
+
expect(result).toEqual({run: true, config: 'config.json'});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Run argument with config shorthand
|
|
196
|
+
it("should return run and use shorthand config file", () => {
|
|
197
|
+
const args = ['run', '-c', 'config.json'];
|
|
198
|
+
const result = parseArgs(args);
|
|
199
|
+
expect(result).toEqual({run: true, config: 'config.json'});
|
|
200
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { generateConfig } from "../src/libs/config_generator";
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
|
|
5
|
+
describe('Config Generator', () => {
|
|
6
|
+
it('should generate config with default values', () => {
|
|
7
|
+
const result = generateConfig();
|
|
8
|
+
expect(result).toEqual({ success: true });
|
|
9
|
+
fs.unlinkSync('ntropi-config.json');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should generate config with specified delay and failure rate', () => {
|
|
13
|
+
const result = generateConfig(1000, 10);
|
|
14
|
+
expect(result).toEqual({ success: true });
|
|
15
|
+
fs.unlinkSync('ntropi-config.json');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should generate config with specified APIs', () => {
|
|
19
|
+
const result = generateConfig(0, 0, ['users', 'orders']);
|
|
20
|
+
expect(result).toEqual({ success: true });
|
|
21
|
+
fs.unlinkSync('ntropi-config.json');
|
|
22
|
+
});
|
|
23
|
+
});
|