@sembix/cli 0.1.0 → 1.0.2

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 (44) hide show
  1. package/README.md +135 -18
  2. package/dist/__tests__/config-schema.test.d.ts +2 -0
  3. package/dist/__tests__/config-schema.test.d.ts.map +1 -0
  4. package/dist/__tests__/config-schema.test.js +471 -0
  5. package/dist/__tests__/config-schema.test.js.map +1 -0
  6. package/dist/__tests__/integration/fixtures/configs.d.ts +477 -0
  7. package/dist/__tests__/integration/fixtures/configs.d.ts.map +1 -0
  8. package/dist/__tests__/integration/fixtures/configs.js +175 -0
  9. package/dist/__tests__/integration/fixtures/configs.js.map +1 -0
  10. package/dist/__tests__/integration/helpers/cli-runner.d.ts +63 -0
  11. package/dist/__tests__/integration/helpers/cli-runner.d.ts.map +1 -0
  12. package/dist/__tests__/integration/helpers/cli-runner.js +152 -0
  13. package/dist/__tests__/integration/helpers/cli-runner.js.map +1 -0
  14. package/dist/__tests__/integration/helpers/command-runner.d.ts +53 -0
  15. package/dist/__tests__/integration/helpers/command-runner.d.ts.map +1 -0
  16. package/dist/__tests__/integration/helpers/command-runner.js +117 -0
  17. package/dist/__tests__/integration/helpers/command-runner.js.map +1 -0
  18. package/dist/__tests__/integration/studio-create.test.d.ts +2 -0
  19. package/dist/__tests__/integration/studio-create.test.d.ts.map +1 -0
  20. package/dist/__tests__/integration/studio-create.test.js +209 -0
  21. package/dist/__tests__/integration/studio-create.test.js.map +1 -0
  22. package/dist/__tests__/integration/studio-update.test.d.ts +2 -0
  23. package/dist/__tests__/integration/studio-update.test.d.ts.map +1 -0
  24. package/dist/__tests__/integration/studio-update.test.js +166 -0
  25. package/dist/__tests__/integration/studio-update.test.js.map +1 -0
  26. package/dist/index.js +0 -0
  27. package/dist/prompts/__tests__/prompt-helpers.test.d.ts +2 -0
  28. package/dist/prompts/__tests__/prompt-helpers.test.d.ts.map +1 -0
  29. package/dist/prompts/__tests__/prompt-helpers.test.js +235 -0
  30. package/dist/prompts/__tests__/prompt-helpers.test.js.map +1 -0
  31. package/dist/sembix-cli-1.0.2.tgz +0 -0
  32. package/dist/utils/__tests__/config-loader.test.d.ts +2 -0
  33. package/dist/utils/__tests__/config-loader.test.d.ts.map +1 -0
  34. package/dist/utils/__tests__/config-loader.test.js +325 -0
  35. package/dist/utils/__tests__/config-loader.test.js.map +1 -0
  36. package/dist/utils/__tests__/github.test.d.ts +2 -0
  37. package/dist/utils/__tests__/github.test.d.ts.map +1 -0
  38. package/dist/utils/__tests__/github.test.js +282 -0
  39. package/dist/utils/__tests__/github.test.js.map +1 -0
  40. package/dist/utils/__tests__/ui.test.d.ts +2 -0
  41. package/dist/utils/__tests__/ui.test.d.ts.map +1 -0
  42. package/dist/utils/__tests__/ui.test.js +256 -0
  43. package/dist/utils/__tests__/ui.test.js.map +1 -0
  44. package/package.json +20 -9
package/README.md CHANGED
@@ -22,6 +22,42 @@ A terminal-based CLI tool for managing Sembix products. An intuitive command-lin
22
22
  - `workflow` - Update GitHub Action workflows
23
23
  - **Deployment Repository** - A GitHub repository set up for Sembix Studio deployments
24
24
 
