@simplens/onboard 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.
- package/README.md +214 -0
- package/dist/__tests__/errors.test.d.ts +2 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.js +125 -0
- package/dist/__tests__/errors.test.js.map +1 -0
- package/dist/__tests__/utils.test.d.ts +2 -0
- package/dist/__tests__/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils.test.js +105 -0
- package/dist/__tests__/utils.test.js.map +1 -0
- package/dist/__tests__/validators.test.d.ts +2 -0
- package/dist/__tests__/validators.test.d.ts.map +1 -0
- package/dist/__tests__/validators.test.js +148 -0
- package/dist/__tests__/validators.test.js.map +1 -0
- package/dist/config/constants.d.ts +69 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +79 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/env-config.d.ts +33 -0
- package/dist/env-config.d.ts.map +1 -0
- package/dist/env-config.js +285 -0
- package/dist/env-config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +153 -0
- package/dist/index.js.map +1 -0
- package/dist/infra.d.ts +31 -0
- package/dist/infra.d.ts.map +1 -0
- package/dist/infra.js +267 -0
- package/dist/infra.js.map +1 -0
- package/dist/plugins.d.ts +35 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +164 -0
- package/dist/plugins.js.map +1 -0
- package/dist/services.d.ts +52 -0
- package/dist/services.d.ts.map +1 -0
- package/dist/services.js +158 -0
- package/dist/services.js.map +1 -0
- package/dist/templates.d.ts +3 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +202 -0
- package/dist/templates.js.map +1 -0
- package/dist/types/domain.d.ts +119 -0
- package/dist/types/domain.d.ts.map +1 -0
- package/dist/types/domain.js +5 -0
- package/dist/types/domain.js.map +1 -0
- package/dist/types/errors.d.ts +69 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +129 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +54 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +92 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils.d.ts +32 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +79 -0
- package/dist/utils.js.map +1 -0
- package/dist/validators.d.ts +93 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +180 -0
- package/dist/validators.js.map +1 -0
- package/package.json +45 -0
- package/src/__tests__/errors.test.ts +187 -0
- package/src/__tests__/utils.test.ts +142 -0
- package/src/__tests__/validators.test.ts +195 -0
- package/src/config/constants.ts +86 -0
- package/src/config/index.ts +1 -0
- package/src/env-config.ts +320 -0
- package/src/index.ts +203 -0
- package/src/infra.ts +300 -0
- package/src/plugins.ts +190 -0
- package/src/services.ts +190 -0
- package/src/templates.ts +203 -0
- package/src/types/domain.ts +127 -0
- package/src/types/errors.ts +173 -0
- package/src/types/index.ts +2 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/logger.ts +118 -0
- package/src/utils.ts +105 -0
- package/src/validators.ts +192 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# @simplens/onboard
|
|
2
|
+
|
|
3
|
+
> CLI tool to setup SimpleNS instances on any linux/amd64 and windows machine
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@simplens/onboard` is an interactive CLI tool that guides you through setting up a complete SimpleNS (Simple Notification System) instance on your local machine or server. It handles infrastructure provisioning, environment configuration, plugin installation, and service orchestration.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- ✅ **Prerequisite Validation** - Checks Docker installation and availability
|
|
12
|
+
- 🏗️ **Infrastructure Setup** - Deploy MongoDB, Kafka, Redis, Loki, Grafana with one command
|
|
13
|
+
- 🐧 **OS-Aware Configuration** - Automatically detects and configures for Windows, Linux, or macOS
|
|
14
|
+
- ⚙️ **Smart Environment Config** - Default or interactive mode for environment variables
|
|
15
|
+
- 🔌 **Plugin Management** - Browse and install official SimpleNS plugins
|
|
16
|
+
- 🚀 **Service Orchestration** - Automatic health checks and sequential service startup
|
|
17
|
+
- 📊 **Service Dashboard** - View all running services and their access URLs
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
- **Docker** - Docker Desktop (Windows/Mac) or Docker Engine (Linux)
|
|
22
|
+
- **Node.js** - Version 18 or higher
|
|
23
|
+
- **NPX** - Comes with npm 5.2+
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Run directly with npx (recommended)
|
|
29
|
+
npx @simplens/onboard
|
|
30
|
+
|
|
31
|
+
# Or install globally
|
|
32
|
+
npm install -g @simplens/onboard
|
|
33
|
+
simplens-onboard
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Basic Setup (Application Services Only)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx @simplens/onboard
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This will:
|
|
45
|
+
- Validate prerequisites
|
|
46
|
+
- Generate `docker-compose.yaml` for app services
|
|
47
|
+
- Configure environment variables (default mode)
|
|
48
|
+
- Prompt for plugin selection
|
|
49
|
+
- Optionally start services
|
|
50
|
+
|
|
51
|
+
### Full Setup (Infrastructure + Application)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx @simplens/onboard --infra
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Includes everything from basic setup plus:
|
|
58
|
+
- Interactive infrastructure service selection (MongoDB, Kafka, Redis, etc.)
|
|
59
|
+
- Generate `docker-compose.infra.yaml`
|
|
60
|
+
- OS-specific host configuration (handles Linux compatibility)
|
|
61
|
+
- Auto-configured connection URLs for infra services
|
|
62
|
+
|
|
63
|
+
### Interactive Environment Configuration
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx @simplens/onboard --env interactive
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Prompts for every environment variable instead of using defaults.
|
|
70
|
+
|
|
71
|
+
### Custom Target Directory
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx @simplens/onboard --infra --dir /path/to/setup
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Command Options
|
|
78
|
+
|
|
79
|
+
| Option | Description | Default |
|
|
80
|
+
|--------|-------------|---------|
|
|
81
|
+
| `--infra` | Setup infrastructure services (MongoDB, Kafka, Redis, Loki, Grafana) | `false` |
|
|
82
|
+
| `--env <mode>` | Environment setup mode: `default` or `interactive` | `default` |
|
|
83
|
+
| `--dir <path>` | Target directory for setup files | Current directory |
|
|
84
|
+
|
|
85
|
+
## Workflow
|
|
86
|
+
|
|
87
|
+
1. **Prerequisites Check**
|
|
88
|
+
- Validates Docker installation
|
|
89
|
+
- Checks Docker daemon status
|
|
90
|
+
- Detects operating system
|
|
91
|
+
|
|
92
|
+
2. **Infrastructure Setup** (if `--infra` flag is used)
|
|
93
|
+
- Select infrastructure services
|
|
94
|
+
- Auto-detect host configuration (Linux-aware)
|
|
95
|
+
- Generate `docker-compose.infra.yaml`
|
|
96
|
+
|
|
97
|
+
3. **Environment Configuration**
|
|
98
|
+
- Load defaults from `.env.example`
|
|
99
|
+
- Auto-fill infra connection URLs
|
|
100
|
+
- Prompt for critical values (API keys, passwords)
|
|
101
|
+
- Generate `.env` file
|
|
102
|
+
|
|
103
|
+
4. **Plugin Installation**
|
|
104
|
+
- Fetch official plugins from registry
|
|
105
|
+
- Interactive multi-select
|
|
106
|
+
- Generate `simplens.config.yaml`
|
|
107
|
+
- Extract and prompt for plugin credentials
|
|
108
|
+
- Append credentials to `.env`
|
|
109
|
+
|
|
110
|
+
5. **Service Orchestration**
|
|
111
|
+
- Optionally start infrastructure services
|
|
112
|
+
- Wait for health checks
|
|
113
|
+
- Start application services
|
|
114
|
+
- Display service URLs and status
|
|
115
|
+
|
|
116
|
+
## Generated Files
|
|
117
|
+
|
|
118
|
+
- `docker-compose.infra.yaml` - Infrastructure services (if `--infra` used)
|
|
119
|
+
- `docker-compose.yaml` - Application services
|
|
120
|
+
- `.env` - Environment variables and credentials
|
|
121
|
+
- `simplens.config.yaml` - Plugin configuration
|
|
122
|
+
|
|
123
|
+
## Service URLs
|
|
124
|
+
|
|
125
|
+
After successful setup, access these URLs:
|
|
126
|
+
|
|
127
|
+
- **API Server**: http://localhost:3000
|
|
128
|
+
- **API Health**: http://localhost:3000/health
|
|
129
|
+
- **Dashboard**: http://localhost:3002
|
|
130
|
+
- **Kafka UI**: http://localhost:8080 (if Kafka selected)
|
|
131
|
+
- **Grafana**: http://localhost:3001 (if Grafana selected)
|
|
132
|
+
|
|
133
|
+
## Examples
|
|
134
|
+
|
|
135
|
+
### Minimal Setup
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Basic setup with defaults
|
|
139
|
+
npx @simplens/onboard
|
|
140
|
+
|
|
141
|
+
# Select plugins when prompted
|
|
142
|
+
# Choose "Start services" at the end
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Full Production Setup
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Infrastructure + interactive env config
|
|
149
|
+
npx @simplens/onboard --infra --env interactive
|
|
150
|
+
|
|
151
|
+
# Select all infrastructure services
|
|
152
|
+
# Provide production credentials
|
|
153
|
+
# Start services immediately
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Development Setup in Custom Directory
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Setup in specific directory
|
|
160
|
+
npx @simplens/onboard --infra --dir ~/simplens-dev
|
|
161
|
+
|
|
162
|
+
# Select only MongoDB, Kafka, Redis
|
|
163
|
+
# Use default env values
|
|
164
|
+
# Start services for testing
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Troubleshooting
|
|
168
|
+
|
|
169
|
+
### Docker Not Running
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
❌ Docker daemon is not running.
|
|
173
|
+
Please start Docker Desktop or Docker daemon.
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Solution**: Start Docker Desktop (Windows/Mac) or `sudo systemctl start docker` (Linux)
|
|
177
|
+
|
|
178
|
+
### Plugin Installation Failed
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
❌ Failed to generate plugin configuration
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Solution**:
|
|
185
|
+
- Check internet connection
|
|
186
|
+
- Verify plugin package name
|
|
187
|
+
- Try installing plugins manually with `@simplens/config-gen`
|
|
188
|
+
|
|
189
|
+
## Development
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Clone repository
|
|
193
|
+
cd packages/onboard
|
|
194
|
+
|
|
195
|
+
# Install dependencies
|
|
196
|
+
npm install
|
|
197
|
+
|
|
198
|
+
# Build TypeScript
|
|
199
|
+
npm run build
|
|
200
|
+
|
|
201
|
+
# Test locally
|
|
202
|
+
npm link
|
|
203
|
+
simplens-onboard --help
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
ISC
|
|
209
|
+
|
|
210
|
+
## Support
|
|
211
|
+
|
|
212
|
+
For issues and questions:
|
|
213
|
+
- GitHub Issues: [SimpleNS Core Issues](https://github.com/SimpleNotificationSystem/simplens-core/issues)
|
|
214
|
+
- Documentation: [SimpleNS Docs](https://simplens.vercel.app)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/errors.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { OnboardingError, DockerNotInstalledError, DockerNotRunningError, DockerPermissionError, DockerComposeError, FileSystemError, DirectoryNotWritableError, isOnboardingError, formatErrorForUser, } from '../types/errors.js';
|
|
3
|
+
describe('error types', () => {
|
|
4
|
+
describe('OnboardingError', () => {
|
|
5
|
+
it('should create error with code and message', () => {
|
|
6
|
+
const error = new OnboardingError('TEST_CODE', 'Test message');
|
|
7
|
+
expect(error.code).toBe('TEST_CODE');
|
|
8
|
+
expect(error.message).toBe('Test message');
|
|
9
|
+
expect(error.name).toBe('OnboardingError');
|
|
10
|
+
});
|
|
11
|
+
it('should include troubleshooting when provided', () => {
|
|
12
|
+
const error = new OnboardingError('TEST_CODE', 'Test message', 'Try this fix');
|
|
13
|
+
expect(error.troubleshooting).toBe('Try this fix');
|
|
14
|
+
});
|
|
15
|
+
it('should have proper stack trace', () => {
|
|
16
|
+
const error = new OnboardingError('TEST', 'message');
|
|
17
|
+
expect(error.stack).toBeDefined();
|
|
18
|
+
expect(error.stack).toContain('OnboardingError');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe('DockerNotInstalledError', () => {
|
|
22
|
+
it('should have correct message and troubleshooting', () => {
|
|
23
|
+
const error = new DockerNotInstalledError();
|
|
24
|
+
expect(error.message).toContain('not installed');
|
|
25
|
+
expect(error.troubleshooting).toContain('https://docs.docker.com/get-docker/');
|
|
26
|
+
expect(error.name).toBe('DockerNotInstalledError');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('DockerNotRunningError', () => {
|
|
30
|
+
it('should have correct message and troubleshooting', () => {
|
|
31
|
+
const error = new DockerNotRunningError();
|
|
32
|
+
expect(error.message).toContain('not running');
|
|
33
|
+
expect(error.troubleshooting).toContain('Docker daemon');
|
|
34
|
+
expect(error.name).toBe('DockerNotRunningError');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('DockerPermissionError', () => {
|
|
38
|
+
it('should have correct message and troubleshooting', () => {
|
|
39
|
+
const error = new DockerPermissionError();
|
|
40
|
+
expect(error.message).toContain('Permission denied');
|
|
41
|
+
expect(error.troubleshooting).toContain('usermod');
|
|
42
|
+
expect(error.name).toBe('DockerPermissionError');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('DockerComposeError', () => {
|
|
46
|
+
it('should include operation in message', () => {
|
|
47
|
+
const error = new DockerComposeError('start services');
|
|
48
|
+
expect(error.message).toContain('start services');
|
|
49
|
+
expect(error.name).toBe('DockerComposeError');
|
|
50
|
+
});
|
|
51
|
+
it('should use custom troubleshooting when provided', () => {
|
|
52
|
+
const error = new DockerComposeError('pull images', 'Check network connection');
|
|
53
|
+
expect(error.troubleshooting).toBe('Check network connection');
|
|
54
|
+
});
|
|
55
|
+
it('should have default troubleshooting', () => {
|
|
56
|
+
const error = new DockerComposeError('operation');
|
|
57
|
+
expect(error.troubleshooting).toContain('docker-compose logs');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('FileSystemError', () => {
|
|
61
|
+
it('should include path in error', () => {
|
|
62
|
+
const error = new FileSystemError('Cannot write', '/path/to/file', 'Check permissions');
|
|
63
|
+
expect(error.path).toBe('/path/to/file');
|
|
64
|
+
expect(error.troubleshooting).toBe('Check permissions');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('DirectoryNotWritableError', () => {
|
|
68
|
+
it('should include path in message and error', () => {
|
|
69
|
+
const error = new DirectoryNotWritableError('/opt/app');
|
|
70
|
+
expect(error.message).toContain('/opt/app');
|
|
71
|
+
expect(error.path).toBe('/opt/app');
|
|
72
|
+
expect(error.troubleshooting).toContain('permissions');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe('isOnboardingError', () => {
|
|
76
|
+
it('should return true for OnboardingError instances', () => {
|
|
77
|
+
const error = new OnboardingError('CODE', 'message');
|
|
78
|
+
expect(isOnboardingError(error)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
it('should return true for subclasses of OnboardingError', () => {
|
|
81
|
+
const error = new DockerNotInstalledError();
|
|
82
|
+
expect(isOnboardingError(error)).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
it('should return false for regular Error', () => {
|
|
85
|
+
const error = new Error('regular error');
|
|
86
|
+
expect(isOnboardingError(error)).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
it('should return false for non-error values', () => {
|
|
89
|
+
expect(isOnboardingError('string')).toBe(false);
|
|
90
|
+
expect(isOnboardingError(null)).toBe(false);
|
|
91
|
+
expect(isOnboardingError(undefined)).toBe(false);
|
|
92
|
+
expect(isOnboardingError({})).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('formatErrorForUser', () => {
|
|
96
|
+
it('should format OnboardingError with troubleshooting', () => {
|
|
97
|
+
const error = new OnboardingError('TEST', 'Something went wrong', 'Try this fix');
|
|
98
|
+
const formatted = formatErrorForUser(error);
|
|
99
|
+
expect(formatted).toContain('❌ Something went wrong');
|
|
100
|
+
expect(formatted).toContain('💡 Troubleshooting:');
|
|
101
|
+
expect(formatted).toContain('Try this fix');
|
|
102
|
+
});
|
|
103
|
+
it('should format OnboardingError without troubleshooting', () => {
|
|
104
|
+
const error = new OnboardingError('TEST', 'Something went wrong');
|
|
105
|
+
const formatted = formatErrorForUser(error);
|
|
106
|
+
expect(formatted).toBe('❌ Something went wrong');
|
|
107
|
+
expect(formatted).not.toContain('Troubleshooting');
|
|
108
|
+
});
|
|
109
|
+
it('should format regular Error', () => {
|
|
110
|
+
const error = new Error('Regular error message');
|
|
111
|
+
const formatted = formatErrorForUser(error);
|
|
112
|
+
expect(formatted).toContain('Unexpected error');
|
|
113
|
+
expect(formatted).toContain('Regular error message');
|
|
114
|
+
});
|
|
115
|
+
it('should handle non-Error values', () => {
|
|
116
|
+
const formatted = formatErrorForUser('some string');
|
|
117
|
+
expect(formatted).toBe('❌ An unknown error occurred');
|
|
118
|
+
});
|
|
119
|
+
it('should handle null/undefined', () => {
|
|
120
|
+
expect(formatErrorForUser(null)).toBe('❌ An unknown error occurred');
|
|
121
|
+
expect(formatErrorForUser(undefined)).toBe('❌ An unknown error occurred');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=errors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../src/__tests__/errors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileExists, writeFile, readFile, appendFile, } from '../utils.js';
|
|
6
|
+
describe('utils - file operations', () => {
|
|
7
|
+
let tempDir;
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
// Create a temporary directory for tests
|
|
10
|
+
tempDir = path.join(os.tmpdir(), `onboard-test-${Date.now()}`);
|
|
11
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
12
|
+
});
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
// Clean up temporary directory
|
|
15
|
+
try {
|
|
16
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
// Ignore cleanup errors
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
describe('fileExists', () => {
|
|
23
|
+
it('should return true for existing file', async () => {
|
|
24
|
+
const filePath = path.join(tempDir, 'test.txt');
|
|
25
|
+
await fs.writeFile(filePath, 'content');
|
|
26
|
+
expect(await fileExists(filePath)).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it('should return false for non-existing file', async () => {
|
|
29
|
+
const filePath = path.join(tempDir, 'nonexistent.txt');
|
|
30
|
+
expect(await fileExists(filePath)).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
it('should return true for existing directory', async () => {
|
|
33
|
+
expect(await fileExists(tempDir)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('writeFile', () => {
|
|
37
|
+
it('should write content to file', async () => {
|
|
38
|
+
const filePath = path.join(tempDir, 'write-test.txt');
|
|
39
|
+
const content = 'Hello, World!';
|
|
40
|
+
await writeFile(filePath, content);
|
|
41
|
+
const written = await fs.readFile(filePath, 'utf-8');
|
|
42
|
+
expect(written).toBe(content);
|
|
43
|
+
});
|
|
44
|
+
it('should create parent directories if they do not exist', async () => {
|
|
45
|
+
const filePath = path.join(tempDir, 'nested', 'dir', 'file.txt');
|
|
46
|
+
const content = 'Nested content';
|
|
47
|
+
await writeFile(filePath, content);
|
|
48
|
+
expect(await fileExists(filePath)).toBe(true);
|
|
49
|
+
const written = await fs.readFile(filePath, 'utf-8');
|
|
50
|
+
expect(written).toBe(content);
|
|
51
|
+
});
|
|
52
|
+
it('should overwrite existing file', async () => {
|
|
53
|
+
const filePath = path.join(tempDir, 'overwrite.txt');
|
|
54
|
+
await fs.writeFile(filePath, 'original');
|
|
55
|
+
await writeFile(filePath, 'new content');
|
|
56
|
+
const written = await fs.readFile(filePath, 'utf-8');
|
|
57
|
+
expect(written).toBe('new content');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('readFile', () => {
|
|
61
|
+
it('should read file content', async () => {
|
|
62
|
+
const filePath = path.join(tempDir, 'read-test.txt');
|
|
63
|
+
const content = 'File content to read';
|
|
64
|
+
await fs.writeFile(filePath, content);
|
|
65
|
+
const read = await readFile(filePath);
|
|
66
|
+
expect(read).toBe(content);
|
|
67
|
+
});
|
|
68
|
+
it('should throw error for non-existing file', async () => {
|
|
69
|
+
const filePath = path.join(tempDir, 'nonexistent.txt');
|
|
70
|
+
await expect(readFile(filePath)).rejects.toThrow();
|
|
71
|
+
});
|
|
72
|
+
it('should handle UTF-8 content', async () => {
|
|
73
|
+
const filePath = path.join(tempDir, 'utf8.txt');
|
|
74
|
+
const content = 'Hello 世界 🌍';
|
|
75
|
+
await fs.writeFile(filePath, content);
|
|
76
|
+
const read = await readFile(filePath);
|
|
77
|
+
expect(read).toBe(content);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('appendFile', () => {
|
|
81
|
+
it('should append content to existing file', async () => {
|
|
82
|
+
const filePath = path.join(tempDir, 'append-test.txt');
|
|
83
|
+
await fs.writeFile(filePath, 'Line 1\n');
|
|
84
|
+
await appendFile(filePath, 'Line 2\n');
|
|
85
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
86
|
+
expect(content).toBe('Line 1\nLine 2\n');
|
|
87
|
+
});
|
|
88
|
+
it('should create file if it does not exist', async () => {
|
|
89
|
+
const filePath = path.join(tempDir, 'new-append.txt');
|
|
90
|
+
await appendFile(filePath, 'First line\n');
|
|
91
|
+
expect(await fileExists(filePath)).toBe(true);
|
|
92
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
93
|
+
expect(content).toBe('First line\n');
|
|
94
|
+
});
|
|
95
|
+
it('should handle multiple appends', async () => {
|
|
96
|
+
const filePath = path.join(tempDir, 'multi-append.txt');
|
|
97
|
+
await appendFile(filePath, 'Line 1\n');
|
|
98
|
+
await appendFile(filePath, 'Line 2\n');
|
|
99
|
+
await appendFile(filePath, 'Line 3\n');
|
|
100
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
101
|
+
expect(content).toBe('Line 1\nLine 2\nLine 3\n');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../../src/__tests__/utils.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/validators.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { checkDockerInstalled, checkDockerRunning, detectOS, validateEnvValue } from '../validators.js';
|
|
3
|
+
import { DockerNotInstalledError, DockerNotRunningError, DockerPermissionError } from '../types/errors.js';
|
|
4
|
+
// Mock execa
|
|
5
|
+
vi.mock('execa', () => ({
|
|
6
|
+
execa: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
// Mock utils
|
|
9
|
+
vi.mock('../utils.js', () => ({
|
|
10
|
+
logError: vi.fn(),
|
|
11
|
+
logSuccess: vi.fn(),
|
|
12
|
+
logWarning: vi.fn(),
|
|
13
|
+
}));
|
|
14
|
+
import { execa } from 'execa';
|
|
15
|
+
describe('validators', () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
describe('checkDockerInstalled', () => {
|
|
20
|
+
it('should not throw when docker is installed', async () => {
|
|
21
|
+
vi.mocked(execa).mockResolvedValueOnce({
|
|
22
|
+
stdout: 'Docker version 24.0.0',
|
|
23
|
+
stderr: '',
|
|
24
|
+
});
|
|
25
|
+
await expect(checkDockerInstalled()).resolves.not.toThrow();
|
|
26
|
+
});
|
|
27
|
+
it('should throw DockerNotInstalledError when docker is not installed', async () => {
|
|
28
|
+
vi.mocked(execa).mockRejectedValueOnce(new Error('Command not found'));
|
|
29
|
+
await expect(checkDockerInstalled()).rejects.toThrow(DockerNotInstalledError);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('checkDockerRunning', () => {
|
|
33
|
+
it('should not throw when docker daemon is running', async () => {
|
|
34
|
+
vi.mocked(execa).mockResolvedValueOnce({
|
|
35
|
+
stdout: 'CONTAINER ID IMAGE',
|
|
36
|
+
stderr: '',
|
|
37
|
+
});
|
|
38
|
+
await expect(checkDockerRunning()).resolves.not.toThrow();
|
|
39
|
+
});
|
|
40
|
+
it('should throw DockerPermissionError on permission denied', async () => {
|
|
41
|
+
const error = new Error('permission denied while trying to connect');
|
|
42
|
+
vi.mocked(execa).mockRejectedValueOnce(error);
|
|
43
|
+
await expect(checkDockerRunning()).rejects.toThrow(DockerPermissionError);
|
|
44
|
+
});
|
|
45
|
+
it('should throw DockerNotRunningError when daemon is not running', async () => {
|
|
46
|
+
const error = new Error('Cannot connect to the Docker daemon');
|
|
47
|
+
vi.mocked(execa).mockRejectedValueOnce(error);
|
|
48
|
+
await expect(checkDockerRunning()).rejects.toThrow(DockerNotRunningError);
|
|
49
|
+
});
|
|
50
|
+
it('should throw DockerNotRunningError on generic docker error', async () => {
|
|
51
|
+
const error = new Error('Some other docker error');
|
|
52
|
+
vi.mocked(execa).mockRejectedValueOnce(error);
|
|
53
|
+
await expect(checkDockerRunning()).rejects.toThrow(DockerNotRunningError);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('detectOS', () => {
|
|
57
|
+
const originalPlatform = process.platform;
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
Object.defineProperty(process, 'platform', {
|
|
60
|
+
value: originalPlatform
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
it('should detect Windows', () => {
|
|
64
|
+
Object.defineProperty(process, 'platform', {
|
|
65
|
+
value: 'win32'
|
|
66
|
+
});
|
|
67
|
+
expect(detectOS()).toBe('windows');
|
|
68
|
+
});
|
|
69
|
+
it('should detect macOS', () => {
|
|
70
|
+
Object.defineProperty(process, 'platform', {
|
|
71
|
+
value: 'darwin'
|
|
72
|
+
});
|
|
73
|
+
expect(detectOS()).toBe('darwin');
|
|
74
|
+
});
|
|
75
|
+
it('should detect Linux', () => {
|
|
76
|
+
Object.defineProperty(process, 'platform', {
|
|
77
|
+
value: 'linux'
|
|
78
|
+
});
|
|
79
|
+
expect(detectOS()).toBe('linux');
|
|
80
|
+
});
|
|
81
|
+
it('should default to Linux for unknown platforms', () => {
|
|
82
|
+
Object.defineProperty(process, 'platform', {
|
|
83
|
+
value: 'freebsd'
|
|
84
|
+
});
|
|
85
|
+
expect(detectOS()).toBe('linux');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('validateEnvValue', () => {
|
|
89
|
+
describe('URL validation', () => {
|
|
90
|
+
it('should reject empty MongoDB URI', () => {
|
|
91
|
+
expect(validateEnvValue('MONGO_URI', '')).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
it('should reject MongoDB URI without proper format', () => {
|
|
94
|
+
expect(validateEnvValue('MONGO_URI', 'localhost:27017')).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
it('should accept valid MongoDB URI', () => {
|
|
97
|
+
expect(validateEnvValue('MONGO_URI', 'mongodb://localhost:27017')).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
it('should reject Redis URL without proper format', () => {
|
|
100
|
+
expect(validateEnvValue('REDIS_URL', 'localhost:6379')).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
it('should accept valid Redis URL', () => {
|
|
103
|
+
expect(validateEnvValue('REDIS_URL', 'redis://localhost:6379')).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('Port validation', () => {
|
|
107
|
+
it('should reject negative ports', () => {
|
|
108
|
+
expect(validateEnvValue('PORT', '-1')).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
it('should reject zero port', () => {
|
|
111
|
+
expect(validateEnvValue('PORT', '0')).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
it('should reject ports > 65535', () => {
|
|
114
|
+
expect(validateEnvValue('PORT', '65536')).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
it('should accept valid ports', () => {
|
|
117
|
+
expect(validateEnvValue('PORT', '3000')).toBe(true);
|
|
118
|
+
expect(validateEnvValue('API_PORT', '8080')).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
it('should reject non-numeric ports', () => {
|
|
121
|
+
expect(validateEnvValue('PORT', 'abc')).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe('Security fields validation', () => {
|
|
125
|
+
it('should reject short API keys', () => {
|
|
126
|
+
expect(validateEnvValue('API_KEY', 'short')).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
it('should reject short passwords', () => {
|
|
129
|
+
expect(validateEnvValue('PASSWORD', '1234567')).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
it('should reject short secrets', () => {
|
|
132
|
+
expect(validateEnvValue('AUTH_SECRET', 'abc')).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
it('should accept API keys with 8+ characters', () => {
|
|
135
|
+
expect(validateEnvValue('API_KEY', 'verylongapikey123')).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
it('should accept passwords with 8+ characters', () => {
|
|
138
|
+
expect(validateEnvValue('PASSWORD', 'password123')).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
describe('General validation', () => {
|
|
142
|
+
it('should accept valid non-special values', () => {
|
|
143
|
+
expect(validateEnvValue('SOME_VAR', 'some-value')).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=validators.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.test.js","sourceRoot":"","sources":["../../src/__tests__/validators.test.ts"],"names":[],"mappings":"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"}
|