@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,328 +0,0 @@
1
- #!/usr/bin/env python3
2
- # Copyright (c) 2026 Flisk Landscape contributors
3
- # SPDX-License-Identifier: MIT
4
- """
5
- Automated tests for ConfigService
6
- Tests path validation, file operations, backups, and security features.
7
- """
8
-
9
- import os
10
- import sys
11
- import tempfile
12
- import shutil
13
- from pathlib import Path
14
-
15
- # Add parent to path
16
- sys.path.insert(0, str(Path(__file__).parent))
17
- from config_service import ConfigService, SaveResult
18
-
19
-
20
- class TestConfigService:
21
- """Test suite for ConfigService"""
22
-
23
- def __init__(self):
24
- self.passed = 0
25
- self.failed = 0
26
- self.temp_dir = None
27
-
28
- def setup(self):
29
- """Create temporary test directory"""
30
- self.temp_dir = tempfile.mkdtemp()
31
- # Create structure mimicking project
32
- (Path(self.temp_dir) / '.tilt-engine' / 'topologies').mkdir(parents=True)
33
- (Path(self.temp_dir) / '.tilt-engine' / 'topologies' / 'test.star').write_text('# test')
34
-
35
- # Create root master configs
36
- (Path(self.temp_dir) / 'TILT_RESOURCE_DEFAULTS.star').write_text('# defaults')
37
- (Path(self.temp_dir) / 'spec.master').write_text('# spec')
38
-
39
- # Override PROJECT_ROOT for testing by patching the module directly
40
- import config_service
41
- self.original_root = config_service.PROJECT_ROOT
42
- config_service.PROJECT_ROOT = Path(self.temp_dir)
43
- # Also patch in file_utils
44
- import file_utils
45
- self.original_file_utils_root = file_utils.PROJECT_ROOT
46
- file_utils.PROJECT_ROOT = Path(self.temp_dir)
47
-
48
- def teardown(self):
49
- """Cleanup temporary directory"""
50
- # Restore original
51
- import config_service
52
- config_service.PROJECT_ROOT = self.original_root
53
- import file_utils
54
- file_utils.PROJECT_ROOT = self.original_file_utils_root
55
-
56
- if self.temp_dir and os.path.exists(self.temp_dir):
57
- shutil.rmtree(self.temp_dir)
58
-
59
- def assert_true(self, condition, msg):
60
- """Assert condition is true"""
61
- if condition:
62
- self.passed += 1
63
- print(f" ✓ {msg}")
64
- else:
65
- self.failed += 1
66
- print(f" ✗ {msg}")
67
-
68
- def assert_false(self, condition, msg):
69
- """Assert condition is false"""
70
- self.assert_true(not condition, msg)
71
-
72
- def assert_equals(self, a, b, msg):
73
- """Assert two values are equal"""
74
- if a == b:
75
- self.passed += 1
76
- print(f" ✓ {msg}")
77
- else:
78
- self.failed += 1
79
- print(f" ✗ {msg}: expected {b}, got {a}")
80
-
81
- def run_all_tests(self):
82
- """Run all test methods"""
83
- print("\n" + "="*60)
84
- print("ConfigService Test Suite")
85
- print("="*60)
86
-
87
- self.setup()
88
- try:
89
- self.test_path_validation()
90
- self.test_file_type_validation()
91
- self.test_hash_computation()
92
- self.test_backup_rotation()
93
- self.test_conflict_detection()
94
- self.test_atomic_write()
95
- self.test_csrf_validation()
96
- self.test_content_length_validation()
97
- finally:
98
- self.teardown()
99
-
100
- print("\n" + "="*60)
101
- print(f"Results: {self.passed} passed, {self.failed} failed")
102
- print("="*60)
103
- return self.failed == 0
104
-
105
- def test_path_validation(self):
106
- """Test 7.4, 7.10, 7.11: Path validation and traversal prevention"""
107
- print("\n📁 Testing Path Validation (Tasks 7.4, 7.10, 7.11)")
108
-
109
- # Valid paths - topologies
110
- valid_paths = [
111
- '.tilt-engine/topologies/test.star',
112
- '.tilt-engine/topologies/tilt/discovery/registry.star',
113
- ]
114
-
115
- for path in valid_paths:
116
- result = ConfigService.validate_write_path(path)
117
- self.assert_true(result is not None, f"Valid path accepted: {path}")
118
-
119
- # Valid paths - project root master configs (Task 7.11)
120
- root_configs = [
121
- 'TILT_RESOURCE_DEFAULTS.star',
122
- 'spec.master',
123
- '.tilt-engine/spec.master',
124
- ]
125
-
126
- for path in root_configs:
127
- result = ConfigService.validate_write_path(path)
128
- self.assert_true(result is not None, f"Root config accepted: {path}")
129
-
130
- # Invalid paths - traversal attempts
131
- invalid_paths = [
132
- '../outside.star',
133
- '.tilt-engine/../../etc/passwd',
134
- '%2e%2e%2fsecret.star',
135
- '/etc/passwd',
136
- 'random/file.star',
137
- 'README.md',
138
- ]
139
-
140
- for path in invalid_paths:
141
- result = ConfigService.validate_write_path(path)
142
- self.assert_true(result is None, f"Invalid path rejected: {path}")
143
-
144
- def test_file_type_validation(self):
145
- """Test 7.12: File extension validation"""
146
- print("\n📄 Testing File Type Validation (Task 7.12)")
147
-
148
- # Valid file types
149
- valid_files = [
150
- 'test.star',
151
- 'spec.master',
152
- '.tilt-engine/spec.master',
153
- 'TILT_RESOURCE_DEFAULTS.star',
154
- ]
155
-
156
- for path in valid_files:
157
- is_valid, _ = ConfigService.validate_file_type(path)
158
- self.assert_true(is_valid, f"Valid file type: {path}")
159
-
160
- # Invalid file types
161
- invalid_files = [
162
- 'README.md',
163
- 'file.txt',
164
- 'script.py',
165
- 'config.json',
166
- ]
167
-
168
- for path in invalid_files:
169
- is_valid, _ = ConfigService.validate_file_type(path)
170
- self.assert_false(is_valid, f"Invalid file type rejected: {path}")
171
-
172
- def test_hash_computation(self):
173
- """Test 7.9: Hash computation for conflict detection"""
174
- print("\n🔐 Testing Hash Computation (Task 7.9)")
175
-
176
- # Same content produces same hash
177
- content1 = "Hello, World!"
178
- hash1 = ConfigService.compute_content_hash(content1)
179
- hash2 = ConfigService.compute_content_hash(content1)
180
- self.assert_equals(hash1, hash2, "Same content produces same hash")
181
-
182
- # Different content produces different hash
183
- content2 = "Hello, World"
184
- hash3 = ConfigService.compute_content_hash(content2)
185
- self.assert_true(hash1 != hash3, "Different content produces different hash")
186
-
187
- # Hash is SHA-256 (64 hex characters)
188
- self.assert_equals(len(hash1), 64, "Hash is 64 hex chars (SHA-256)")
189
-
190
- def test_backup_rotation(self):
191
- """Test 7.8: Backup creation and rotation"""
192
- print("\n💾 Testing Backup Rotation (Task 7.8)")
193
-
194
- import time
195
-
196
- # Create test file
197
- test_file = Path(self.temp_dir) / '.tilt-engine' / 'topologies' / 'backup_test.star'
198
- test_file.write_text('version 1')
199
-
200
- # Create multiple backups with small delays to ensure different timestamps
201
- for i in range(5):
202
- time.sleep(0.01) # Small delay to ensure different timestamps
203
- test_file.write_text(f'version {i+2}')
204
- backup_path, msg = ConfigService.create_backup_with_rotation(test_file, max_backups=3)
205
- self.assert_true(backup_path is not None, f"Backup {i+1} created: {msg}")
206
-
207
- # Check only 3 backups exist
208
- backup_dir = test_file.parent / '.backups'
209
- if backup_dir.exists():
210
- backups = list(backup_dir.glob('backup_test.star.*.backup'))
211
- self.assert_true(len(backups) <= 3, f"Rotation keeps max 3 backups (found {len(backups)})")
212
- else:
213
- self.assert_true(False, "Backup directory should exist")
214
-
215
- def test_conflict_detection(self):
216
- """Test 7.9: File content hash conflict detection"""
217
- print("\n⚠️ Testing Conflict Detection (Task 7.9)")
218
-
219
- # Create test file
220
- test_file = Path(self.temp_dir) / '.tilt-engine' / 'topologies' / 'conflict_test.star'
221
- original_content = "Original content"
222
- test_file.write_text(original_content)
223
-
224
- # Get original hash
225
- original_hash = ConfigService.compute_content_hash(original_content)
226
-
227
- # No conflict when file unchanged
228
- has_conflict, msg = ConfigService.check_conflict(test_file, original_hash)
229
- self.assert_false(has_conflict, "No conflict when file unchanged")
230
-
231
- # Modify file
232
- test_file.write_text("Modified content")
233
-
234
- # Conflict detected after modification
235
- has_conflict, msg = ConfigService.check_conflict(test_file, original_hash)
236
- self.assert_true(has_conflict, "Conflict detected after file modification")
237
-
238
- def test_atomic_write(self):
239
- """Test 7.7, 7.20: Atomic write and failure recovery"""
240
- print("\n📝 Testing Atomic Write (Tasks 7.7, 7.20)")
241
-
242
- # Use relative path that will pass validation
243
- rel_path = '.tilt-engine/topologies/atomic_test.star'
244
- abs_path = Path(self.temp_dir) / rel_path
245
-
246
- # Verify path validation works
247
- validated = ConfigService.validate_write_path(rel_path)
248
- self.assert_true(validated is not None, "Path validation for atomic write")
249
-
250
- # Test successful write (using relative path)
251
- result = ConfigService.atomic_write(validated, "Test content", create_backup=False)
252
- self.assert_true(result.success, f"Atomic write succeeds: {result.message}")
253
- self.assert_true(abs_path.exists(), "File created")
254
- self.assert_equals(abs_path.read_text(), "Test content", "Content written correctly")
255
-
256
- # Test write with backup
257
- result = ConfigService.atomic_write(validated, "Updated content", create_backup=True)
258
- self.assert_true(result.success, f"Atomic write with backup succeeds: {result.message}")
259
- self.assert_true(result.backup_path is not None, "Backup created")
260
-
261
- # Test conflict detection during write
262
- original_hash = ConfigService.compute_content_hash("Wrong hash")
263
- result = ConfigService.atomic_write(
264
- validated,
265
- "New content",
266
- original_hash=original_hash,
267
- create_backup=False
268
- )
269
- self.assert_false(result.success, "Write fails with hash conflict")
270
-
271
- def test_csrf_validation(self):
272
- """Test 7.13: CSRF protection"""
273
- print("\n🛡️ Testing CSRF Protection (Task 7.13)")
274
-
275
- # Valid token with localhost origin
276
- is_valid, _ = ConfigService.validate_csrf_token(
277
- "valid_token_12345",
278
- referer="http://localhost:9767/",
279
- origin="http://localhost:9767"
280
- )
281
- self.assert_true(is_valid, "Valid CSRF token with localhost origin")
282
-
283
- # Invalid token (too short)
284
- is_valid, _ = ConfigService.validate_csrf_token(
285
- "short",
286
- referer="http://localhost:9767/",
287
- origin="http://localhost:9767"
288
- )
289
- self.assert_false(is_valid, "Short token rejected")
290
-
291
- # Valid token but wrong origin
292
- is_valid, _ = ConfigService.validate_csrf_token(
293
- "valid_token_12345",
294
- referer="https://evil.com/",
295
- origin="https://evil.com"
296
- )
297
- self.assert_false(is_valid, "Wrong origin rejected")
298
-
299
- def test_content_length_validation(self):
300
- """Test content length limits"""
301
- print("\n📏 Testing Content Length Validation")
302
-
303
- # Valid content length
304
- is_valid, _ = ConfigService.validate_request_content_length("1024")
305
- self.assert_true(is_valid, "Valid content length accepted")
306
-
307
- # Too large
308
- is_valid, _ = ConfigService.validate_request_content_length(str(2 * 1024 * 1024))
309
- self.assert_false(is_valid, "Oversized content rejected")
310
-
311
- # Missing
312
- is_valid, _ = ConfigService.validate_request_content_length(None)
313
- self.assert_false(is_valid, "Missing content length rejected")
314
-
315
- # Invalid format
316
- is_valid, _ = ConfigService.validate_request_content_length("invalid")
317
- self.assert_false(is_valid, "Invalid content length rejected")
318
-
319
-
320
- def run_tests():
321
- """Run all tests and return exit code"""
322
- tester = TestConfigService()
323
- success = tester.run_all_tests()
324
- return 0 if success else 1
325
-
326
-
327
- if __name__ == '__main__':
328
- sys.exit(run_tests())