planar 0.10.0__py3-none-any.whl → 0.11.0__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 (60) hide show
  1. planar/app.py +18 -6
  2. planar/routers/info.py +79 -36
  3. planar/scaffold_templates/pyproject.toml.j2 +1 -1
  4. planar/testing/fixtures.py +7 -4
  5. {planar-0.10.0.dist-info → planar-0.11.0.dist-info}/METADATA +9 -1
  6. {planar-0.10.0.dist-info → planar-0.11.0.dist-info}/RECORD +8 -60
  7. planar/ai/test_agent_serialization.py +0 -229
  8. planar/ai/test_agent_tool_step_display.py +0 -78
  9. planar/data/test_dataset.py +0 -358
  10. planar/files/storage/test_azure_blob.py +0 -435
  11. planar/files/storage/test_local_directory.py +0 -162
  12. planar/files/storage/test_s3.py +0 -299
  13. planar/files/test_files.py +0 -282
  14. planar/human/test_human.py +0 -385
  15. planar/logging/test_formatter.py +0 -327
  16. planar/modeling/mixins/test_auditable.py +0 -97
  17. planar/modeling/mixins/test_timestamp.py +0 -134
  18. planar/modeling/mixins/test_uuid_primary_key.py +0 -52
  19. planar/routers/test_agents_router.py +0 -174
  20. planar/routers/test_dataset_router.py +0 -429
  21. planar/routers/test_files_router.py +0 -49
  22. planar/routers/test_object_config_router.py +0 -367
  23. planar/routers/test_routes_security.py +0 -168
  24. planar/routers/test_rule_router.py +0 -470
  25. planar/routers/test_workflow_router.py +0 -564
  26. planar/rules/test_data/account_dormancy_management.json +0 -223
  27. planar/rules/test_data/airline_loyalty_points_calculator.json +0 -262
  28. planar/rules/test_data/applicant_risk_assessment.json +0 -435
  29. planar/rules/test_data/booking_fraud_detection.json +0 -407
  30. planar/rules/test_data/cellular_data_rollover_system.json +0 -258
  31. planar/rules/test_data/clinical_trial_eligibility_screener.json +0 -437
  32. planar/rules/test_data/customer_lifetime_value.json +0 -143
  33. planar/rules/test_data/import_duties_calculator.json +0 -289
  34. planar/rules/test_data/insurance_prior_authorization.json +0 -443
  35. planar/rules/test_data/online_check_in_eligibility_system.json +0 -254
  36. planar/rules/test_data/order_consolidation_system.json +0 -375
  37. planar/rules/test_data/portfolio_risk_monitor.json +0 -471
  38. planar/rules/test_data/supply_chain_risk.json +0 -253
  39. planar/rules/test_data/warehouse_cross_docking.json +0 -237
  40. planar/rules/test_rules.py +0 -1494
  41. planar/security/tests/test_auth_middleware.py +0 -162
  42. planar/security/tests/test_authorization_context.py +0 -78
  43. planar/security/tests/test_cedar_basics.py +0 -41
  44. planar/security/tests/test_cedar_policies.py +0 -158
  45. planar/security/tests/test_jwt_principal_context.py +0 -179
  46. planar/test_app.py +0 -142
  47. planar/test_cli.py +0 -394
  48. planar/test_config.py +0 -515
  49. planar/test_object_config.py +0 -527
  50. planar/test_object_registry.py +0 -14
  51. planar/test_sqlalchemy.py +0 -193
  52. planar/test_utils.py +0 -105
  53. planar/testing/test_memory_storage.py +0 -143
  54. planar/workflows/test_concurrency_detection.py +0 -120
  55. planar/workflows/test_lock_timeout.py +0 -140
  56. planar/workflows/test_serialization.py +0 -1203
  57. planar/workflows/test_suspend_deserialization.py +0 -231
  58. planar/workflows/test_workflow.py +0 -2005
  59. {planar-0.10.0.dist-info → planar-0.11.0.dist-info}/WHEEL +0 -0
  60. {planar-0.10.0.dist-info → planar-0.11.0.dist-info}/entry_points.txt +0 -0
