@flisk-sh/flisk 1.4.0 → 1.4.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 +305 -52
  2. package/cli/dist/commands/doctor.js +62 -0
  3. package/cli/dist/commands/runtime.js +6 -3
  4. package/cli/dist/generator/template-engine.js +7 -4
  5. package/cli/dist/utils/runtime-layout.js +7 -0
  6. package/cli/dist-bundle/{chunk-QQEV6D74.js → chunk-3XODDLRJ.js} +1 -1
  7. package/cli/dist-bundle/{chunk-VMB5673O.js → chunk-43XWL2PB.js} +30 -26
  8. package/cli/dist-bundle/{chunk-5EZ4EX6U.js → chunk-53L2JN7G.js} +1 -1
  9. package/cli/dist-bundle/{chunk-BWK6MPCB.js → chunk-5F7YP5IR.js} +1 -1
  10. package/cli/dist-bundle/{chunk-36SOQVAL.js → chunk-5QBYM33Q.js} +1 -1
  11. package/cli/dist-bundle/{chunk-HEMPQKD6.js → chunk-DU2BN46C.js} +1 -1
  12. package/cli/dist-bundle/{chunk-ICXF6PXA.js → chunk-ETKUVKJ2.js} +2 -2
  13. package/cli/dist-bundle/{chunk-PVBAGX5X.js → chunk-I7TGIK5N.js} +1 -1
  14. package/cli/dist-bundle/{chunk-B6Q5VKL7.js → chunk-IGN5UC3P.js} +1 -1
  15. package/cli/dist-bundle/chunk-MWOVYIBB.js +54 -0
  16. package/cli/dist-bundle/{chunk-D7N2GGJH.js → chunk-YHYRGWUH.js} +1 -1
  17. package/cli/dist-bundle/{chunk-VHFFSBVJ.js → chunk-YNCBBJ4V.js} +1 -1
  18. package/cli/dist-bundle/cli.js +153 -153
  19. package/cli/dist-bundle/{cold-preflight-ABU26DYW.js → cold-preflight-ZOPR7PWJ.js} +1 -1
  20. package/cli/dist-bundle/{demo-stage-EBIUCJBX.js → demo-stage-OHNTP2UJ.js} +1 -1
  21. package/cli/dist-bundle/{open-2KSAF6M2.js → open-XROJ6YEY.js} +1 -1
  22. package/cli/dist-bundle/project-7PTIOHGF.js +2 -0
  23. package/cli/dist-bundle/{services-V6Q55JR5.js → services-BIMF6MVU.js} +1 -1
  24. package/cli/dist-bundle/{tilt-port-owner-MWJ7P6NB.js → tilt-port-owner-ZY5M2JC4.js} +1 -1
  25. package/cli/package.json +1 -1
  26. package/package.json +12 -2
  27. package/runtime/platform/services/platform/proxy/service.yaml +14 -0
  28. package/cli/dist/commands/__tests__/project-e2e-helpers.js +0 -49
  29. package/cli/dist/commands/__tests__/ui-loading-screen.test.js +0 -16
  30. package/cli/dist/generator/__tests__/golden-helpers.js +0 -43
  31. package/cli/dist-bundle/chunk-763VK575.js +0 -54
  32. package/cli/dist-bundle/project-YD3C4ZV3.js +0 -2
  33. package/runtime/discovery/test_resource_snapshot.py +0 -253
  34. package/runtime/engine/assets/docker/wake-gateway/decide.test.ts +0 -177
  35. package/runtime/engine/topologies/tilt/manifest/tests/test_integration.star +0 -278
  36. package/runtime/engine/topologies/tilt/manifest/tests/test_loader_parser.star +0 -475
  37. package/runtime/engine/topologies/tilt/manifest/tests/test_manifest.star +0 -319
  38. package/runtime/engine/topologies/tilt/manifest/tests/test_validator.star +0 -472
  39. package/runtime/ext/ide-components/shared/test_config_service.py +0 -328
  40. /package/{shared-platform-engineering → runtime/shared-platform-engineering}/docker-templates/bun-hoisted-symlink-fix.sh +0 -0
  41. /package/{shared-platform-engineering → runtime/shared-platform-engineering}/docker-templates/infisical-entrypoint.sh +0 -0
  42. /package/{shared-platform-engineering → runtime/shared-platform-engineering}/docker-templates/install-deps.sh +0 -0
  43. /package/{shared-platform-engineering → runtime/shared-platform-engineering}/docker-templates/prisma-bun-client-link-fix.sh +0 -0
  44. /package/{shared-platform-engineering → runtime/shared-platform-engineering}/docker-templates/prisma-normalize-client.sh +0 -0
