@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.
- package/README.md +305 -52
- package/cli/dist/commands/doctor.js +62 -0
- package/cli/dist/commands/runtime.js +6 -3
- package/cli/dist/generator/template-engine.js +7 -4
- package/cli/dist/utils/runtime-layout.js +7 -0
- package/cli/dist-bundle/{chunk-QQEV6D74.js → chunk-3XODDLRJ.js} +1 -1
- package/cli/dist-bundle/{chunk-VMB5673O.js → chunk-43XWL2PB.js} +30 -26
- package/cli/dist-bundle/{chunk-5EZ4EX6U.js → chunk-53L2JN7G.js} +1 -1
- package/cli/dist-bundle/{chunk-BWK6MPCB.js → chunk-5F7YP5IR.js} +1 -1
- package/cli/dist-bundle/{chunk-36SOQVAL.js → chunk-5QBYM33Q.js} +1 -1
- package/cli/dist-bundle/{chunk-HEMPQKD6.js → chunk-DU2BN46C.js} +1 -1
- package/cli/dist-bundle/{chunk-ICXF6PXA.js → chunk-ETKUVKJ2.js} +2 -2
- package/cli/dist-bundle/{chunk-PVBAGX5X.js → chunk-I7TGIK5N.js} +1 -1
- package/cli/dist-bundle/{chunk-B6Q5VKL7.js → chunk-IGN5UC3P.js} +1 -1
- package/cli/dist-bundle/chunk-MWOVYIBB.js +54 -0
- package/cli/dist-bundle/{chunk-D7N2GGJH.js → chunk-YHYRGWUH.js} +1 -1
- package/cli/dist-bundle/{chunk-VHFFSBVJ.js → chunk-YNCBBJ4V.js} +1 -1
- package/cli/dist-bundle/cli.js +153 -153
- package/cli/dist-bundle/{cold-preflight-ABU26DYW.js → cold-preflight-ZOPR7PWJ.js} +1 -1
- package/cli/dist-bundle/{demo-stage-EBIUCJBX.js → demo-stage-OHNTP2UJ.js} +1 -1
- package/cli/dist-bundle/{open-2KSAF6M2.js → open-XROJ6YEY.js} +1 -1
- package/cli/dist-bundle/project-7PTIOHGF.js +2 -0
- package/cli/dist-bundle/{services-V6Q55JR5.js → services-BIMF6MVU.js} +1 -1
- package/cli/dist-bundle/{tilt-port-owner-MWJ7P6NB.js → tilt-port-owner-ZY5M2JC4.js} +1 -1
- package/cli/package.json +1 -1
- package/package.json +12 -2
- package/runtime/platform/services/platform/proxy/service.yaml +14 -0
- package/cli/dist/commands/__tests__/project-e2e-helpers.js +0 -49
- package/cli/dist/commands/__tests__/ui-loading-screen.test.js +0 -16
- package/cli/dist/generator/__tests__/golden-helpers.js +0 -43
- package/cli/dist-bundle/chunk-763VK575.js +0 -54
- package/cli/dist-bundle/project-YD3C4ZV3.js +0 -2
- package/runtime/discovery/test_resource_snapshot.py +0 -253
- package/runtime/engine/assets/docker/wake-gateway/decide.test.ts +0 -177
- package/runtime/engine/topologies/tilt/manifest/tests/test_integration.star +0 -278
- package/runtime/engine/topologies/tilt/manifest/tests/test_loader_parser.star +0 -475
- package/runtime/engine/topologies/tilt/manifest/tests/test_manifest.star +0 -319
- package/runtime/engine/topologies/tilt/manifest/tests/test_validator.star +0 -472
- package/runtime/ext/ide-components/shared/test_config_service.py +0 -328
- /package/{shared-platform-engineering → runtime/shared-platform-engineering}/docker-templates/bun-hoisted-symlink-fix.sh +0 -0
- /package/{shared-platform-engineering → runtime/shared-platform-engineering}/docker-templates/infisical-entrypoint.sh +0 -0
- /package/{shared-platform-engineering → runtime/shared-platform-engineering}/docker-templates/install-deps.sh +0 -0
- /package/{shared-platform-engineering → runtime/shared-platform-engineering}/docker-templates/prisma-bun-client-link-fix.sh +0 -0
- /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())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|