moose-lib 0.6.90__py3-none-any.whl → 0.6.283__py3-none-any.whl

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 (59) hide show
  1. moose_lib/__init__.py +38 -3
  2. moose_lib/blocks.py +497 -37
  3. moose_lib/clients/redis_client.py +26 -14
  4. moose_lib/commons.py +94 -5
  5. moose_lib/config/config_file.py +44 -2
  6. moose_lib/config/runtime.py +137 -5
  7. moose_lib/data_models.py +451 -46
  8. moose_lib/dmv2/__init__.py +88 -60
  9. moose_lib/dmv2/_registry.py +3 -1
  10. moose_lib/dmv2/_source_capture.py +37 -0
  11. moose_lib/dmv2/consumption.py +55 -32
  12. moose_lib/dmv2/ingest_api.py +9 -2
  13. moose_lib/dmv2/ingest_pipeline.py +56 -13
  14. moose_lib/dmv2/life_cycle.py +3 -1
  15. moose_lib/dmv2/materialized_view.py +24 -14
  16. moose_lib/dmv2/moose_model.py +165 -0
  17. moose_lib/dmv2/olap_table.py +304 -119
  18. moose_lib/dmv2/registry.py +28 -3
  19. moose_lib/dmv2/sql_resource.py +16 -8
  20. moose_lib/dmv2/stream.py +241 -21
  21. moose_lib/dmv2/types.py +14 -8
  22. moose_lib/dmv2/view.py +13 -6
  23. moose_lib/dmv2/web_app.py +175 -0
  24. moose_lib/dmv2/web_app_helpers.py +96 -0
  25. moose_lib/dmv2/workflow.py +37 -9
  26. moose_lib/internal.py +537 -68
  27. moose_lib/main.py +87 -56
  28. moose_lib/query_builder.py +18 -5
  29. moose_lib/query_param.py +54 -20
  30. moose_lib/secrets.py +122 -0
  31. moose_lib/streaming/streaming_function_runner.py +266 -156
  32. moose_lib/utilities/sql.py +0 -1
  33. {moose_lib-0.6.90.dist-info → moose_lib-0.6.283.dist-info}/METADATA +19 -1
  34. moose_lib-0.6.283.dist-info/RECORD +63 -0
  35. tests/__init__.py +1 -1
  36. tests/conftest.py +38 -1
  37. tests/test_backward_compatibility.py +85 -0
  38. tests/test_cluster_validation.py +85 -0
  39. tests/test_codec.py +75 -0
  40. tests/test_column_formatting.py +80 -0
  41. tests/test_fixedstring.py +43 -0
  42. tests/test_iceberg_config.py +105 -0
  43. tests/test_int_types.py +211 -0
  44. tests/test_kafka_config.py +141 -0
  45. tests/test_materialized.py +74 -0
  46. tests/test_metadata.py +37 -0
  47. tests/test_moose.py +21 -30
  48. tests/test_moose_model.py +153 -0
  49. tests/test_olap_table_moosemodel.py +89 -0
  50. tests/test_olap_table_versioning.py +210 -0
  51. tests/test_query_builder.py +97 -9
  52. tests/test_redis_client.py +10 -3
  53. tests/test_s3queue_config.py +211 -110
  54. tests/test_secrets.py +239 -0
  55. tests/test_simple_aggregate.py +114 -0
  56. tests/test_web_app.py +227 -0
  57. moose_lib-0.6.90.dist-info/RECORD +0 -42
  58. {moose_lib-0.6.90.dist-info → moose_lib-0.6.283.dist-info}/WHEEL +0 -0
  59. {moose_lib-0.6.90.dist-info → moose_lib-0.6.283.dist-info}/top_level.txt +0 -0