25
+ ## Deployment Process
26
+
27
+ Sembix Studio uses a **two-phased deployment** process to establish secure cross-account access with the Sembix Hub:
28
+
29
+ ### Phase 1: Bootstrap & Initial Deployment
30
+
31
+ 1. **Create environment** - Run `sembix studio create` to configure your environment (Steps 1-8)
32
+ - Skip Hub integration initially (use `--skip-hub` or skip Step 9)
33
+ - Creates GitHub Actions environment with AWS configuration
34
+
35
+ 2. **Bootstrap deployment** - Run the GitHub Actions workflow
36
+ - Deploys initial Terraform infrastructure
37
+ - Creates IAM roles and resources in your AWS account
38
+ - Produces IAM ARNs needed for Hub integration
39
+
40
+ 3. **Coordinate with Sembix Support** - Send the IAM ARNs to Sembix support
41
+ - Sembix configures the Hub to trust your account
42
+ - Sembix provides Hub role ARNs back to you:
43
+ - Hub Engine Execution Role ARN
44
+ - Hub Consumer Role ARN
45
+ - Hub Admin Role ARN
46
+ - Hub AppConfig Role ARN (plus Application ID, Environment ID, Profile ID)
47
+
48
+ ### Phase 2: Hub Integration & Full Deployment
49
+
50
+ 4. **Add Hub integration** - Run `sembix studio add-hub` with ARNs from Sembix support
51
+ - Updates GitHub Actions environment with Hub role ARNs
52
+ - Configures cross-account access to Sembix Hub
53
+
54
+ 5. **Final deployment** - Run the GitHub Actions workflow again
55
+ - Completes full Sembix Studio deployment
56
+ - Enables Hub cross-account access
57
+ - Your Studio environment is now fully operational
58
+
59
+ **Important:** Do not skip Phase 1. The Hub integration requires IAM resources created during the bootstrap deployment.
60
+
25
61
  ## Installation
26
62
 
27
63
  ### From npm (Recommended)
@@ -106,8 +142,25 @@ See [`config.example.yaml`](./config.example.yaml) for a complete configuration
106
142
 
107
143
  ### Quick Start
108
144
 