@@ -1,475 +0,0 @@
1
- # Copyright (c) 2026 Flisk Landscape contributors
2
- # SPDX-License-Identifier: MIT
3
- # =============================================================================
4
- # 📋 MANIFEST MODULE - INTEGRATION TESTS (Phase 2)
5
- # =============================================================================
6
- # Path: .tilt/topologies/tilt/manifest/tests/test_loader_parser.star
7
- # Purpose: Integration tests for loader and parser modules
8
- # Status: Phase 2 of manifest system refactoring
9
- # =============================================================================
10
-
11
- load("../__init__.star", "Manifest")
12
- load("../loader.star", "ManifestLoader")
13
- load("../parser.star", "ManifestParser")
14
-
15
- # =============================================================================
16
- # TEST FRAMEWORK
17
- # =============================================================================
18
-
19
- test_results = {
20
- 'passed': 0,
21
- 'failed': 0,
22
- 'errors': [],
23
- }
24
-
25
- def assert_true(condition, message="Assertion failed"):
26
- """Assert that condition is true."""
27
- if not condition:
28
- test_results['failed'] += 1
29
- test_results['errors'].append("FAIL: " + message)
30
- else:
31
- test_results['passed'] += 1
32
-
33
- def assert_equal(expected, actual, message="Values not equal"):
34
- """Assert that expected equals actual."""
35
- if expected != actual:
36
- test_results['failed'] += 1
37
- test_results['errors'].append(
38
- "FAIL: {} - expected '{}', got '{}'".format(message, expected, actual)
39
- )
40
- else:
41
- test_results['passed'] += 1
42
-
43
- def assert_not_none(value, message="Value is None"):
44
- """Assert that value is not None."""
45
- if value == None:
46
- test_results['failed'] += 1
47
- test_results['errors'].append("FAIL: " + message)
48
- else:
49
- test_results['passed'] += 1
50
-
51
- def assert_in(value, collection, message="Value not in collection"):
52
- """Assert that value is in collection."""
53
- found = False
54
- for item in collection:
55
- if item == value:
56
- found = True
57
- break
58
-
59
- if not found:
60
- test_results['failed'] += 1
61
- test_results['errors'].append(
62
- "FAIL: {} - '{}' not in collection".format(message, value)
63
- )
64
- else:
65
- test_results['passed'] += 1
66
-
67
- # =============================================================================
68
- # TEST SUITE: Loader Module
69
- # =============================================================================
70
-
71
- def _test_loader_functions():
72
- """Test loader functions."""
73
- print("🔍 Testing loader module...")
74
-
75
- # Test that loader functions are accessible via facade
76
- assert_not_none(Manifest.load_from_file, "load_from_file should be accessible")
77
- assert_not_none(Manifest.load_from_path, "load_from_path should be accessible")
78
- assert_not_none(Manifest.load_all, "load_all should be accessible")
79
- assert_not_none(Manifest.clear_cache, "clear_cache should be accessible")
80
- assert_not_none(Manifest.get_cache_stats, "get_cache_stats should be accessible")
81
- assert_not_none(Manifest.is_cached, "is_cached should be accessible")
82
- assert_not_none(Manifest.get_manifest_path, "get_manifest_path should be accessible")
83
-
84
- # Test get_manifest_path
85
- path = Manifest.get_manifest_path("services/product/user/user-management-backend")
86
- assert_true(
87
- path.endswith("service.json"),
88
- "get_manifest_path should include manifest filename"
89
- )
90
-
91
- # Test cache functions
92
- Manifest.clear_cache()
93
- stats = Manifest.get_cache_stats()
94
- assert_equal(0, stats['entries'], "Cache should be empty after clear")
95
-
96
- print("✅ Loader module tests complete\n")
97
-
98
- # =============================================================================
99
- # TEST SUITE: Parser Module
100
- # =============================================================================
101
-
102
- def _test_parser_functions():
103
- """Test parser functions."""
104
- print("🔍 Testing parser module...")
105
-
106
- # Test that parser functions are accessible via facade
107
- assert_not_none(Manifest.parse, "parse should be accessible")
108
- assert_not_none(Manifest.normalize, "normalize should be accessible")
109
- assert_not_none(Manifest.get_normalized, "get_normalized should be accessible")
110
- assert_not_none(Manifest.extract_resource_path, "extract_resource_path should be accessible")
111
- assert_not_none(Manifest.extract_stack, "extract_stack should be accessible")
112
- assert_not_none(Manifest.parse_traefik, "parse_traefik should be accessible")
113
- assert_not_none(Manifest.merge_manifests, "merge_manifests should be accessible")
114
- assert_not_none(Manifest.get_description, "get_description should be accessible")
115
-
116
- print("✅ Parser module tests complete\n")
117
-
118
- # =============================================================================
119
- # TEST SUITE: Parse and Normalize
120
- # =============================================================================
121
-
122
- def _test_parse_and_normalize():
123
- """Test parsing and normalization together."""
124
- print("🔍 Testing parse and normalize integration...")
125
-
126
- # Test valid JSON parsing
127
- valid_json = '''
128
- {
129
- "appName": "test-service",
130
- "appType": "backend",
131
- "stack": "user",
132
- "port": 4001
133
- }
134
- '''
135
-
136
- result = Manifest.parse(valid_json)
137
- assert_not_none(result.manifest, "Should parse valid JSON")
138
- assert_equal(None, result.error, "Should have no error for valid JSON")
139
-
140
- # Test normalization
141
- normalized = Manifest.normalize(result.manifest, "services/product/user/test-service")
142
- assert_not_none(normalized, "Should normalize manifest")
143
- assert_equal("test-service", normalized['appName'], "Should preserve appName")
144
- assert_equal("backend", normalized['appType'], "Should preserve appType")
145
- assert_equal("user", normalized['stack'], "Should preserve stack")
146
- assert_equal(4001, normalized['port'], "Should preserve port")
147
-
148
- # Test defaults are applied
149
- assert_equal(1, normalized['replicas'], "Should apply default replicas")
150
- assert_equal(3000, normalized['internalPort'], "Should apply default internalPort")
151
- assert_equal("bun", normalized['runtime'], "Should apply default runtime")
152
- assert_not_none(normalized['syncs'], "Should compute default syncs")
153
- assert_not_none(normalized['traefik'], "Should create traefik config")
154
-
155
- # Test _normalized flag is set
156
- assert_equal(True, normalized.get('_normalized'), "Should set _normalized flag")
157
-
158
- print("✅ Parse and normalize tests complete\n")
159
-
160
- # =============================================================================
161
- # TEST SUITE: Domain Extraction
162
- # =============================================================================
163
-
164
- def _test_stack_extraction():
165
- """Test stack extraction from resource paths."""
166
- print("🔍 Testing stack extraction...")
167
-
168
- # Standard paths
169
- stack = Manifest.extract_stack("services/product/user/user-management-backend")
170
- assert_equal("user", stack, "Should extract user stack")
171
-
172
- stack = Manifest.extract_stack("services/product/order/order-management-backend")
173
- assert_equal("order", stack, "Should extract order stack")
174
-
175
- stack = Manifest.extract_stack("services/product/identity/identity-management-backend")
176
- assert_equal("identity", stack, "Should extract identity stack")
177
-
178
- # Special case: order-planner
179
- stack = Manifest.extract_stack("services/product/order-planner/order-planner-backend")
180
- assert_equal("order-planner", stack, "Should extract order-planner stack")
181
-
182
- # Special case: profile -> identity
183
- stack = Manifest.extract_stack("services/product/profile/profile-management-backend")
184
- assert_equal("identity", stack, "Should map profile to identity stack")
185
-
186
- print("✅ Stack extraction tests complete\n")
187
-
188
- # =============================================================================
189
- # TEST SUITE: Frontend Manifest Normalization
190
- # =============================================================================
191
-
192
- def _test_frontend_normalization():
193
- """Test normalization for frontend manifests."""
194
- print("🔍 Testing frontend normalization...")
195
-
196
- frontend_json = '''
197
- {
198
- "appName": "user-management-frontend",
199
- "appType": "frontend",
200
- "stack": "user",
201
- "port": 3000,
202
- "backendName": "user-management-backend"
203
- }
204
- '''
205
-
206
- result = Manifest.parse(frontend_json)
207
- normalized = Manifest.normalize(result.manifest, "services/product/user/user-management-frontend")
208
-
209
- assert_equal("frontend", normalized['appType'], "Should preserve frontend appType")
210
- assert_equal("user-management-backend", normalized['backendName'], "Should preserve backendName")
211
-
212
- # Test backendName auto-computation
213
- frontend_json_no_backend = '''
214
- {
215
- "appName": "test-frontend",
216
- "appType": "frontend",
217
- "stack": "user",
218
- "port": 3001
219
- }
220
- '''
221
-
222
- result2 = Manifest.parse(frontend_json_no_backend)
223
- normalized2 = Manifest.normalize(result2.manifest, "services/product/user/test-frontend")
224
- assert_equal("test-backend", normalized2['backendName'], "Should auto-compute backendName")
225
-
226
- print("✅ Frontend normalization tests complete\n")
227
-
228
- # =============================================================================
229
- # TEST SUITE: Traefik Configuration
230
- # =============================================================================
231
-
232
- def _test_traefik_config():
233
- """Test Traefik configuration parsing."""
234
- print("🔍 Testing Traefik configuration...")
235
-
236
- # Backend with explicit traefik config
237
- backend_json = '''
238
- {
239
- "appName": "user-management-backend",
240
- "appType": "backend",
241
- "stack": "user",
242
- "port": 4000,
243
- "traefik": {
244
- "pathPrefix": "/api/v1/users",
245
- "priority": 200
246
- }
247
- }
248
- '''
249
-
250
- result = Manifest.parse(backend_json)
251
- normalized = Manifest.normalize(result.manifest, "services/product/user/user-management-backend")
252
-
253
- assert_equal("/api/v1/users", normalized['traefik']['pathPrefix'], "Should preserve pathPrefix")
254
- assert_equal(200, normalized['traefik']['priority'], "Should preserve priority")
255
-
256
- # Test auto-generated pathPrefix
257
- backend_json_no_traefik = '''
258
- {
259
- "appName": "test-management-backend",
260
- "appType": "backend",
261
- "stack": "user",
262
- "port": 4001
263
- }
264
- '''
265
-
266
- result2 = Manifest.parse(backend_json_no_traefik)
267
- normalized2 = Manifest.normalize(result2.manifest, "services/product/user/test-management-backend")
268
-
269
- assert_not_none(normalized2['traefik']['pathPrefix'], "Should auto-generate pathPrefix")
270
- assert_not_none(normalized2['traefik']['priority'], "Should set default priority")
271
-
272
- print("✅ Traefik configuration tests complete\n")
273
-
274
- # =============================================================================
275
- # TEST SUITE: Manifest Merging
276
- # =============================================================================
277
-
278
- def _test_manifest_merging():
279
- """Test manifest merging functionality."""
280
- print("🔍 Testing manifest merging...")
281
-
282
- base = {
283
- 'appName': 'test-service',
284
- 'appType': 'backend',
285
- 'port': 4000,
286
- 'featuresEnabled': ['nats'],
287
- 'envVars': {
288
- 'SHARED_VAR': 'base_value',
289
- },
290
- }
291
-
292
- overlay = {
293
- 'port': 4001, # Override
294
- 'featuresEnabled': ['prisma'], # Override
295
- 'envVars': {
296
- 'SHARED_VAR': 'overlay_value', # Override
297
- 'NEW_VAR': 'new_value', # Add
298
- },
299
- }
300
-
301
- merged = Manifest.merge_manifests(base, overlay)
302
-
303
- assert_equal('test-service', merged['appName'], "Should preserve base appName")
304
- assert_equal(4001, merged['port'], "Should overlay port")
305
- assert_equal(['prisma'], merged['featuresEnabled'], "Should overlay features")
306
- assert_equal('overlay_value', merged['envVars']['SHARED_VAR'], "Should overlay nested env var")
307
- assert_equal('new_value', merged['envVars']['NEW_VAR'], "Should add new env var")
308
-
309
- print("✅ Manifest merging tests complete\n")
310
-
311
- # =============================================================================
312
- # TEST SUITE: Values written into generated config (GHSA-phgf-pww4-7jxc)
313
- # =============================================================================
314
-
315
- def _test_generated_string_fields():
316
- """A line break or YAML syntax in a field that reaches generated compose output must be rejected."""
317
- base = {'appName': 'api', 'stack': 'shop', 'traefik': {'pathPrefix': '/api/v1/shop-management'}}
318
-
319
- assert_equal(None, ManifestParser.generated_string_error(base), "Clean manifest has no generated-field error")
320
-
321
- safe = {
322
- 'appName': 'api',
323
- 'stack': 'shop-1',
324
- 'healthCheckPath': '/api/v1/health',
325
- 'databaseName': 'Flisk_shop',
326
- 'image': 'ghcr.io/acme/app:1.0',
327
- 'traefik': {'host': 'shop.backend.my-project.local', 'pathPrefix': '/api/v1/shop', 'healthCheck': '/health'},
328
- 'nats': {'queueGroup': 'shop_backend_svc'},
329
- }
330
- assert_equal(None, ManifestParser.generated_string_error(safe), "Safe values are accepted")
331
-
332
- hostile = [
333
- {'healthCheckPath': '/health\n privileged: true'},
334
- {'healthCheckPath': '/health"'},
335
- {'traefik': {'host': 'a.local\n privileged: true', 'pathPrefix': '/api'}},
336
- {'traefik': {'pathPrefix': '/api/v1/x\n network_mode: host'}},
337
- {'traefik': {'pathPrefix': '/api', 'healthCheck': '/h\n volumes: []'}},
338
- {'traefik': {'pathPrefix': '/api'}, 'nats': {'queueGroup': 'g: true'}},
339
- {'traefik': {'pathPrefix': '/api'}, 'databaseName': 'Flisk_x\n privileged: true'},
340
- {'traefik': {'pathPrefix': '/api'}, 'stack': 'shop # comment'},
341
- {'traefik': {'pathPrefix': '/api'}, 'image': 'app\n privileged: true'},
342
- {'traefik': {'pathPrefix': '/api'}, 'healthCheckPath': 'health'},
343
- {'traefik': {'pathPrefix': '/api'}, 'healthCheckPath': 42},
344
- ]
345
- for manifest in hostile:
346
- assert_not_none(ManifestParser.generated_string_error(manifest), "Rejects unsafe value: " + str(manifest))
347
-
348
- # The message names the field and never echoes the value
349
- message = ManifestParser.generated_string_error(hostile[0], 'services/shop/api')
350
- assert_true("healthCheckPath" in message, "Error names the field")
351
- assert_true("\n" not in message, "Error does not echo the value")
352
-
353
- # Fields outside the allowlist also reach generated config, so a line break anywhere is refused
354
- clean = {'appName': 'api', 'stack': 'shop', 'traefik': {'pathPrefix': '/api'}}
355
- line_breaks = [
356
- {'port': '4000\n privileged: true'},
357
- {'appName': 'api\n privileged: true'},
358
- {'dockerfile': 'Dockerfile\r privileged: true'},
359
- {'buildContext': 'src\u2028privileged: true'},
360
- {'envVars': [{'name': 'A', 'value': 'v\n privileged: true'}]},
361
- {'params': {'a\n privileged': 'x'}},
362
- ]
363
- for extra in line_breaks:
364
- manifest = dict(clean)
365
- manifest.update(extra)
366
- assert_not_none(ManifestParser.generated_string_error(manifest), "Rejects a line break: " + repr(extra))
367
-
368
- smoke = dict(clean)
369
- smoke['smoke'] = {'via': '/api', 'create': {'body': '{\n}'}}
370
- assert_equal(None, ManifestParser.generated_string_error(smoke), "smoke is never written into generated config")
371
-
372
- # =============================================================================
373
- # TEST SUITE: Get Normalized Convenience Function
374
- # =============================================================================
375
-
376
- def _test_get_normalized():
377
- """Test the get_normalized convenience function."""
378
- print("🔍 Testing get_normalized...")
379
-
380
- json_content = '''
381
- {
382
- "appName": "test-service",
383
- "appType": "backend",
384
- "stack": "user",
385
- "port": 4001
386
- }
387
- '''
388
-
389
- result = Manifest.get_normalized(json_content, "services/product/user/test-service")
390
-
391
- assert_not_none(result.manifest, "Should return normalized manifest")
392
- assert_equal(None, result.error, "Should have no error")
393
- assert_equal("test-service", result.manifest['appName'], "Should have correct appName")
394
- assert_equal("backend", result.manifest['appType'], "Should have correct appType")
395
- assert_equal(True, result.manifest.get('_normalized'), "Should be marked as normalized")
396
-
397
- print("✅ Get normalized tests complete\n")
398
-
399
- # =============================================================================
400
- # TEST SUITE: Service Description
401
- # =============================================================================
402
-
403
- def _test_resource_description():
404
- """Test service description generation."""
405
- print("🔍 Testing service description...")
406
-
407
- manifest = {
408
- 'appName': 'user-management-backend',
409
- 'appType': 'backend',
410
- 'stack': 'user',
411
- }
412
-
413
- desc = Manifest.get_description(manifest)
414
- assert_equal("user/backend/user-management-backend", desc, "Should generate correct description")
415
-
416
- print("✅ Service description tests complete\n")
417
-
418
- # =============================================================================
419
- # MAIN
420
- # =============================================================================
421
-
422
- def _test_byo_health_path_is_optional():
423
- """A bring-your-own service without healthCheckPath gets none; /health is only the default for HTTP types."""
424
- print("\n🧪 Testing bring-your-own health path is optional...")
425
-
426
- byo = ManifestParser.normalize({'appType': 'bring-your-own', 'appName': 'postgres', 'stack': 'shop', 'port': 4500}, 'services/shop/postgres')
427
- assert_true('healthCheckPath' not in byo, "BYO without healthCheckPath gets no default /health")
428
-
429
- byo_http = ManifestParser.normalize({'appType': 'bring-your-own', 'appName': 'web', 'stack': 'shop', 'port': 4501, 'healthCheckPath': '/status'}, 'services/shop/web')
430
- assert_equal('/status', byo_http.get('healthCheckPath'), "BYO keeps an explicit healthCheckPath")
431
-
432
- backend = ManifestParser.normalize({'appType': 'backend', 'appName': 'api', 'stack': 'shop', 'port': 4000}, 'services/shop/api')
433
- assert_equal('/health', backend.get('healthCheckPath'), "Backends still default to /health")
434
-
435
- def run_tests():
436
- """Run all Phase 2 integration tests."""
437
- print("\n" + "="*70)
438
- print("📋 MANIFEST MODULE - PHASE 2 INTEGRATION TESTS")
439
- print("="*70 + "\n")
440
-
441
- # Run all test suites
442
- _test_loader_functions()
443
- _test_parser_functions()
444
- _test_parse_and_normalize()
445
- _test_stack_extraction()
446
- _test_frontend_normalization()
447
- _test_traefik_config()
448
- _test_manifest_merging()
449
- _test_get_normalized()
450
- _test_resource_description()
451
- _test_generated_string_fields()
452
- _test_byo_health_path_is_optional()
453
-
454
- # Print results
455
- print("\n" + "="*70)
456
- print("📊 TEST RESULTS")
457
- print("="*70)
458
- print("✅ Passed: {}".format(test_results['passed']))
459
- print("❌ Failed: {}".format(test_results['failed']))
460
-
461
- if test_results['errors']:
462
- print("\n📝 ERRORS:")
463
- for error in test_results['errors']:
464
- print(" " + error)
465
- else:
466
- print("\n🎉 All Phase 2 integration tests passed!")
467
-
468
- print("="*70 + "\n")
469
-
470
- return test_results['failed'] == 0
471
-
472
- # Export test runner
473
- TestLoaderParser = struct(
474
- run=run_tests,
475
- )