tests/test_secrets.py ADDED
@@ -0,0 +1,239 @@
1
+ """Tests for the moose_lib.secrets module.
2
+
3
+ This module tests the runtime environment variable marker functionality,
4
+ which allows users to defer secret resolution until runtime rather than
5
+ embedding secrets at build time.
6
+ """
7
+
8
+ import os
9
+ import pytest
10
+ from moose_lib.secrets import moose_runtime_env, get, MOOSE_RUNTIME_ENV_PREFIX
11
+
12
+
13
+ @pytest.fixture(scope="module", autouse=True)
14
+ def set_infra_map_loading_for_secrets_tests():
15
+ """Set IS_LOADING_INFRA_MAP=true for secrets tests so moose_runtime_env.get() returns markers."""
16
+ os.environ["IS_LOADING_INFRA_MAP"] = "true"
17
+ yield
18
+ # Clean up after all tests in this module
19
+ os.environ.pop("IS_LOADING_INFRA_MAP", None)
20
+
21
+
22
+ class TestMooseRuntimeEnvGet:
23
+ """Tests for the moose_runtime_env.get() method."""
24
+
25
+ def test_creates_marker_with_correct_prefix(self):
26
+ """Should create a marker string with the correct prefix."""
27
+ var_name = "AWS_ACCESS_KEY_ID"
28
+ result = moose_runtime_env.get(var_name)
29
+
30
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
31
+ assert result == "__MOOSE_RUNTIME_ENV__:AWS_ACCESS_KEY_ID"
32
+
33
+ def test_handles_different_variable_names(self):
34
+ """Should handle different environment variable names correctly."""
35
+ test_cases = [
36
+ "AWS_SECRET_ACCESS_KEY",
37
+ "DATABASE_PASSWORD",
38
+ "API_KEY",
39
+ "MY_CUSTOM_SECRET",
40
+ ]
41
+
42
+ for var_name in test_cases:
43
+ result = moose_runtime_env.get(var_name)
44
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
45
+ assert var_name in result
46
+
47
+ def test_raises_error_for_empty_string(self):
48
+ """Should raise ValueError for empty string."""
49
+ with pytest.raises(
50
+ ValueError, match="Environment variable name cannot be empty"
51
+ ):
52
+ moose_runtime_env.get("")
53
+
54
+ def test_raises_error_for_whitespace_only(self):
55
+ """Should raise ValueError for whitespace-only string."""
56
+ with pytest.raises(
57
+ ValueError, match="Environment variable name cannot be empty"
58
+ ):
59
+ moose_runtime_env.get(" ")
60
+
61
+ def test_raises_error_for_tabs_only(self):
62
+ """Should raise ValueError for string with only tabs."""
63
+ with pytest.raises(
64
+ ValueError, match="Environment variable name cannot be empty"
65
+ ):
66
+ moose_runtime_env.get("\t\t")
67
+
68
+ def test_allows_underscores_in_variable_names(self):
69
+ """Should allow variable names with underscores."""
70
+ var_name = "MY_LONG_VAR_NAME"
71
+ result = moose_runtime_env.get(var_name)
72
+
73
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
74
+
75
+ def test_allows_numbers_in_variable_names(self):
76
+ """Should allow variable names with numbers."""
77
+ var_name = "API_KEY_123"
78
+ result = moose_runtime_env.get(var_name)
79
+
80
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
81
+
82
+ def test_preserves_exact_casing(self):
83
+ """Should preserve exact variable name casing."""
84
+ var_name = "MixedCase_VarName"
85
+ result = moose_runtime_env.get(var_name)
86
+
87
+ assert var_name in result
88
+ assert var_name.lower() not in result # Ensure casing wasn't changed
89
+
90
+ def test_can_be_used_in_s3queue_config(self):
91
+ """Should create markers that can be used in S3Queue configuration."""
92
+ access_key_marker = moose_runtime_env.get("AWS_ACCESS_KEY_ID")
93
+ secret_key_marker = moose_runtime_env.get("AWS_SECRET_ACCESS_KEY")
94
+
95
+ config = {
96
+ "aws_access_key_id": access_key_marker,
97
+ "aws_secret_access_key": secret_key_marker,
98
+ }
99
+
100
+ assert "AWS_ACCESS_KEY_ID" in config["aws_access_key_id"]
101
+ assert "AWS_SECRET_ACCESS_KEY" in config["aws_secret_access_key"]
102
+
103
+
104
+ class TestModuleLevelGetFunction:
105
+ """Tests for the module-level get() function."""
106
+
107
+ def test_module_level_get_creates_marker(self):
108
+ """The module-level get function should create markers."""
109
+ var_name = "TEST_SECRET"
110
+ result = get(var_name)
111
+
112
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
113
+
114
+ def test_module_level_get_matches_class_method(self):
115
+ """Module-level get should produce same result as class method."""
116
+ var_name = "MY_SECRET"
117
+
118
+ result_module = get(var_name)
119
+ result_class = moose_runtime_env.get(var_name)
120
+
121
+ assert result_module == result_class
122
+
123
+ def test_module_level_get_raises_error_for_empty(self):
124
+ """Module-level get should raise ValueError for empty string."""
125
+ with pytest.raises(
126
+ ValueError, match="Environment variable name cannot be empty"
127
+ ):
128
+ get("")
129
+
130
+
131
+ class TestMooseRuntimeEnvPrefix:
132
+ """Tests for the MOOSE_RUNTIME_ENV_PREFIX constant."""
133
+
134
+ def test_has_expected_value(self):
135
+ """Should have the expected prefix value."""
136
+ assert MOOSE_RUNTIME_ENV_PREFIX == "__MOOSE_RUNTIME_ENV__:"
137
+
138
+ def test_is_string(self):
139
+ """Should be a string."""
140
+ assert isinstance(MOOSE_RUNTIME_ENV_PREFIX, str)
141
+
142
+ def test_is_not_empty(self):
143
+ """Should not be empty."""
144
+ assert len(MOOSE_RUNTIME_ENV_PREFIX) > 0
145
+
146
+
147
+ class TestMarkerFormatValidation:
148
+ """Tests for marker format validation and parsing."""
149
+
150
+ def test_creates_easily_detectable_markers(self):
151
+ """Should create markers that are easily detectable."""
152
+ marker = moose_runtime_env.get("TEST_VAR")
153
+
154
+ assert marker.startswith("__MOOSE_RUNTIME_ENV__:")
155
+
156
+ def test_markers_can_be_split_to_extract_variable_name(self):
157
+ """Should create markers that can be split to extract variable name."""
158
+ var_name = "MY_SECRET"
159
+ marker = moose_runtime_env.get(var_name)
160
+
161
+ parts = marker.split(MOOSE_RUNTIME_ENV_PREFIX)
162
+ assert len(parts) == 2
163
+ assert parts[1] == var_name
164
+
165
+ def test_markers_are_json_serializable(self):
166
+ """Should create markers that are JSON serializable."""
167
+ import json
168
+
169
+ marker = moose_runtime_env.get("TEST_VAR")
170
+ json_str = json.dumps({"secret": marker})
171
+ parsed = json.loads(json_str)
172
+
173
+ assert parsed["secret"] == marker
174
+
175
+ def test_markers_work_with_dict_serialization(self):
176
+ """Should work correctly with dictionary serialization."""
177
+ marker = moose_runtime_env.get("DATABASE_PASSWORD")
178
+
179
+ config = {"password": marker, "other_field": "value"}
180
+
181
+ # Verify the marker is preserved in the dict
182
+ assert config["password"] == marker
183
+ assert MOOSE_RUNTIME_ENV_PREFIX in config["password"]
184
+
185
+
186
+ class TestIntegrationScenarios:
187
+ """Integration tests for real-world usage scenarios."""
188
+
189
+ def test_s3queue_engine_with_secrets(self):
190
+ """Should work correctly in S3Queue engine configuration."""
191
+ from moose_lib.blocks import S3QueueEngine
192
+
193
+ engine = S3QueueEngine(
194
+ s3_path="s3://my-bucket/data/*.json",
195
+ format="JSONEachRow",
196
+ aws_access_key_id=moose_runtime_env.get("AWS_ACCESS_KEY_ID"),
197
+ aws_secret_access_key=moose_runtime_env.get("AWS_SECRET_ACCESS_KEY"),
198
+ )
199
+
200
+ # Verify markers were set correctly
201
+ assert engine.aws_access_key_id == "__MOOSE_RUNTIME_ENV__:AWS_ACCESS_KEY_ID"
202
+ assert (
203
+ engine.aws_secret_access_key
204
+ == "__MOOSE_RUNTIME_ENV__:AWS_SECRET_ACCESS_KEY"
205
+ )
206
+
207
+ def test_multiple_secrets_in_same_config(self):
208
+ """Should handle multiple secrets in the same configuration."""
209
+ config = {
210
+ "username": moose_runtime_env.get("DB_USERNAME"),
211
+ "password": moose_runtime_env.get("DB_PASSWORD"),
212
+ "api_key": moose_runtime_env.get("API_KEY"),
213
+ }
214
+
215
+ # All should have the correct prefix
216
+ for value in config.values():
217
+ assert value.startswith(MOOSE_RUNTIME_ENV_PREFIX)
218
+
219
+ # Each should have the correct variable name
220
+ assert "DB_USERNAME" in config["username"]
221
+ assert "DB_PASSWORD" in config["password"]
222
+ assert "API_KEY" in config["api_key"]
223
+
224
+ def test_mixed_secret_and_plain_values(self):
225
+ """Should handle mix of secret markers and plain values."""
226
+ config = {
227
+ "region": "us-east-1", # Plain value
228
+ "access_key": moose_runtime_env.get("AWS_ACCESS_KEY_ID"), # Secret
229
+ "bucket": "my-bucket", # Plain value
230
+ "secret_key": moose_runtime_env.get("AWS_SECRET_ACCESS_KEY"), # Secret
231
+ }
232
+
233
+ # Plain values should be unchanged
234
+ assert config["region"] == "us-east-1"
235
+ assert config["bucket"] == "my-bucket"
236
+
237
+ # Secrets should have markers
238
+ assert MOOSE_RUNTIME_ENV_PREFIX in config["access_key"]
239
+ assert MOOSE_RUNTIME_ENV_PREFIX in config["secret_key"]
@@ -0,0 +1,114 @@
1
+ import datetime
2
+ from pydantic import BaseModel
3
+
4
+ from moose_lib import simple_aggregated, Key
5
+ from moose_lib.data_models import SimpleAggregateFunction, _to_columns
6
+
7
+
8
+ def test_simple_aggregated_helper():
9
+ """Test that simple_aggregated helper creates correct annotation"""
10
+ annotated_type = simple_aggregated("sum", int)
11
+
12
+ # Check that it's annotated
13
+ assert hasattr(annotated_type, "__metadata__")
14
+ metadata = annotated_type.__metadata__[0]
15
+
16
+ # Check metadata is SimpleAggregateFunction instance
17
+ assert isinstance(metadata, SimpleAggregateFunction)
18
+ assert metadata.agg_func == "sum"
19
+ assert metadata.arg_type == int
20
+
21
+
22
+ def test_simple_aggregate_function_to_dict():
23
+ """Test that SimpleAggregateFunction.to_dict() creates correct structure"""
24
+ func = SimpleAggregateFunction(agg_func="sum", arg_type=int)
25
+ result = func.to_dict()
26
+
27
+ assert result["functionName"] == "sum"
28
+ assert "argumentType" in result
29
+ # unless Annotated, Python int becomes `Int64`
30
+ assert result["argumentType"] == "Int64"
31
+
32
+
33
+ def test_simple_aggregate_function_to_dict_with_different_types():
34
+ """Test SimpleAggregateFunction.to_dict() with various types"""
35
+ # Test with float
36
+ func_float = SimpleAggregateFunction(agg_func="max", arg_type=float)
37
+ result_float = func_float.to_dict()
38
+ assert result_float["functionName"] == "max"
39
+ assert result_float["argumentType"] == "Float64"
40
+
41
+ # Test with str
42
+ func_str = SimpleAggregateFunction(agg_func="anyLast", arg_type=str)
43
+ result_str = func_str.to_dict()
44
+ assert result_str["functionName"] == "anyLast"
45
+ assert result_str["argumentType"] == "String"
46
+
47
+
48
+ def test_dataclass_with_simple_aggregated():
49
+ """Test that BaseModel with simple_aggregated field converts correctly"""
50
+
51
+ class TestModel(BaseModel):
52
+ date_stamp: Key[datetime.datetime]
53
+ table_name: Key[str]
54
+ row_count: simple_aggregated("sum", int)
55
+
56
+ columns = _to_columns(TestModel)
57
+
58
+ # Find the row_count column
59
+ row_count_col = next(c for c in columns if c.name == "row_count")
60
+
61
+ # Check basic type - Python int maps to "Int64" by default
62
+ assert row_count_col.data_type == "Int64"
63
+
64
+ # Check annotation
65
+ simple_agg_annotation = next(
66
+ (a for a in row_count_col.annotations if a[0] == "simpleAggregationFunction"),
67
+ None,
68
+ )
69
+ assert simple_agg_annotation is not None
70
+ assert simple_agg_annotation[1]["functionName"] == "sum"
71
+ assert simple_agg_annotation[1]["argumentType"] == "Int64"
72
+
73
+
74
+ def test_multiple_simple_aggregated_fields():
75
+ """Test BaseModel with multiple SimpleAggregateFunction fields"""
76
+
77
+ class StatsModel(BaseModel):
78
+ timestamp: Key[datetime.datetime]
79
+ total_count: simple_aggregated("sum", int)
80
+ max_value: simple_aggregated("max", int)
81
+ min_value: simple_aggregated("min", int)
82
+ last_seen: simple_aggregated("anyLast", datetime.datetime)
83
+
84
+ columns = _to_columns(StatsModel)
85
+
86
+ # Test sum
87
+ sum_col = next(c for c in columns if c.name == "total_count")
88
+ sum_annotation = next(
89
+ a for a in sum_col.annotations if a[0] == "simpleAggregationFunction"
90
+ )
91
+ assert sum_annotation[1]["functionName"] == "sum"
92
+
93
+ # Test max
94
+ max_col = next(c for c in columns if c.name == "max_value")
95
+ max_annotation = next(
96
+ a for a in max_col.annotations if a[0] == "simpleAggregationFunction"
97
+ )
98
+ assert max_annotation[1]["functionName"] == "max"
99
+
100
+ # Test min
101
+ min_col = next(c for c in columns if c.name == "min_value")
102
+ min_annotation = next(
103
+ a for a in min_col.annotations if a[0] == "simpleAggregationFunction"
104
+ )
105
+ assert min_annotation[1]["functionName"] == "min"
106
+
107
+ # Test anyLast with datetime
108
+ last_col = next(c for c in columns if c.name == "last_seen")
109
+ assert last_col.data_type == "DateTime"
110
+ last_annotation = next(
111
+ a for a in last_col.annotations if a[0] == "simpleAggregationFunction"
112
+ )
113
+ assert last_annotation[1]["functionName"] == "anyLast"
114
+ assert last_annotation[1]["argumentType"] == "DateTime"
tests/test_web_app.py ADDED
@@ -0,0 +1,227 @@
1
+ """
2
+ Unit tests for WebApp SDK functionality.
3
+ """
4
+
5
+ import pytest
6
+ from moose_lib.dmv2 import WebApp, WebAppConfig, WebAppMetadata
7
+ from moose_lib.dmv2._registry import _web_apps
8
+
9
+
10
+ # Mock FastAPI app for testing
11
+ class MockFastAPIApp:
12
+ """Mock FastAPI application for testing."""
13
+
14
+ pass
15
+
16
+
17
+ @pytest.fixture(autouse=True)
18
+ def clear_registry():
19
+ """Clear the WebApp registry before each test."""
20
+ _web_apps.clear()
21
+ yield
22
+ _web_apps.clear()
23
+
24
+
25
+ def test_webapp_basic_creation():
26
+ """Test basic WebApp creation with required mount_path."""
27
+ app = MockFastAPIApp()
28
+ config = WebAppConfig(mount_path="/test")
29
+ webapp = WebApp("test_app", app, config)
30
+
31
+ assert webapp.name == "test_app"
32
+ assert webapp.app is app
33
+ assert webapp.config.mount_path == "/test"
34
+ assert webapp.config.inject_moose_utils is True
35
+ assert "test_app" in _web_apps
36
+
37
+
38
+ def test_webapp_with_custom_mount_path():
39
+ """Test WebApp with custom mount path."""
40
+ app = MockFastAPIApp()
41
+ config = WebAppConfig(mount_path="/myapi")
42
+ webapp = WebApp("test_app", app, config)
43
+
44
+ assert webapp.config.mount_path == "/myapi"
45
+
46
+
47
+ def test_webapp_with_metadata():
48
+ """Test WebApp with metadata."""
49
+ app = MockFastAPIApp()
50
+ config = WebAppConfig(
51
+ mount_path="/api",
52
+ metadata=WebAppMetadata(description="My API"),
53
+ )
54
+
55
+ with pytest.raises(ValueError, match="cannot begin with a reserved path"):
56
+ WebApp("test_app", app, config)
57
+
58
+ # Now test with valid mount path
59
+ config.mount_path = "/myapi"
60
+ webapp = WebApp("test_app", app, config)
61
+ assert webapp.config.metadata.description == "My API"
62
+
63
+
64
+ def test_webapp_duplicate_name():
65
+ """Test that duplicate WebApp names are rejected."""
66
+ app1 = MockFastAPIApp()
67
+ app2 = MockFastAPIApp()
68
+
69
+ WebApp("test_app", app1, WebAppConfig(mount_path="/test1"))
70
+
71
+ with pytest.raises(ValueError, match="WebApp with name 'test_app' already exists"):
72
+ WebApp("test_app", app2, WebAppConfig(mount_path="/test2"))
73
+
74
+
75
+ def test_webapp_trailing_slash_validation():
76
+ """Test that trailing slashes are rejected."""
77
+ app = MockFastAPIApp()
78
+ config = WebAppConfig(mount_path="/myapi/")
79
+
80
+ with pytest.raises(ValueError, match="mountPath cannot end with a trailing slash"):
81
+ WebApp("test_app", app, config)
82
+
83
+
84
+ def test_webapp_root_path_rejected():
85
+ """Test that root path '/' is rejected to prevent overlap with reserved paths."""
86
+ app = MockFastAPIApp()
87
+ config = WebAppConfig(mount_path="/")
88
+
89
+ with pytest.raises(
90
+ ValueError,
91
+ match='mountPath cannot be "/" as it would allow routes to overlap with reserved paths',
92
+ ):
93
+ WebApp("test_app", app, config)
94
+
95
+
96
+ def test_webapp_reserved_paths():
97
+ """Test that reserved paths are rejected."""
98
+ reserved_paths = [
99
+ "/admin",
100
+ "/api",
101
+ "/consumption",
102
+ "/health",
103
+ "/ingest",
104
+ "/moose",
105
+ "/ready",
106
+ "/workflows",
107
+ ]
108
+
109
+ for path in reserved_paths:
110
+ app = MockFastAPIApp()
111
+ config = WebAppConfig(mount_path=path)
112
+
113
+ with pytest.raises(ValueError, match="cannot begin with a reserved path"):
114
+ WebApp(f"test_{path}", app, config)
115
+
116
+
117
+ def test_webapp_reserved_path_prefix():
118
+ """Test that paths starting with reserved prefixes are rejected."""
119
+ app = MockFastAPIApp()
120
+ config = WebAppConfig(mount_path="/api/v1")
121
+
122
+ with pytest.raises(ValueError, match="cannot begin with a reserved path"):
123
+ WebApp("test_app", app, config)
124
+
125
+
126
+ def test_webapp_duplicate_mount_path():
127
+ """Test that duplicate mount paths are rejected."""
128
+ app1 = MockFastAPIApp()
129
+ app2 = MockFastAPIApp()
130
+
131
+ config1 = WebAppConfig(mount_path="/myapi")
132
+ WebApp("app1", app1, config1)
133
+
134
+ config2 = WebAppConfig(mount_path="/myapi")
135
+ with pytest.raises(
136
+ ValueError, match='WebApp with mountPath "/myapi" already exists'
137
+ ):
138
+ WebApp("app2", app2, config2)
139
+
140
+
141
+ def test_webapp_different_mount_paths():
142
+ """Test that WebApps with different mount paths can coexist."""
143
+ app1 = MockFastAPIApp()
144
+ app2 = MockFastAPIApp()
145
+
146
+ WebApp("app1", app1, WebAppConfig(mount_path="/api1"))
147
+ WebApp("app2", app2, WebAppConfig(mount_path="/api2"))
148
+
149
+ assert len(_web_apps) == 2
150
+
151
+
152
+ def test_webapp_inject_moose_utils_false():
153
+ """Test WebApp with inject_moose_utils disabled."""
154
+ app = MockFastAPIApp()
155
+ config = WebAppConfig(mount_path="/test", inject_moose_utils=False)
156
+ webapp = WebApp("test_app", app, config)
157
+
158
+ assert webapp.config.inject_moose_utils is False
159
+
160
+
161
+ def test_webapp_repr():
162
+ """Test WebApp string representation."""
163
+ app = MockFastAPIApp()
164
+ webapp = WebApp("test_app", app, WebAppConfig(mount_path="/myapi"))
165
+
166
+ assert "test_app" in repr(webapp)
167
+ assert "/myapi" in repr(webapp)
168
+
169
+
170
+ def test_webapp_mount_path_required():
171
+ """Test that mount_path is required."""
172
+ app = MockFastAPIApp()
173
+
174
+ with pytest.raises(ValueError, match="mountPath is required"):
175
+ WebApp("test_app", app, WebAppConfig(mount_path=""))
176
+
177
+
178
+ def test_webapp_serialization():
179
+ """Test that WebApps can be serialized via internal.py."""
180
+ from moose_lib.internal import to_infra_map
181
+ from moose_lib.dmv2 import get_web_apps
182
+
183
+ app = MockFastAPIApp()
184
+ WebApp(
185
+ "test_app",
186
+ app,
187
+ WebAppConfig(
188
+ mount_path="/myapi", metadata=WebAppMetadata(description="Test API")
189
+ ),
190
+ )
191
+
192
+ # Verify it's in the registry
193
+ web_apps = get_web_apps()
194
+ assert "test_app" in web_apps
195
+
196
+ # Serialize to infra map
197
+ infra_map = to_infra_map()
198
+
199
+ assert "webApps" in infra_map
200
+ assert "test_app" in infra_map["webApps"]
201
+ assert infra_map["webApps"]["test_app"]["name"] == "test_app"
202
+ assert infra_map["webApps"]["test_app"]["mountPath"] == "/myapi"
203
+ assert infra_map["webApps"]["test_app"]["metadata"]["description"] == "Test API"
204
+
205
+
206
+ def test_webapp_serialization_with_mount_path():
207
+ """Test WebApp serialization with explicit mount path."""
208
+ from moose_lib.internal import to_infra_map
209
+
210
+ app = MockFastAPIApp()
211
+ WebApp("test_app", app, WebAppConfig(mount_path="/testpath"))
212
+
213
+ infra_map = to_infra_map()
214
+
215
+ assert infra_map["webApps"]["test_app"]["mountPath"] == "/testpath"
216
+
217
+
218
+ def test_webapp_serialization_no_metadata():
219
+ """Test WebApp serialization without metadata."""
220
+ from moose_lib.internal import to_infra_map
221
+
222
+ app = MockFastAPIApp()
223
+ WebApp("test_app", app, WebAppConfig(mount_path="/myapi"))
224
+
225
+ infra_map = to_infra_map()
226
+
227
+ assert infra_map["webApps"]["test_app"]["metadata"] is None
@@ -1,42 +0,0 @@
1
- moose_lib/__init__.py,sha256=RyZnXIB7-eo-pxTXvKE8tqK9IHJuI5CmBadFY0nI9n4,766
2
- moose_lib/blocks.py,sha256=78u_ufWJmObijMMjLe7pDrZtrCVkeyNBe78mKgWVsMA,9789
3
- moose_lib/commons.py,sha256=FUpRv8D3-LeGcjhcqtDyiimz5izwpCq53h50ydxC_uA,3711
4
- moose_lib/data_models.py,sha256=TMa6t9wLBKhHk1-KjCHDzt2SFS9Eo-ALnKUHEQUSkU0,10910
5
- moose_lib/dmv2_serializer.py,sha256=CL_Pvvg8tJOT8Qk6hywDNzY8MYGhMVdTOw8arZi3jng,49
6
- moose_lib/internal.py,sha256=ngUsuczYrD7Vzr_WhO23vVwnJMJAegORi5uTw3x9p7U,21139
7
- moose_lib/main.py,sha256=JWsgza52xEh25AyF61cO1ItJ8VXJHHz8j-4HG445Whg,20380
8
- moose_lib/query_builder.py,sha256=-L5p2dArBx3SBA-WZPkcCJPemKXnqJw60NHy-wn5wa4,6619
9
- moose_lib/query_param.py,sha256=kxcR09BMIsEg4o2qetjKrVu1YFRaLfMEzwzyGsKUpvA,6474
10
- moose_lib/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- moose_lib/clients/redis_client.py,sha256=BDYjJ582V-rW92qVQ4neZ9Pu7JtDNt8x8jBWApt1XUg,11895
12
- moose_lib/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- moose_lib/config/config_file.py,sha256=NyjY6YFraBel7vBk18lLkpGaPR1viKMAEv4ZldyfLIA,2585
14
- moose_lib/config/runtime.py,sha256=h4SXRn4Mlbeh02lKN7HX2Mdp3QzySnZOeSvm8G-B3ko,3857
15
- moose_lib/dmv2/__init__.py,sha256=3DVAtNMZUoP94CMJBFhuXfYEQXDbQUNKSgg9XnKqae0,2768
16
- moose_lib/dmv2/_registry.py,sha256=gsLuWOvekgQRVvjVjPRHW2hN48LWyOWpLSpS2I-CWHc,708
17
- moose_lib/dmv2/consumption.py,sha256=sWfGwgCBQIIrhFEbx1CULfoN3rFFoy8uCBUefu4ejiw,12928
18
- moose_lib/dmv2/ingest_api.py,sha256=XhvHHgGPXp-BuRpAALth-FRhanwy-zJQ_83Cg_RLolM,2586
19
- moose_lib/dmv2/ingest_pipeline.py,sha256=VOkHaCrmalBGJkWy4iI93WJ_tyaXmHRSSIHBflms6F0,7432
20
- moose_lib/dmv2/life_cycle.py,sha256=wl0k6yzwU1MJ_fO_UkN29buoY5G6ChYZvfwigP9fVfM,1254
21
- moose_lib/dmv2/materialized_view.py,sha256=wTam1V5CC2rExt7YrdK_Cz4rRTONm2keKOF951LlCP4,4875
22
- moose_lib/dmv2/olap_table.py,sha256=YY5zgMrijPnRDaEkM5DbnqSzVRcP8s2bHCFFXISrUE0,34062
23
- moose_lib/dmv2/registry.py,sha256=SZOXhSC3kizhPHKQN6ZWzoMZHl90AUG3H_WKVev5zIU,2680
24
- moose_lib/dmv2/sql_resource.py,sha256=kUZoGqxhZMHMthtBZGYJBxTFjXkspXiWLXhJRYXgGUM,1864
25
- moose_lib/dmv2/stream.py,sha256=IGrUdWBEHjZ9nYz3-TChq-x9P98V9dH6TbamQ2oz_TE,11725
26
- moose_lib/dmv2/types.py,sha256=3YZ5Kbz2eOn94GWMTYT6Ix69Ekwe6aoUR4DiETQjv9E,4633
27
- moose_lib/dmv2/view.py,sha256=fVbfbJgc2lvhjpGvpfKcFUqZqxKuLD4X59jdupxIe94,1350
28
- moose_lib/dmv2/workflow.py,sha256=_FY4-VRo7uWxRtoipxGSo04qzBb4pbP30iQei1W0Ios,6287
29
- moose_lib/streaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- moose_lib/streaming/streaming_function_runner.py,sha256=Qv3vTSowo5Y_SeGu2XxcK5NeL_ZW0Um0ZgHxHpZ9VGM,23693
31
- moose_lib/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- moose_lib/utilities/sql.py,sha256=kbg1DT5GdsIwgTsMzbsd6SAVf9aWER8DqmT_eKS3XN4,904
33
- tests/__init__.py,sha256=0Gh4yzPkkC3TzBGKhenpMIxJcRhyrrCfxLSfpTZnPMQ,53
34
- tests/conftest.py,sha256=ZVJNbnr4DwbcqkTmePW6U01zAzE6QD0kNAEZjPG1f4s,169
35
- tests/test_moose.py,sha256=mBsx_OYWmL8ppDzL_7Bd7xR6qf_i3-pCIO3wm2iQNaA,2136
36
- tests/test_query_builder.py,sha256=O3imdFSaqU13kbK1jSQaHbBgynhVmJaApT8DlRqYwJU,1116
37
- tests/test_redis_client.py,sha256=d9_MLYsJ4ecVil_jPB2gW3Q5aWnavxmmjZg2uYI3LVo,3256
38
- tests/test_s3queue_config.py,sha256=F05cnD61S2wBKPabcpEJxf55-DJGF4nLqwBb6aFbprc,9741
39
- moose_lib-0.6.90.dist-info/METADATA,sha256=HtKY83Dl-nuR8s2XqXt0jEo1e7K8bWSZmUYYshb4GLI,766
40
- moose_lib-0.6.90.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
41
- moose_lib-0.6.90.dist-info/top_level.txt,sha256=XEns2-4aCmGp2XjJAeEH9TAUcGONLnSLy6ycT9FSJh8,16
42
- moose_lib-0.6.90.dist-info/RECORD,,