planar/test_config.py DELETED
@@ -1,515 +0,0 @@
1
- import os
2
- import tempfile
3
- from pathlib import Path
4
- from unittest.mock import patch
5
-
6
- import pytest
7
-
8
- from planar.config import (
9
- JWT_COPLANE_CONFIG,
10
- LOCAL_CORS_CONFIG,
11
- PROD_CORS_CONFIG,
12
- InvalidConfigurationError,
13
- PostgreSQLConfig,
14
- SecurityConfig,
15
- SQLiteConfig,
16
- load_config,
17
- load_environment_aware_config,
18
- )
19
-
20
- VALID_CONFIG = """
21
- db_connections:
22
- sqlite_test:
23
- driver: sqlite
24
- path: /tmp/test.db
25
-
26
- pg_test1:
27
- driver: postgresql
28
- db: mydb
29
- user: user
30
-
31
- pg_test2:
32
- driver: postgresql+asyncpg
33
- host: db.example.com
34
- port: 6432
35
- user: readonly
36
- password: secret
37
- db: analytics
38
-
39
- app:
40
- db_connection: pg_test2
41
- """
42
-
43
-
44
- def test_valid_full_config():
45
- config = load_config(VALID_CONFIG)
46
-
47
- assert config.app.db_connection == "pg_test2"
48
- assert len(config.db_connections) == 3
49
-
50
- # Test SQLite config
51
- sqlite = config.db_connections["sqlite_test"]
52
- assert isinstance(sqlite, SQLiteConfig)
53
- assert str(sqlite.connection_url()) == "sqlite+aiosqlite:////tmp/test.db"
54
-
55
- # Test minimal PostgreSQL config
56
- pg1 = config.db_connections["pg_test1"]
57
- assert isinstance(pg1, PostgreSQLConfig)
58
- assert str(pg1.connection_url()) == "postgresql+asyncpg://user@/mydb"
59
-
60
- # Test full PostgreSQL config
61
- pg2 = config.db_connections["pg_test2"]
62
- assert isinstance(pg2, PostgreSQLConfig)
63
- assert pg2.port == 6432
64
- assert str(pg2.connection_url()) == (
65
- "postgresql+asyncpg://readonly:***@db.example.com:6432/analytics"
66
- )
67
-
68
- # Test selected connection
69
- assert config.connection_url() == pg2.connection_url()
70
-
71
-
72
- def test_missing_required_fields():
73
- config_yaml = """
74
- db_connections:
75
- invalid:
76
- driver: sqlite
77
- # missing path
78
- app:
79
- db_connection: invalid
80
- """
81
-
82
- with pytest.raises(InvalidConfigurationError) as excinfo:
83
- load_config(config_yaml)
84
-
85
- assert "path" in str(excinfo.value)
86
-
87
-
88
- def test_invalid_driver():
89
- config_yaml = """
90
- db_connections:
91
- invalid:
92
- driver: mysql
93
- database: test
94
- app:
95
- db_connection: invalid
96
- """
97
-
98
- with pytest.raises(InvalidConfigurationError) as excinfo:
99
- load_config(config_yaml)
100
-
101
- assert "driver" in str(excinfo.value)
102
-
103
-
104
- def test_invalid_port_type():
105
- config_yaml = """
106
- db_connections:
107
- invalid:
108
- driver: postgresql
109
- port: 'not-an-int'
110
- db: test
111
- app:
112
- db_connection: invalid
113
- """
114
-
115
- with pytest.raises(InvalidConfigurationError) as excinfo:
116
- load_config(config_yaml)
117
-
118
- assert "port" in str(excinfo.value)
119
-
120
-
121
- def test_missing_db_connection_reference():
122
- config_yaml = """
123
- db_connections:
124
- exists:
125
- driver: sqlite
126
- path: test.db
127
- app:
128
- db_connection: missing
129
- """
130
-
131
- with pytest.raises(InvalidConfigurationError) as excinfo:
132
- load_config(config_yaml)
133
-
134
- assert "missing" in str(excinfo.value)
135
-
136
-
137
- def test_empty_config():
138
- with pytest.raises(InvalidConfigurationError):
139
- load_config("")
140
-
141
-
142
- def test_invalid_yaml_syntax():
143
- invalid_yaml = """
144
- db_connections
145
- invalid: yaml
146
- """
147
-
148
- with pytest.raises(InvalidConfigurationError) as excinfo:
149
- load_config(invalid_yaml)
150
-
151
- assert "mapping values are not allowed here" in str(excinfo.value)
152
-
153
-
154
- def test_postgresql_minimal_config():
155
- config_yaml = """
156
- db_connections:
157
- minimal_pg:
158
- driver: postgresql
159
- db: essential
160
- app:
161
- db_connection: minimal_pg
162
- """
163
-
164
- config = load_config(config_yaml)
165
- pg = config.db_connections["minimal_pg"]
166
- assert isinstance(pg, PostgreSQLConfig)
167
- assert pg.host is None
168
- assert pg.port is None
169
- assert pg.user is None
170
- assert pg.password is None
171
- assert str(pg.connection_url()) == "postgresql+asyncpg:///essential"
172
-
173
-
174
- def test_connection_priorities():
175
- config_yaml = """
176
- db_connections:
177
- pg_env:
178
- driver: postgresql+asyncpg
179
- host: localhost
180
- db: testdb
181
- app:
182
- db_connection: pg_env
183
- """
184
-
185
- config = load_config(config_yaml)
186
- conn_str = str(config.connection_url())
187
- assert "asyncpg" in conn_str
188
- assert "localhost" in conn_str
189
- assert "testdb" in conn_str
190
-
191
-
192
- def test_config_with_extra_fields():
193
- config_yaml = """
194
- db_connections:
195
- with_extras:
196
- driver: sqlite
197
- path: data.db
198
- unknown_field: should_be_ignored
199
- app:
200
- db_connection: with_extras
201
- extra_setting: 123
202
- """
203
-
204
- config = load_config(config_yaml)
205
- # Should parse without errors and ignore extra fields
206
- assert config.app.db_connection == "with_extras"
207
-
208
-
209
- @pytest.fixture
210
- def temp_dir():
211
- """Create a temporary directory for testing."""
212
- with tempfile.TemporaryDirectory() as temp_dir:
213
- yield Path(temp_dir)
214
-
215
-
216
- @pytest.fixture
217
- def temp_config_file(temp_dir):
218
- """Create a temporary config file for testing."""
219
- config_file = temp_dir / "test_config.yaml"
220
- yield str(config_file)
221
-
222
-
223
- @pytest.fixture
224
- def override_config_content():
225
- """Sample override config content for testing."""
226
- return """
227
- db_connections:
228
- custom_db:
229
- driver: postgresql
230
- host: custom.example.com
231
- port: 5433
232
- user: custom_user
233
- password: custom_pass
234
- db: custom_db
235
-
236
- app:
237
- db_connection: custom_db
238
-
239
- security:
240
- cors:
241
- allow_origins: ["https://custom.example.com"]
242
- allow_credentials: true
243
- allow_methods: ["GET", "POST"]
244
- allow_headers: ["Authorization"]
245
- """
246
-
247
-
248
- def test_load_environment_aware_config_dev_default():
249
- """Test loading default dev configuration."""
250
- with patch.dict(os.environ, {"PLANAR_ENV": "dev", "PLANAR_CONFIG": ""}, clear=True):
251
- config = load_environment_aware_config()
252
-
253
- assert config.environment == "dev"
254
- assert config.security.cors == LOCAL_CORS_CONFIG
255
- assert config.security.jwt is None
256
- assert config.app.db_connection == "app"
257
- assert isinstance(config.db_connections["app"], SQLiteConfig)
258
- assert config.db_connections["app"].path == "planar_dev.db"
259
-
260
-
261
- def test_load_environment_aware_config_prod_default():
262
- """Test loading default prod configuration."""
263
- with patch.dict(
264
- os.environ, {"PLANAR_ENV": "prod", "PLANAR_CONFIG": ""}, clear=False
265
- ):
266
- config = load_environment_aware_config()
267
-
268
- assert config.environment == "prod"
269
- assert config.security == SecurityConfig(
270
- cors=PROD_CORS_CONFIG, jwt=JWT_COPLANE_CONFIG
271
- )
272
- assert config.app.db_connection == "app"
273
- assert isinstance(config.db_connections["app"], SQLiteConfig)
274
- assert config.db_connections["app"].path == "planar.db"
275
-
276
-
277
- def test_load_environment_aware_config_with_explicit_config_path(
278
- temp_config_file, override_config_content
279
- ):
280
- """Test loading config with explicit PLANAR_CONFIG path."""
281
- # Write override config to temp file
282
- with open(temp_config_file, "w") as f:
283
- f.write(override_config_content)
284
-
285
- with patch.dict(
286
- os.environ,
287
- {"PLANAR_CONFIG": temp_config_file, "PLANAR_ENV": "dev"},
288
- clear=False,
289
- ):
290
- config = load_environment_aware_config()
291
-
292
- # Should use override config
293
- assert config.app.db_connection == "custom_db"
294
- assert isinstance(config.db_connections["custom_db"], PostgreSQLConfig)
295
- assert config.db_connections["custom_db"].host == "custom.example.com"
296
- assert config.db_connections["custom_db"].port == 5433
297
-
298
-
299
- def test_load_environment_aware_config_with_env_var_expansion(temp_config_file):
300
- """Test environment variable expansion in config files."""
301
- config_with_env_vars = """
302
- db_connections:
303
- env_db:
304
- driver: postgresql
305
- host: ${DB_HOST}
306
- port: ${DB_PORT}
307
- user: ${DB_USER}
308
- password: ${DB_PASSWORD}
309
- db: ${DB_NAME}
310
-
311
- app:
312
- db_connection: env_db
313
- """
314
-
315
- with open(temp_config_file, "w") as f:
316
- f.write(config_with_env_vars)
317
-
318
- env_vars = {
319
- "PLANAR_CONFIG": temp_config_file,
320
- "PLANAR_ENV": "dev",
321
- "DB_HOST": "env.example.com",
322
- "DB_PORT": "5434",
323
- "DB_USER": "env_user",
324
- "DB_PASSWORD": "env_pass",
325
- "DB_NAME": "env_db",
326
- }
327
-
328
- with patch.dict(os.environ, env_vars, clear=False):
329
- config = load_environment_aware_config()
330
-
331
- db_config = config.db_connections["env_db"]
332
- assert isinstance(db_config, PostgreSQLConfig)
333
- assert db_config.host == "env.example.com"
334
- assert db_config.port == 5434
335
- assert db_config.user == "env_user"
336
- assert db_config.password == "env_pass"
337
- assert db_config.db == "env_db"
338
-
339
-
340
- def test_load_environment_aware_config_missing_explicit_file():
341
- """Test error when explicit config file doesn't exist."""
342
- non_existent_path = "/path/that/does/not/exist.yaml"
343
-
344
- with patch.dict(os.environ, {"PLANAR_CONFIG": non_existent_path}, clear=False):
345
- with pytest.raises(InvalidConfigurationError) as excinfo:
346
- load_environment_aware_config()
347
-
348
- assert "Configuration file not found" in str(excinfo.value)
349
- assert non_existent_path in str(excinfo.value)
350
-
351
-
352
- def test_load_environment_aware_config_invalid_yaml(temp_config_file):
353
- """Test error handling for invalid YAML in override config."""
354
- invalid_yaml = """
355
- db_connections:
356
- invalid
357
- driver: sqlite
358
- """
359
-
360
- with open(temp_config_file, "w") as f:
361
- f.write(invalid_yaml)
362
-
363
- with patch.dict(os.environ, {"PLANAR_CONFIG": temp_config_file}, clear=False):
364
- with pytest.raises(InvalidConfigurationError) as excinfo:
365
- load_environment_aware_config()
366
-
367
- assert "Error parsing override configuration file" in str(excinfo.value)
368
-
369
-
370
- def test_load_environment_aware_config_partial_override(temp_config_file):
371
- """Test that partial override configs merge correctly with defaults."""
372
- partial_override = """
373
- security:
374
- cors:
375
- allow_origins: ["https://custom.example.com"]
376
- """
377
-
378
- with open(temp_config_file, "w") as f:
379
- f.write(partial_override)
380
-
381
- with patch.dict(
382
- os.environ,
383
- {"PLANAR_CONFIG": temp_config_file, "PLANAR_ENV": "dev"},
384
- clear=False,
385
- ):
386
- config = load_environment_aware_config()
387
-
388
- # Should override specific fields
389
- assert config.security.cors.allow_origins == ["https://custom.example.com"]
390
-
391
- # Should keep defaults for non-overridden fields
392
- assert config.app.db_connection == "app" # default
393
- assert config.security.cors.allow_credentials # from LOCAL_CORS_CONFIG
394
- assert config.environment == "dev"
395
- assert isinstance(config.db_connections["app"], SQLiteConfig)
396
-
397
-
398
- def test_load_environment_aware_config_with_entry_point_path(
399
- temp_dir, override_config_content
400
- ):
401
- """Test config file discovery using PLANAR_ENTRY_POINT."""
402
- entry_point_path = temp_dir / "app.py"
403
- config_path = temp_dir / "planar.dev.yaml"
404
-
405
- # Create the config file in the entry point directory
406
- with open(config_path, "w") as f:
407
- f.write(override_config_content)
408
-
409
- env_vars = {"PLANAR_ENTRY_POINT": str(entry_point_path), "PLANAR_ENV": "dev"}
410
-
411
- # Clear PLANAR_CONFIG to test entry point discovery
412
- with patch.dict(os.environ, env_vars, clear=False):
413
- if "PLANAR_CONFIG" in os.environ:
414
- del os.environ["PLANAR_CONFIG"]
415
-
416
- config = load_environment_aware_config()
417
-
418
- # Should find and use the config file from entry point directory
419
- assert config.app.db_connection == "custom_db"
420
-
421
-
422
- def test_load_environment_aware_config_with_env_dev_file(temp_dir):
423
- """Test loading environment variables from .env.dev file."""
424
- entry_point_path = temp_dir / "app.py"
425
- env_dev_path = temp_dir / ".env.dev"
426
-
427
- # Create .env.dev file with test variables
428
- env_content = """
429
- DB_HOST=dev.example.com
430
- DB_PORT=5433
431
- DB_USER=dev_user
432
- DB_PASSWORD=dev_secret
433
- """
434
- with open(env_dev_path, "w") as f:
435
- f.write(env_content)
436
-
437
- # Create a config file that uses these env vars
438
- config_content = """
439
- db_connections:
440
- app:
441
- driver: postgresql
442
- host: ${DB_HOST}
443
- port: ${DB_PORT}
444
- user: ${DB_USER}
445
- password: ${DB_PASSWORD}
446
- db: test_db
447
- """
448
- config_path = temp_dir / "planar.dev.yaml"
449
- with open(config_path, "w") as f:
450
- f.write(config_content)
451
-
452
- env_vars = {
453
- "PLANAR_ENTRY_POINT": str(entry_point_path),
454
- "PLANAR_ENV": "dev",
455
- "PLANAR_CONFIG": str(config_path),
456
- }
457
-
458
- with patch.dict(os.environ, env_vars, clear=False):
459
- config = load_environment_aware_config()
460
-
461
- # Verify that env vars from .env.dev were loaded and used
462
- db_config = config.db_connections["app"]
463
- assert isinstance(db_config, PostgreSQLConfig)
464
- assert db_config.host == "dev.example.com"
465
- assert db_config.port == 5433
466
- assert db_config.user == "dev_user"
467
- assert db_config.password == "dev_secret"
468
-
469
-
470
- def test_load_environment_aware_config_with_generic_env_file(temp_dir):
471
- """Test loading environment variables from generic .env file."""
472
- entry_point_path = temp_dir / "app.py"
473
- env_path = temp_dir / ".env"
474
-
475
- # Create .env file with test variables
476
- env_content = """
477
- DB_HOST=generic.example.com
478
- DB_PORT=5432
479
- DB_USER=generic_user
480
- DB_PASSWORD=generic_secret
481
- """
482
- with open(env_path, "w") as f:
483
- f.write(env_content)
484
-
485
- # Create a config file that uses these env vars
486
- config_content = """
487
- db_connections:
488
- app:
489
- driver: postgresql
490
- host: ${DB_HOST}
491
- port: ${DB_PORT}
492
- user: ${DB_USER}
493
- password: ${DB_PASSWORD}
494
- db: test_db
495
- """
496
- config_path = temp_dir / "planar.yaml"
497
- with open(config_path, "w") as f:
498
- f.write(config_content)
499
-
500
- env_vars = {
501
- "PLANAR_ENTRY_POINT": str(entry_point_path),
502
- "PLANAR_ENV": "prod", # Using prod to test generic .env fallback
503
- "PLANAR_CONFIG": str(config_path),
504
- }
505
-
506
- with patch.dict(os.environ, env_vars, clear=False):
507
- config = load_environment_aware_config()
508
-
509
- # Verify that env vars from .env were loaded and used
510
- db_config = config.db_connections["app"]
511
- assert isinstance(db_config, PostgreSQLConfig)
512
- assert db_config.host == "generic.example.com"
513
- assert db_config.port == 5432
514
- assert db_config.user == "generic_user"
515
- assert db_config.password == "generic_secret"