145
+ For a complete deployment, follow the **two-phased process** (see [Deployment Process](#deployment-process) section above):
146
+
147
+ ```bash
148
+ # PHASE 1: Bootstrap deployment
149
+ sembix studio create --skip-hub
150
+ # → Deploy via GitHub Actions
151
+ # → Send IAM ARNs to Sembix support
152
+ # → Receive Hub role ARNs from support
153
+
154
+ # PHASE 2: Hub integration and full deployment
155
+ sembix studio add-hub <environment>
156
+ # → Deploy via GitHub Actions again
157
+ # → Studio environment fully operational
158
+ ```
159
+
160
+ **Alternative workflows:**
161
+
109
162
  ```bash
110
- # Fully interactive (beginner-friendly)
163
+ # Fully interactive (prompts for all configuration)
111
164
  sembix studio create
112
165
 
113
166
  # Fast setup with config file
@@ -337,41 +390,105 @@ sembix studio list acme-corp/sembix-deployment
337
390
  sembix studio list acme/deploy --token ghp_custom_token
338
391
  ```
339
392
 
340
- ### `sembix studio add-hub` [DEPRECATED]
393
+ ### `sembix studio add-hub`
341
394
 
342
- **⚠️ This command is deprecated.** Use `sembix studio update` instead.
395
+ Add Sembix Hub integration to an existing Studio environment. **This is typically used in Phase 2 of the deployment process** after receiving Hub role ARNs from Sembix support.
343
396
 
344
- Hub integration is now part of the main `create` workflow (Step 9) and can be added/updated using the `update` command.
397
+ **Syntax:**
398
+ ```bash
399
+ sembix studio add-hub [environment] [options]
400
+ ```
401
+
402
+ **Options:**
403
+ - `-r, --repo <repo>` - Target repository (owner/repo format)
404
+ - `-e, --env <environment>` - Environment name (alternative to positional argument)
405
+ - `--hub-engine-execution-role <arn>` - Hub Engine Execution Role ARN (from Sembix support)
406
+ - `--hub-consumer-role <arn>` - Hub Consumer Role ARN (from Sembix support)
407
+ - `--hub-admin-role <arn>` - Hub Admin Role ARN (from Sembix support)
408
+ - `--hub-appconfig-role <arn>` - Hub AppConfig Role ARN (from Sembix support)
409
+ - `--hub-appconfig-app-id <id>` - Hub AppConfig Application ID (from Sembix support)
410
+ - `--hub-appconfig-env-id <id>` - Hub AppConfig Environment ID (from Sembix support)
411
+ - `--hub-appconfig-profile-id <id>` - Hub AppConfig Profile ID (from Sembix support)
412
+
413
+ **Example:**
414
+ ```bash
415
+ # Interactive mode (prompts for ARNs)
416
+ sembix studio add-hub production --repo acme/deploy
417
+
418
+ # With CLI flags (using ARNs from Sembix support)
419
+ sembix studio add-hub production \
420
+ --repo acme/deploy \
421
+ --hub-engine-execution-role arn:aws:iam::999888777666:role/HubEngineRole \
422
+ --hub-consumer-role arn:aws:iam::999888777666:role/HubConsumerRole \
423
+ --hub-admin-role arn:aws:iam::999888777666:role/HubAdminRole \
424
+ --hub-appconfig-role arn:aws:iam::999888777666:role/HubAppConfigRole \
425
+ --hub-appconfig-app-id abc123 \
426
+ --hub-appconfig-env-id def456 \
427
+ --hub-appconfig-profile-id ghi789
428
+ ```
429
+
430
+ **When to use this command:**
431
+
432
+ - **Phase 2 deployment** (recommended): After completing Phase 1 bootstrap deployment and receiving Hub ARNs from Sembix support
433
+ - **Alternative**: Hub integration can also be added during initial environment creation (Step 9 of `create`) if you already have the Hub ARNs
434
+ - **Updates**: Use the `update` command to modify existing Hub configuration
435
+
436
+ **Important:** The Hub role ARNs are provided by Sembix support after they configure the Hub to trust your AWS account. Do not use this command until you have received these ARNs.
345
437
 
346
438
  ## Workflow
347
439
 
348
- ### Complete Setup Process
440
+ ### Two-Phased Deployment Process
349
441
 
350
442
  ```
443
+ ╔═══════════════════════════════════════════════════════════════════╗
444
+ ║ PHASE 1: BOOTSTRAP DEPLOYMENT ║
445
+ ╚═══════════════════════════════════════════════════════════════════╝
446
+
351
447
  ┌─────────────────────────────────────────────────┐
352
- │ 1. Run: sembix studio create
448
+ │ 1. Create Environment (Skip Hub)
449
+ │ sembix studio create --skip-hub │
353
450
  │ • Configure environment (Steps 1-8) │
354
- │ • Optionally configure Hub (Step 9)
355
- │ • Creates GitHub environment + secrets
451
+ │ • Skip Hub integration (Step 9)
452
+ │ • Creates GitHub Actions environment
356
453
  └─────────────────────────────────────────────────┘
357
454
 
358
455
  ┌─────────────────────────────────────────────────┐
359
- │ 2. Deploy via GitHub Actions
360
- │ • Go to repo → Actions
361
- │ • Run deployment workflow
362
- │ • Copy Hub ARNs from output (if needed)
456
+ │ 2. Bootstrap Deployment via GitHub Actions
457
+ │ • Go to repo → Actions → Run workflow
458
+ │ • Deploys initial Terraform infrastructure
459
+ │ • Creates IAM roles in your AWS account
460
+ │ • Copy IAM ARNs from deployment output │
363
461
  └─────────────────────────────────────────────────┘
364
462
 
365
463
  ┌─────────────────────────────────────────────────┐
366
- │ 3. Run: sembix studio update (if Hub skipped)
367
- │ • Add Hub role ARNs
368
- │ • Add AppConfig configuration
464
+ │ 3. Coordinate with Sembix Support
465
+ │ • Send IAM ARNs to Sembix support
466
+ │ • Wait for Sembix to configure Hub
467
+ │ • Receive Hub role ARNs from support: │
468
+ │ - Hub Engine Execution Role ARN │
469
+ │ - Hub Consumer Role ARN │
470
+ │ - Hub Admin Role ARN │
471
+ │ - Hub AppConfig Role ARN + IDs │
472
+ └─────────────────────────────────────────────────┘
473
+
474
+ ╔═══════════════════════════════════════════════════════════════════╗
475
+ ║ PHASE 2: HUB INTEGRATION & FULL DEPLOYMENT ║
476
+ ╚═══════════════════════════════════════════════════════════════════╝
477
+
478
+ ┌─────────────────────────────────────────────────┐
479
+ │ 4. Add Hub Integration │
480
+ │ sembix studio add-hub <environment> │
481
+ │ • Provide Hub role ARNs from support │
482
+ │ • Updates GitHub Actions environment │
483
+ │ • Configures cross-account access │
369
484
  └─────────────────────────────────────────────────┘
370
485
 
371
486
  ┌─────────────────────────────────────────────────┐
372
- 4. Deploy Again (with Hub integration)
373
- │ • Full Sembix Studio deployment
374
- │ • Cross-account access enabled
487
+ 5. Full Deployment via GitHub Actions
488
+ │ • Go to repo → Actions → Run workflow
489
+ │ • Completes full Sembix Studio deployment
490
+ │ • Enables Hub cross-account access │
491
+ │ • ✅ Studio environment fully operational │
375
492
  └─────────────────────────────────────────────────┘
376
493
  ```
377
494
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config-schema.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-schema.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-schema.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,471 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { configFileSchema } from '../config-schema.js';
3
+ describe('config-schema', () => {
4
+ describe('configFileSchema - basic fields', () => {
5
+ it('should accept valid environment name', () => {
6
+ const config = { environmentName: 'production' };
7
+ expect(configFileSchema.safeParse(config).success).toBe(true);
8
+ });
9
+ it('should accept environment name with numbers and hyphens', () => {
10
+ const config = { environmentName: 'prod-env-123' };
11
+ expect(configFileSchema.safeParse(config).success).toBe(true);
12
+ });
13
+ it('should reject environment name with uppercase', () => {
14
+ const config = { environmentName: 'Production' };
15
+ expect(configFileSchema.safeParse(config).success).toBe(false);
16
+ });
17
+ it('should reject environment name with underscores', () => {
18
+ const config = { environmentName: 'prod_env' };
19
+ expect(configFileSchema.safeParse(config).success).toBe(false);
20
+ });
21
+ it('should reject environment name shorter than 3 characters', () => {
22
+ const config = { environmentName: 'ab' };
23
+ expect(configFileSchema.safeParse(config).success).toBe(false);
24
+ });
25
+ it('should accept valid AWS account ID', () => {
26
+ const config = { awsAccountId: '123456789012' };
27
+ expect(configFileSchema.safeParse(config).success).toBe(true);
28
+ });
29
+ it('should reject AWS account ID with less than 12 digits', () => {
30
+ const config = { awsAccountId: '12345678901' };
31
+ expect(configFileSchema.safeParse(config).success).toBe(false);
32
+ });
33
+ it('should reject AWS account ID with more than 12 digits', () => {
34
+ const config = { awsAccountId: '1234567890123' };
35
+ expect(configFileSchema.safeParse(config).success).toBe(false);
36
+ });
37
+ it('should reject AWS account ID with letters', () => {
38
+ const config = { awsAccountId: '12345678901a' };
39
+ expect(configFileSchema.safeParse(config).success).toBe(false);
40
+ });
41
+ it('should accept valid AWS region', () => {
42
+ const config = { awsRegion: 'us-east-1' };
43
+ expect(configFileSchema.safeParse(config).success).toBe(true);
44
+ });
45
+ it('should reject invalid AWS region', () => {
46
+ const config = { awsRegion: 'invalid-region' };
47
+ expect(configFileSchema.safeParse(config).success).toBe(false);
48
+ });
49
+ });
50
+ describe('configFileSchema - IAM role ARNs', () => {
51
+ it('should accept valid IAM role ARN', () => {
52
+ const config = {
53
+ customerRoleArn: 'arn:aws:iam::123456789012:role/MyRole',
54
+ };
55
+ expect(configFileSchema.safeParse(config).success).toBe(true);
56
+ });
57
+ it('should accept IAM role ARN with path', () => {
58
+ const config = {
59
+ customerRoleArn: 'arn:aws:iam::123456789012:role/path/to/MyRole',
60
+ };
61
+ expect(configFileSchema.safeParse(config).success).toBe(true);
62
+ });
63
+ it('should reject invalid IAM role ARN format', () => {
64
+ const config = {
65
+ customerRoleArn: 'invalid-arn',
66
+ };
67
+ expect(configFileSchema.safeParse(config).success).toBe(false);
68
+ });
69
+ it('should reject IAM role ARN with wrong service', () => {
70
+ const config = {
71
+ customerRoleArn: 'arn:aws:s3::123456789012:role/MyRole',
72
+ };
73
+ expect(configFileSchema.safeParse(config).success).toBe(false);
74
+ });
75
+ });
76
+ describe('configFileSchema - ACM certificate ARNs', () => {
77
+ it('should accept valid CloudFront certificate ARN', () => {
78
+ const config = {
79
+ tls: {
80
+ cloudfrontCertArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',
81
+ },
82
+ };
83
+ expect(configFileSchema.safeParse(config).success).toBe(true);
84
+ });
85
+ it('should accept valid ALB certificate ARN', () => {
86
+ const config = {
87
+ tls: {
88
+ bffAlbCertificateArn: 'arn:aws:acm:us-west-2:123456789012:certificate/def-456',
89
+ },
90
+ };
91
+ expect(configFileSchema.safeParse(config).success).toBe(true);
92
+ });
93
+ it('should reject invalid certificate ARN', () => {
94
+ const config = {
95
+ tls: {
96
+ cloudfrontCertArn: 'invalid-cert-arn',
97
+ },
98
+ };
99
+ expect(configFileSchema.safeParse(config).success).toBe(false);
100
+ });
101
+ });
102
+ describe('configFileSchema - Route53 hosted zone IDs', () => {
103
+ it('should accept valid hosted zone ID', () => {
104
+ const config = {
105
+ tls: {
106
+ hostedZoneId: 'Z1234567890ABC',
107
+ },
108
+ };
109
+ expect(configFileSchema.safeParse(config).success).toBe(true);
110
+ });
111
+ it('should reject hosted zone ID not starting with Z', () => {
112
+ const config = {
113
+ tls: {
114
+ hostedZoneId: 'A1234567890ABC',
115
+ },
116
+ };
117
+ expect(configFileSchema.safeParse(config).success).toBe(false);
118
+ });
119
+ it('should reject hosted zone ID with lowercase', () => {
120
+ const config = {
121
+ tls: {
122
+ hostedZoneId: 'Z1234567890abc',
123
+ },
124
+ };
125
+ expect(configFileSchema.safeParse(config).success).toBe(false);
126
+ });
127
+ });
128
+ describe('configFileSchema - VPC and networking', () => {
129
+ it('should accept valid VPC ID', () => {
130
+ const config = {
131
+ networking: {
132
+ customVpcId: 'vpc-1234567890abcdef',
133
+ },
134
+ };
135
+ expect(configFileSchema.safeParse(config).success).toBe(true);
136
+ });
137
+ it('should reject invalid VPC ID format', () => {
138
+ const config = {
139
+ networking: {
140
+ customVpcId: 'invalid-vpc-id',
141
+ },
142
+ };
143
+ expect(configFileSchema.safeParse(config).success).toBe(false);
144
+ });
145
+ it('should accept valid CIDR block', () => {
146
+ const config = {
147
+ networking: {
148
+ vpcCidr: '10.0.0.0/16',
149
+ },
150
+ };
151
+ expect(configFileSchema.safeParse(config).success).toBe(true);
152
+ });
153
+ it('should reject invalid CIDR block', () => {
154
+ const config = {
155
+ networking: {
156
+ vpcCidr: '10.0.0.0',
157
+ },
158
+ };
159
+ expect(configFileSchema.safeParse(config).success).toBe(false);
160
+ });
161
+ it('should accept valid subnet IDs', () => {
162
+ const config = {
163
+ networking: {
164
+ customPublicSubnetIds: 'subnet-abc123,subnet-def456',
165
+ },
166
+ };
167
+ expect(configFileSchema.safeParse(config).success).toBe(true);
168
+ });
169
+ it('should accept single subnet ID', () => {
170
+ const config = {
171
+ networking: {
172
+ customPrivateSubnetIds: 'subnet-abc123',
173
+ },
174
+ };
175
+ expect(configFileSchema.safeParse(config).success).toBe(true);
176
+ });
177
+ it('should reject invalid subnet ID format', () => {
178
+ const config = {
179
+ networking: {
180
+ customPublicSubnetIds: 'invalid-subnet',
181
+ },
182
+ };
183
+ expect(configFileSchema.safeParse(config).success).toBe(false);
184
+ });
185
+ it('should accept valid JSON array for subnet CIDRs', () => {
186
+ const config = {
187
+ networking: {
188
+ publicSubnetCidrs: '["10.0.1.0/24", "10.0.2.0/24"]',
189
+ },
190
+ };
191
+ expect(configFileSchema.safeParse(config).success).toBe(true);
192
+ });
193
+ it('should reject non-JSON string for subnet CIDRs', () => {
194
+ const config = {
195
+ networking: {
196
+ publicSubnetCidrs: '10.0.1.0/24, 10.0.2.0/24',
197
+ },
198
+ };
199
+ expect(configFileSchema.safeParse(config).success).toBe(false);
200
+ });
201
+ it('should accept valid AZ count', () => {
202
+ const config = {
203
+ networking: {
204
+ azCount: '2',
205
+ },
206
+ };
207
+ expect(configFileSchema.safeParse(config).success).toBe(true);
208
+ });
209
+ it('should reject invalid AZ count', () => {
210
+ const config = {
211
+ networking: {
212
+ azCount: '4',
213
+ },
214
+ };
215
+ expect(configFileSchema.safeParse(config).success).toBe(false);
216
+ });
217
+ });
218
+ describe('configFileSchema - security groups', () => {
219
+ it('should accept valid security group ID', () => {
220
+ const config = {
221
+ security: {
222
+ customSecurityGroups: {
223
+ aurora: 'sg-1234567890abcdef',
224
+ },
225
+ },
226
+ };
227
+ expect(configFileSchema.safeParse(config).success).toBe(true);
228
+ });
229
+ it('should reject invalid security group ID', () => {
230
+ const config = {
231
+ security: {
232
+ customSecurityGroups: {
233
+ workflowEngine: 'invalid-sg',
234
+ },
235
+ },
236
+ };
237
+ expect(configFileSchema.safeParse(config).success).toBe(false);
238
+ });
239
+ it('should accept multiple security groups', () => {
240
+ const config = {
241
+ security: {
242
+ customSecurityGroups: {
243
+ aurora: 'sg-1234567890abcdef',
244
+ rdsProxy: 'sg-1234567890abcdef',
245
+ bffEcs: 'sg-1234567890abcdef',
246
+ },
247
+ },
248
+ };
249
+ const result = configFileSchema.safeParse(config);
250
+ if (!result.success) {
251
+ console.log('Validation errors:', result.error.errors);
252
+ }
253
+ expect(result.success).toBe(true);
254
+ });
255
+ });
256
+ describe('configFileSchema - database', () => {
257
+ it('should accept valid database name', () => {
258
+ const config = {
259
+ database: {
260
+ name: 'mydb',
261
+ },
262
+ };
263
+ expect(configFileSchema.safeParse(config).success).toBe(true);
264
+ });
265
+ it('should accept database name with underscores and numbers', () => {
266
+ const config = {
267
+ database: {
268
+ name: 'my_db_123',
269
+ },
270
+ };
271
+ expect(configFileSchema.safeParse(config).success).toBe(true);
272
+ });
273
+ it('should reject database name starting with number', () => {
274
+ const config = {
275
+ database: {
276
+ name: '123db',
277
+ },
278
+ };
279
+ expect(configFileSchema.safeParse(config).success).toBe(false);
280
+ });
281
+ it('should reject database name with hyphens', () => {
282
+ const config = {
283
+ database: {
284
+ name: 'my-db',
285
+ },
286
+ };
287
+ expect(configFileSchema.safeParse(config).success).toBe(false);
288
+ });
289
+ });
290
+ describe('configFileSchema - repository', () => {
291
+ it('should accept valid repository', () => {
292
+ const config = {
293
+ repository: {
294
+ owner: 'my-org',
295
+ repo: 'my-repo',
296
+ },
297
+ };
298
+ expect(configFileSchema.safeParse(config).success).toBe(true);
299
+ });
300
+ it('should reject repository with empty owner', () => {
301
+ const config = {
302
+ repository: {
303
+ owner: '',
304
+ repo: 'my-repo',
305
+ },
306
+ };
307
+ expect(configFileSchema.safeParse(config).success).toBe(false);
308
+ });
309
+ it('should reject repository with empty repo name', () => {
310
+ const config = {
311
+ repository: {
312
+ owner: 'my-org',
313
+ repo: '',
314
+ },
315
+ };
316
+ expect(configFileSchema.safeParse(config).success).toBe(false);
317
+ });
318
+ });
319
+ describe('configFileSchema - feature flags', () => {
320
+ it('should accept boolean feature flags', () => {
321
+ const config = {
322
+ features: {
323
+ deploySembixStudioMemory: true,
324
+ deploySembixStudioNotifications: false,
325
+ enableBffWaf: true,
326
+ },
327
+ };
328
+ expect(configFileSchema.safeParse(config).success).toBe(true);
329
+ });
330
+ it('should reject non-boolean feature flags', () => {
331
+ const config = {
332
+ features: {
333
+ deploySembixStudioMemory: 'yes',
334
+ },
335
+ };
336
+ expect(configFileSchema.safeParse(config).success).toBe(false);
337
+ });
338
+ });
339
+ describe('configFileSchema - frontend', () => {
340
+ it('should accept valid frontend configuration', () => {
341
+ const config = {
342
+ frontend: {
343
+ githubAppClientId: 'Iv1.abc123',
344
+ githubAppName: 'my-github-app',
345
+ jiraClientId: 'jira-client-123',
346
+ },
347
+ };
348
+ expect(configFileSchema.safeParse(config).success).toBe(true);
349
+ });
350
+ it('should reject empty GitHub App Client ID', () => {
351
+ const config = {
352
+ frontend: {
353
+ githubAppClientId: '',
354
+ },
355
+ };
356
+ expect(configFileSchema.safeParse(config).success).toBe(false);
357
+ });
358
+ });
359
+ describe('configFileSchema - hub configuration', () => {
360
+ it('should accept valid hub configuration', () => {
361
+ const config = {
362
+ hub: {
363
+ hubRoles: {
364
+ engineExecutionRole: 'arn:aws:iam::123456789012:role/hub-engine',
365
+ consumerRole: 'arn:aws:iam::123456789012:role/hub-consumer',
366
+ },
367
+ appConfig: {
368
+ roleArn: 'arn:aws:iam::123456789012:role/appconfig',
369
+ appId: 'app-123',
370
+ envId: 'env-456',
371
+ profileId: 'profile-789',
372
+ },
373
+ },
374
+ };
375
+ expect(configFileSchema.safeParse(config).success).toBe(true);
376
+ });
377
+ it('should reject invalid hub role ARN', () => {
378
+ const config = {
379
+ hub: {
380
+ hubRoles: {
381
+ engineExecutionRole: 'invalid-arn',
382
+ },
383
+ },
384
+ };
385
+ expect(configFileSchema.safeParse(config).success).toBe(false);
386
+ });
387
+ });
388
+ describe('configFileSchema - terraform state bucket', () => {
389
+ it('should accept valid S3 bucket name', () => {
390
+ const config = {
391
+ terraformStateBucket: 'my-terraform-state-bucket',
392
+ };
393
+ expect(configFileSchema.safeParse(config).success).toBe(true);
394
+ });
395
+ it('should accept S3 bucket name with numbers', () => {
396
+ const config = {
397
+ terraformStateBucket: 'my-bucket-123',
398
+ };
399
+ expect(configFileSchema.safeParse(config).success).toBe(true);
400
+ });
401
+ it('should reject S3 bucket name with uppercase', () => {
402
+ const config = {
403
+ terraformStateBucket: 'My-Bucket',
404
+ };
405
+ expect(configFileSchema.safeParse(config).success).toBe(false);
406
+ });
407
+ it('should reject S3 bucket name starting with hyphen', () => {
408
+ const config = {
409
+ terraformStateBucket: '-my-bucket',
410
+ };
411
+ expect(configFileSchema.safeParse(config).success).toBe(false);
412
+ });
413
+ it('should reject S3 bucket name ending with hyphen', () => {
414
+ const config = {
415
+ terraformStateBucket: 'my-bucket-',
416
+ };
417
+ expect(configFileSchema.safeParse(config).success).toBe(false);
418
+ });
419
+ });
420
+ describe('configFileSchema - complete configuration', () => {
421
+ it('should accept complete valid configuration', () => {
422
+ const config = {
423
+ repository: {
424
+ owner: 'sembix-ai',
425
+ repo: 'sembix-studio',
426
+ },
427
+ environmentName: 'production',
428
+ awsAccountId: '123456789012',
429
+ awsRegion: 'us-east-1',
430
+ customerRoleArn: 'arn:aws:iam::123456789012:role/CustomerRole',
431
+ terraformStateBucket: 'my-terraform-state',
432
+ database: {
433
+ name: 'studiodb',
434
+ user: 'dbadmin',
435
+ },
436
+ networking: {
437
+ enableVpcEndpoints: true,
438
+ useCustomNetworking: false,
439
+ vpcCidr: '10.0.0.0/16',
440
+ azCount: '3',
441
+ },
442
+ security: {
443
+ useCustomSecurityGroups: false,
444
+ useCustomIamPolicies: false,
445
+ },
446
+ tls: {
447
+ cloudfrontDomain: 'app.example.com',
448
+ cloudfrontCertArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc',
449
+ bffAlbCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/def',
450
+ hostedZoneId: 'Z1234567890ABC',
451
+ },
452
+ features: {
453
+ deploySembixStudioMemory: true,
454
+ deploySembixStudioNotifications: true,
455
+ enableBffWaf: false,
456
+ },
457
+ frontend: {
458
+ githubAppClientId: 'Iv1.abc123',
459
+ githubAppName: 'my-app',
460
+ jiraClientId: 'jira-123',
461
+ },
462
+ };
463
+ expect(configFileSchema.safeParse(config).success).toBe(true);
464
+ });
465
+ it('should accept empty configuration (all fields optional)', () => {
466
+ const config = {};
467
+ expect(configFileSchema.safeParse(config).success).toBe(true);
468
+ });
469
+ });
470
+ });
471
+ //# sourceMappingURL=config-schema.test.js.map