kiarina-lib-google-cloud-storage 1.6.2__tar.gz → 1.6.3__tar.gz

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 (23) hide show
  1. kiarina_lib_google_cloud_storage-1.6.3/.env.sample +1 -0
  2. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/.gitignore +1 -0
  3. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/CHANGELOG.md +12 -1
  4. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/PKG-INFO +26 -25
  5. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/README.md +24 -23
  6. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/pyproject.toml +2 -2
  7. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/src/kiarina/lib/google/cloud_storage/_get_blob.py +16 -4
  8. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/src/kiarina/lib/google/cloud_storage/_get_bucket.py +1 -5
  9. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/src/kiarina/lib/google/cloud_storage/settings.py +3 -3
  10. kiarina_lib_google_cloud_storage-1.6.3/test_settings.sample.yaml +15 -0
  11. kiarina_lib_google_cloud_storage-1.6.3/tests/conftest.py +33 -0
  12. kiarina_lib_google_cloud_storage-1.6.3/tests/test_get_blob.py +82 -0
  13. kiarina_lib_google_cloud_storage-1.6.3/tests/test_get_bucket.py +18 -0
  14. kiarina_lib_google_cloud_storage-1.6.3/tests/test_get_storage_client.py +20 -0
  15. kiarina_lib_google_cloud_storage-1.6.2/tests/conftest.py +0 -0
  16. kiarina_lib_google_cloud_storage-1.6.2/tests/test_get_blob.py +0 -224
  17. kiarina_lib_google_cloud_storage-1.6.2/tests/test_get_bucket.py +0 -91
  18. kiarina_lib_google_cloud_storage-1.6.2/tests/test_get_storage_client.py +0 -57
  19. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/.vscode/settings.json +0 -0
  20. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/src/kiarina/lib/google/cloud_storage/__init__.py +0 -0
  21. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/src/kiarina/lib/google/cloud_storage/_get_storage_client.py +0 -0
  22. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/src/kiarina/lib/google/cloud_storage/py.typed +0 -0
  23. {kiarina_lib_google_cloud_storage-1.6.2 → kiarina_lib_google_cloud_storage-1.6.3}/tests/__init__.py +0 -0
@@ -0,0 +1 @@
1
+ KIARINA_LIB_GOOGLE_CLOUD_STORAGE_TEST_SETTINGS_FILE=packages/kiarina-lib-google-cloud-storage/test_settings.yaml
@@ -25,6 +25,7 @@ htmlcov/
25
25
  # Project specific
26
26
  *.log
27
27
  tmp/
28
+ packages/*/test_settings.yaml
28
29
 
29
30
  # Test data
30
31
  tests/data/large/
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.6.3] - 2025-10-13
11
+
12
+ ### Changed
13
+ - Converted tests from mock-based to real integration tests with multi-tenancy patterns
14
+ - Simplified test settings loading using `load_user_configs` from pydantic-settings-manager
15
+ - Updated `pydantic-settings-manager` dependency from `>=2.1.0` to `>=2.3.0`
16
+ - Refactored to use `settings_manager.get_settings` instead of deprecated `get_settings_by_key`
17
+
18
+ ### Added
19
+ - Added `.env.sample` and `test_settings.sample.yaml` for easier test setup
20
+
10
21
  ## [1.6.2] - 2025-10-10
11
22
 
12
23
  ### Changed
@@ -14,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14
25
  - `blob_name_pattern` supports both fixed names and template patterns with placeholders
15
26
  - `get_blob()` now accepts `placeholders` parameter for pattern formatting
16
27
  - `blob_name` parameter in `get_blob()` now always represents the full blob path
17
- - Pattern examples: `"data.json"` (fixed), `"files/{basename}"` (single placeholder), `"web/{user_id}/{agent_id}/files/{basename}"` (multiple placeholders)
28
+ - Pattern examples: `"data.json"` (fixed), `"files/{basename}"` (single placeholder), `"my-service/{tenant_id}/users/{user_id}/files/{basename}"` (multiple placeholders)
18
29
  - Priority: explicit `blob_name` → `blob_name_pattern` with `placeholders` → `blob_name_pattern` without placeholders
19
30
  - Enhanced error messages for missing placeholders
20
31
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kiarina-lib-google-cloud-storage
3
- Version: 1.6.2
3
+ Version: 1.6.3
4
4
  Summary: Google Cloud Storage client library for kiarina namespace
5
5
  Project-URL: Homepage, https://github.com/kiarina/kiarina-python
6
6
  Project-URL: Repository, https://github.com/kiarina/kiarina-python
@@ -22,7 +22,7 @@ Classifier: Typing :: Typed
22
22
  Requires-Python: >=3.12
23
23
  Requires-Dist: google-cloud-storage>=2.19.0
24
24
  Requires-Dist: kiarina-lib-google-auth>=1.4.0
25
- Requires-Dist: pydantic-settings-manager>=2.1.0
25
+ Requires-Dist: pydantic-settings-manager>=2.3.0
26
26
  Requires-Dist: pydantic-settings>=2.10.1
27
27
  Description-Content-Type: text/markdown
28
28
 
@@ -115,18 +115,18 @@ New requirement: Add agent-level isolation for multi-tenancy.
115
115
 
116
116
  **Updated Security Rules:**
117
117
  ```javascript
118
- match /web/{user_id}/{agent_id}/files/{basename} {
119
- allow read, write: if request.auth.uid == user_id
120
- && request.auth.token.agent_id == agent_id;
118
+ match /my-service/{tenant_id}/users/{user_id}/files/{basename} {
119
+ allow read, write: if request.auth.uid == user_id
120
+ && request.auth.token.tenant_id == tenant_id;
121
121
  }
122
122
  ```
123
123
 
124
124
  **Problem**: You must now update **every place** in your application code that constructs these paths:
125
125
  ```python
126
126
  # Must change all of these
127
- blob_name = f"web/{user_id}/{agent_id}/files/{file_name}" # Changed!
128
- blob_name = f"web/{user_id}/{agent_id}/thumbnails/{file_name}" # Changed!
129
- blob_name = f"web/{user_id}/{agent_id}/exports/{file_name}" # Changed!
127
+ blob_name = f"my-service/{tenant_id}/users/{user_id}/files/{file_name}" # Changed!
128
+ blob_name = f"my-service/{tenant_id}/users/{user_id}/thumbnails/{file_name}" # Changed!
129
+ blob_name = f"my-service/{tenant_id}/users/{user_id}/exports/{file_name}" # Changed!
130
130
  # ... and many more
131
131
  ```
132
132
 
@@ -138,14 +138,14 @@ blob_name = f"web/{user_id}/{agent_id}/exports/{file_name}" # Changed!
138
138
  google_cloud_storage:
139
139
  default:
140
140
  bucket_name: "my-app-data"
141
- blob_name_pattern: "web/{user_id}/{agent_id}/files/{basename}"
141
+ blob_name_pattern: "my-service/{tenant_id}/users/{user_id}/files/{basename}"
142
142
  ```
143
143
 
144
144
  **GCS Security Rules (Infrastructure Concern):**
145
145
  ```javascript
146
- match /web/{user_id}/{agent_id}/files/{basename} {
147
- allow read, write: if request.auth.uid == user_id
148
- && request.auth.token.agent_id == agent_id;
146
+ match /my-service/{tenant_id}/users/{user_id}/files/{basename} {
147
+ allow read, write: if request.auth.uid == user_id
148
+ && request.auth.token.tenant_id == tenant_id;
149
149
  }
150
150
  ```
151
151
 
@@ -368,14 +368,14 @@ def save_document(tenant_id: str, doc_id: str, content: bytes):
368
368
  def list_documents(tenant_id: str) -> list[str]:
369
369
  """List documents for any tenant"""
370
370
  from kiarina.lib.google.cloud_storage import get_bucket
371
-
371
+
372
372
  config_key = f"tenant_{tenant_id}"
373
373
  bucket = get_bucket(config_key=config_key, auth_config_key=config_key)
374
-
374
+
375
375
  # Get prefix from settings
376
- settings = settings_manager.get_settings_by_key(config_key)
376
+ settings = settings_manager.get_settings(config_key)
377
377
  prefix = f"{settings.blob_name_prefix}/documents/" if settings.blob_name_prefix else "documents/"
378
-
378
+
379
379
  blobs = bucket.list_blobs(prefix=prefix)
380
380
  return [blob.name for blob in blobs]
381
381
  ```
@@ -409,10 +409,10 @@ def mock_storage_config():
409
409
  def test_save_user_profile(mock_storage_config):
410
410
  """Test user profile saving"""
411
411
  from myapp.services import save_user_profile
412
-
412
+
413
413
  # Application code uses test configuration automatically
414
414
  save_user_profile("user123", {"name": "Alice"})
415
-
415
+
416
416
  # Verify using the same configuration
417
417
  from kiarina.lib.google.cloud_storage import get_blob
418
418
  blob = get_blob(blob_name="users/user123/profile.json")
@@ -428,20 +428,20 @@ from kiarina.lib.google.cloud_storage import settings_manager
428
428
 
429
429
  def debug_storage_config(config_key: str | None = None):
430
430
  """Show actual storage paths for debugging"""
431
- settings = settings_manager.get_settings_by_key(config_key)
432
-
431
+ settings = settings_manager.get_settings(config_key)
432
+
433
433
  print(f"Configuration: {config_key or 'default'}")
434
434
  print(f" Bucket: {settings.bucket_name}")
435
435
  print(f" Prefix: {settings.blob_name_prefix or '(none)'}")
436
436
  print(f" Auth: {settings.google_auth_config_key}")
437
-
437
+
438
438
  # Example paths
439
439
  example_blob = "users/123/profile.json"
440
440
  if settings.blob_name_prefix:
441
441
  full_path = f"{settings.blob_name_prefix}/{example_blob}"
442
442
  else:
443
443
  full_path = example_blob
444
-
444
+
445
445
  print(f" Example: gs://{settings.bucket_name}/{full_path}")
446
446
 
447
447
  # Usage
@@ -665,13 +665,14 @@ blob = get_blob()
665
665
  # Actual: gs://bucket/data/fixed.json
666
666
 
667
667
  # Complex pattern
668
- # If blob_name_pattern="web/{user_id}/{agent_id}/files/{basename}"
668
+ # If blob_name_pattern="my-service/{tenant_id}/users/{user_id}/files/{basename}"
669
669
  blob = get_blob(placeholders={
670
+ "tenant_id": "tenant123",
670
671
  "user_id": "user123",
671
672
  "agent_id": "agent456",
672
673
  "basename": "document.pdf"
673
674
  })
674
- # Actual: gs://bucket/web/user123/agent456/files/document.pdf
675
+ # Actual: gs://bucket/my-service/tenant123/users/user123/files/document.pdf
675
676
 
676
677
  # With custom configurations
677
678
  blob = get_blob(
@@ -700,7 +701,7 @@ settings_manager: SettingsManager[GoogleCloudStorageSettings]
700
701
  - `active_key`: Get/set active configuration key
701
702
 
702
703
  **Methods:**
703
- - `get_settings_by_key(key: str)`: Get settings by specific key
704
+ - `get_settings(key: str)`: Get settings by specific key
704
705
  - `clear()`: Clear cached settings
705
706
 
706
707
  ## Common Operations
@@ -87,18 +87,18 @@ New requirement: Add agent-level isolation for multi-tenancy.
87
87
 
88
88
  **Updated Security Rules:**
89
89
  ```javascript
90
- match /web/{user_id}/{agent_id}/files/{basename} {
91
- allow read, write: if request.auth.uid == user_id
92
- && request.auth.token.agent_id == agent_id;
90
+ match /my-service/{tenant_id}/users/{user_id}/files/{basename} {
91
+ allow read, write: if request.auth.uid == user_id
92
+ && request.auth.token.tenant_id == tenant_id;
93
93
  }
94
94
  ```
95
95
 
96
96
  **Problem**: You must now update **every place** in your application code that constructs these paths:
97
97
  ```python
98
98
  # Must change all of these
99
- blob_name = f"web/{user_id}/{agent_id}/files/{file_name}" # Changed!
100
- blob_name = f"web/{user_id}/{agent_id}/thumbnails/{file_name}" # Changed!
101
- blob_name = f"web/{user_id}/{agent_id}/exports/{file_name}" # Changed!
99
+ blob_name = f"my-service/{tenant_id}/users/{user_id}/files/{file_name}" # Changed!
100
+ blob_name = f"my-service/{tenant_id}/users/{user_id}/thumbnails/{file_name}" # Changed!
101
+ blob_name = f"my-service/{tenant_id}/users/{user_id}/exports/{file_name}" # Changed!
102
102
  # ... and many more
103
103
  ```
104
104
 
@@ -110,14 +110,14 @@ blob_name = f"web/{user_id}/{agent_id}/exports/{file_name}" # Changed!
110
110
  google_cloud_storage:
111
111
  default:
112
112
  bucket_name: "my-app-data"
113
- blob_name_pattern: "web/{user_id}/{agent_id}/files/{basename}"
113
+ blob_name_pattern: "my-service/{tenant_id}/users/{user_id}/files/{basename}"
114
114
  ```
115
115
 
116
116
  **GCS Security Rules (Infrastructure Concern):**
117
117
  ```javascript
118
- match /web/{user_id}/{agent_id}/files/{basename} {
119
- allow read, write: if request.auth.uid == user_id
120
- && request.auth.token.agent_id == agent_id;
118
+ match /my-service/{tenant_id}/users/{user_id}/files/{basename} {
119
+ allow read, write: if request.auth.uid == user_id
120
+ && request.auth.token.tenant_id == tenant_id;
121
121
  }
122
122
  ```
123
123
 
@@ -340,14 +340,14 @@ def save_document(tenant_id: str, doc_id: str, content: bytes):
340
340
  def list_documents(tenant_id: str) -> list[str]:
341
341
  """List documents for any tenant"""
342
342
  from kiarina.lib.google.cloud_storage import get_bucket
343
-
343
+
344
344
  config_key = f"tenant_{tenant_id}"
345
345
  bucket = get_bucket(config_key=config_key, auth_config_key=config_key)
346
-
346
+
347
347
  # Get prefix from settings
348
- settings = settings_manager.get_settings_by_key(config_key)
348
+ settings = settings_manager.get_settings(config_key)
349
349
  prefix = f"{settings.blob_name_prefix}/documents/" if settings.blob_name_prefix else "documents/"
350
-
350
+
351
351
  blobs = bucket.list_blobs(prefix=prefix)
352
352
  return [blob.name for blob in blobs]
353
353
  ```
@@ -381,10 +381,10 @@ def mock_storage_config():
381
381
  def test_save_user_profile(mock_storage_config):
382
382
  """Test user profile saving"""
383
383
  from myapp.services import save_user_profile
384
-
384
+
385
385
  # Application code uses test configuration automatically
386
386
  save_user_profile("user123", {"name": "Alice"})
387
-
387
+
388
388
  # Verify using the same configuration
389
389
  from kiarina.lib.google.cloud_storage import get_blob
390
390
  blob = get_blob(blob_name="users/user123/profile.json")
@@ -400,20 +400,20 @@ from kiarina.lib.google.cloud_storage import settings_manager
400
400
 
401
401
  def debug_storage_config(config_key: str | None = None):
402
402
  """Show actual storage paths for debugging"""
403
- settings = settings_manager.get_settings_by_key(config_key)
404
-
403
+ settings = settings_manager.get_settings(config_key)
404
+
405
405
  print(f"Configuration: {config_key or 'default'}")
406
406
  print(f" Bucket: {settings.bucket_name}")
407
407
  print(f" Prefix: {settings.blob_name_prefix or '(none)'}")
408
408
  print(f" Auth: {settings.google_auth_config_key}")
409
-
409
+
410
410
  # Example paths
411
411
  example_blob = "users/123/profile.json"
412
412
  if settings.blob_name_prefix:
413
413
  full_path = f"{settings.blob_name_prefix}/{example_blob}"
414
414
  else:
415
415
  full_path = example_blob
416
-
416
+
417
417
  print(f" Example: gs://{settings.bucket_name}/{full_path}")
418
418
 
419
419
  # Usage
@@ -637,13 +637,14 @@ blob = get_blob()
637
637
  # Actual: gs://bucket/data/fixed.json
638
638
 
639
639
  # Complex pattern
640
- # If blob_name_pattern="web/{user_id}/{agent_id}/files/{basename}"
640
+ # If blob_name_pattern="my-service/{tenant_id}/users/{user_id}/files/{basename}"
641
641
  blob = get_blob(placeholders={
642
+ "tenant_id": "tenant123",
642
643
  "user_id": "user123",
643
644
  "agent_id": "agent456",
644
645
  "basename": "document.pdf"
645
646
  })
646
- # Actual: gs://bucket/web/user123/agent456/files/document.pdf
647
+ # Actual: gs://bucket/my-service/tenant123/users/user123/files/document.pdf
647
648
 
648
649
  # With custom configurations
649
650
  blob = get_blob(
@@ -672,7 +673,7 @@ settings_manager: SettingsManager[GoogleCloudStorageSettings]
672
673
  - `active_key`: Get/set active configuration key
673
674
 
674
675
  **Methods:**
675
- - `get_settings_by_key(key: str)`: Get settings by specific key
676
+ - `get_settings(key: str)`: Get settings by specific key
676
677
  - `clear()`: Clear cached settings
677
678
 
678
679
  ## Common Operations
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kiarina-lib-google-cloud-storage"
3
- version = "1.6.2"
3
+ version = "1.6.3"
4
4
  description = "Google Cloud Storage client library for kiarina namespace"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -26,7 +26,7 @@ dependencies = [
26
26
  "google-cloud-storage>=2.19.0",
27
27
  "kiarina-lib-google-auth>=1.4.0",
28
28
  "pydantic-settings>=2.10.1",
29
- "pydantic-settings-manager>=2.1.0",
29
+ "pydantic-settings-manager>=2.3.0",
30
30
  ]
31
31
 
32
32
  [project.urls]
@@ -1,3 +1,4 @@
1
+ import re
1
2
  from typing import Any
2
3
 
3
4
  from google.cloud import storage # type: ignore[import-untyped]
@@ -38,10 +39,10 @@ def get_blob(
38
39
  blob = get_blob(placeholders={"user_id": "123", "basename": "profile.json"})
39
40
  # With pattern "users/{user_id}/{basename}" -> "users/123/profile.json"
40
41
 
41
- # Using default pattern from settings
42
- blob = get_blob() # Uses settings.blob_name_pattern without placeholders
42
+ # Using fixed pattern from settings (no placeholders)
43
+ blob = get_blob() # Uses settings.blob_name_pattern if it has no placeholders
43
44
  """
44
- settings = settings_manager.get_settings_by_key(config_key)
45
+ settings = settings_manager.get_settings(config_key)
45
46
 
46
47
  # Priority 1: Explicit blob_name
47
48
  if blob_name is not None:
@@ -65,7 +66,6 @@ def get_blob(
65
66
 
66
67
  # Priority 3: Default pattern from settings
67
68
  elif settings.blob_name_pattern is not None:
68
- # Pattern without placeholders (fixed name)
69
69
  final_blob_name = settings.blob_name_pattern
70
70
 
71
71
  else:
@@ -74,5 +74,17 @@ def get_blob(
74
74
  "and blob_name_pattern is not set in settings"
75
75
  )
76
76
 
77
+ # Safety check: Ensure no unresolved placeholders remain
78
+ if _has_placeholders(final_blob_name):
79
+ raise ValueError(
80
+ f"Unresolved placeholders found in blob name: {final_blob_name}. "
81
+ f"Please provide placeholders argument to resolve them."
82
+ )
83
+
77
84
  bucket = get_bucket(config_key, auth_config_key=auth_config_key, **kwargs)
78
85
  return bucket.blob(final_blob_name)
86
+
87
+
88
+ def _has_placeholders(pattern: str) -> bool:
89
+ """Check if a pattern contains placeholders like {key}."""
90
+ return bool(re.search(r"\{[^}]+\}", pattern))
@@ -12,10 +12,6 @@ def get_bucket(
12
12
  auth_config_key: str | None = None,
13
13
  **kwargs: Any,
14
14
  ) -> storage.Bucket:
15
- settings = settings_manager.get_settings_by_key(config_key)
16
-
17
- if settings.bucket_name is None:
18
- raise ValueError("bucket_name is not set in the settings")
19
-
15
+ settings = settings_manager.get_settings(config_key)
20
16
  client = get_storage_client(auth_config_key, **kwargs)
21
17
  return client.bucket(settings.bucket_name)
@@ -3,16 +3,16 @@ from pydantic_settings_manager import SettingsManager
3
3
 
4
4
 
5
5
  class GoogleCloudStorageSettings(BaseSettings):
6
- bucket_name: str | None = None
6
+ bucket_name: str
7
7
 
8
8
  blob_name_pattern: str | None = None
9
9
  """
10
10
  Blob name pattern with placeholders.
11
-
11
+
12
12
  Examples:
13
13
  - "data.json" (fixed name)
14
14
  - "files/{basename}" (single placeholder)
15
- - "web/{user_id}/{agent_id}/files/{basename}" (multiple placeholders)
15
+ - "my-service/{tenant_id}/users/{user_id}/files/{basename}" (multiple placeholders)
16
16
  """
17
17
 
18
18
 
@@ -0,0 +1,15 @@
1
+ kiarina.lib.google.auth:
2
+ service_account:
3
+ type: service_account
4
+ project_id: your-project-id
5
+ service_account_email: your-service-account@your-project.iam.gserviceaccount.com
6
+ service_account_file: ~/.gcp/service-account/your-project/your-service-account/key.json
7
+ kiarina.lib.google.cloud_storage:
8
+ default:
9
+ bucket_name: your-bucket-name
10
+ blob_name_pattern: "my-service/{tenant_id}/users/{user_id}/files/{basename}"
11
+ no_blob_name_pattern:
12
+ bucket_name: your-bucket-name
13
+ fixed:
14
+ bucket_name: your-bucket-name
15
+ blob_name_pattern: "my-service/kiarina-lib-google-cloud-storage/users/test_get_blob_with_fixed_pattern/files/miineko.png"
@@ -0,0 +1,33 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+ import pytest
5
+ from pydantic_settings_manager import load_user_configs
6
+
7
+ import kiarina.utils.file as kf
8
+
9
+
10
+ @pytest.fixture
11
+ def data_dir() -> Path:
12
+ return Path(__file__).parent.parent.parent.parent / "tests" / "data"
13
+
14
+
15
+ @pytest.fixture(scope="session")
16
+ def load_settings():
17
+ env_var = "KIARINA_LIB_GOOGLE_CLOUD_STORAGE_TEST_SETTINGS_FILE"
18
+
19
+ if env_var not in os.environ:
20
+ pytest.skip(f"Environment variable {env_var} not set, skipping tests.")
21
+
22
+ test_settings_file = os.environ[env_var]
23
+ test_settings_file = os.path.expanduser(test_settings_file)
24
+
25
+ if not os.path.exists(test_settings_file):
26
+ raise FileNotFoundError(f"Settings file not found: {test_settings_file}")
27
+
28
+ user_configs = kf.read_yaml_dict(test_settings_file)
29
+
30
+ if not user_configs:
31
+ raise ValueError(f"Settings file is empty or invalid: {test_settings_file}")
32
+
33
+ load_user_configs(user_configs)
@@ -0,0 +1,82 @@
1
+ import pytest
2
+
3
+ from kiarina.lib.google.cloud_storage import get_blob, settings_manager
4
+
5
+
6
+ def test_get_blob_with_blob_name(data_dir, load_settings, request):
7
+ settings = settings_manager.settings
8
+
9
+ assert settings.blob_name_pattern is not None
10
+
11
+ blob_name = settings.blob_name_pattern.format(
12
+ tenant_id="kiarina-lib-google-cloud-storage",
13
+ user_id=request.node.name,
14
+ basename="miineko.png",
15
+ )
16
+
17
+ blob = get_blob(blob_name)
18
+
19
+ if not blob.exists():
20
+ blob.upload_from_filename(data_dir / "small" / "miineko_256x256_799b.png")
21
+
22
+
23
+ def test_get_blob_with_placeholders(data_dir, load_settings, request):
24
+ blob = get_blob(
25
+ placeholders={
26
+ "tenant_id": "kiarina-lib-google-cloud-storage",
27
+ "user_id": request.node.name,
28
+ "basename": "miineko.png",
29
+ }
30
+ )
31
+
32
+ if not blob.exists():
33
+ blob.upload_from_filename(data_dir / "small" / "miineko_256x256_799b.png")
34
+
35
+
36
+ def test_get_blob_with_placeholders_but_no_pattern(data_dir, load_settings, request):
37
+ with pytest.raises(
38
+ ValueError,
39
+ match="placeholders provided but blob_name_pattern is not set in settings",
40
+ ):
41
+ get_blob(
42
+ placeholders={
43
+ "user_id": request.node.name,
44
+ "basename": "miineko.png",
45
+ },
46
+ config_key="no_blob_name_pattern",
47
+ )
48
+
49
+
50
+ def test_get_blob_with_not_enough_placeholders(data_dir, load_settings, request):
51
+ with pytest.raises(
52
+ ValueError, match="Missing placeholder 'basename' in blob_name_pattern"
53
+ ):
54
+ get_blob(
55
+ placeholders={
56
+ "tenant_id": "kiarina-lib-google-cloud-storage",
57
+ "user_id": request.node.name,
58
+ }
59
+ )
60
+
61
+
62
+ def test_get_blob_with_fixed_pattern(data_dir, load_settings, request):
63
+ blob = get_blob(config_key="fixed")
64
+
65
+ if not blob.exists():
66
+ blob.upload_from_filename(data_dir / "small" / "miineko_256x256_799b.png")
67
+
68
+
69
+ def test_not_enough_placeholders(data_dir, load_settings, request):
70
+ with pytest.raises(
71
+ ValueError,
72
+ match="Unresolved placeholders found in blob name",
73
+ ):
74
+ get_blob()
75
+
76
+
77
+ def test_no_blob_name_provided(data_dir, load_settings, request):
78
+ with pytest.raises(
79
+ ValueError,
80
+ match="blob_name is not provided, placeholders are not provided, and blob_name_pattern is not set in settings",
81
+ ):
82
+ get_blob(config_key="no_blob_name_pattern")
@@ -0,0 +1,18 @@
1
+ from kiarina.lib.google.cloud_storage import get_bucket, settings_manager
2
+
3
+
4
+ def test_get_bucket(data_dir, load_settings, request):
5
+ settings = settings_manager.settings
6
+ assert settings.blob_name_pattern is not None
7
+
8
+ blob_name = settings.blob_name_pattern.format(
9
+ tenant_id="kiarina-lib-google-cloud-storage",
10
+ user_id=request.node.name,
11
+ basename="miineko.png",
12
+ )
13
+
14
+ bucket = get_bucket()
15
+ blob = bucket.blob(blob_name)
16
+
17
+ if not blob.exists():
18
+ blob.upload_from_filename(data_dir / "small" / "miineko_256x256_799b.png")
@@ -0,0 +1,20 @@
1
+ from kiarina.lib.google.cloud_storage import settings_manager, get_storage_client
2
+
3
+
4
+ def test_get_storage_client(data_dir, load_settings, request):
5
+ settings = settings_manager.settings
6
+ assert settings.blob_name_pattern is not None
7
+
8
+ bucket_name = settings.bucket_name
9
+ blob_name = settings.blob_name_pattern.format(
10
+ tenant_id="kiarina-lib-google-cloud-storage",
11
+ user_id=request.node.name,
12
+ basename="miineko.png",
13
+ )
14
+
15
+ client = get_storage_client()
16
+ bucket = client.bucket(bucket_name)
17
+ blob = bucket.blob(blob_name)
18
+
19
+ if not blob.exists():
20
+ blob.upload_from_filename(data_dir / "small" / "miineko_256x256_799b.png")
File without changes
@@ -1,224 +0,0 @@
1
- from unittest.mock import MagicMock, patch
2
-
3
- import pytest
4
-
5
- from kiarina.lib.google.cloud_storage import get_blob, settings_manager
6
-
7
-
8
- def test_get_blob_with_blob_name():
9
- """Test get_blob with explicit blob_name parameter."""
10
- settings_manager.user_config = {
11
- "default": {
12
- "bucket_name": "test-bucket",
13
- }
14
- }
15
-
16
- mock_bucket = MagicMock()
17
- mock_blob = MagicMock()
18
- mock_blob.name = "data/file.json"
19
- mock_bucket.blob.return_value = mock_blob
20
-
21
- with patch(
22
- "kiarina.lib.google.cloud_storage._get_blob.get_bucket",
23
- return_value=mock_bucket,
24
- ):
25
- blob = get_blob(blob_name="data/file.json")
26
- assert blob.name == "data/file.json"
27
- mock_bucket.blob.assert_called_once_with("data/file.json")
28
-
29
-
30
- def test_get_blob_with_pattern_and_placeholders():
31
- """Test get_blob with blob_name_pattern and placeholders."""
32
- settings_manager.user_config = {
33
- "default": {
34
- "bucket_name": "test-bucket",
35
- "blob_name_pattern": "users/{user_id}/files/{basename}",
36
- }
37
- }
38
-
39
- mock_bucket = MagicMock()
40
- mock_blob = MagicMock()
41
- mock_blob.name = "users/123/files/profile.json"
42
- mock_bucket.blob.return_value = mock_blob
43
-
44
- with patch(
45
- "kiarina.lib.google.cloud_storage._get_blob.get_bucket",
46
- return_value=mock_bucket,
47
- ):
48
- blob = get_blob(placeholders={"user_id": "123", "basename": "profile.json"})
49
- assert blob.name == "users/123/files/profile.json"
50
- mock_bucket.blob.assert_called_once_with("users/123/files/profile.json")
51
-
52
-
53
- def test_get_blob_with_fixed_pattern():
54
- """Test get_blob with fixed blob_name_pattern (no placeholders)."""
55
- settings_manager.user_config = {
56
- "default": {
57
- "bucket_name": "test-bucket",
58
- "blob_name_pattern": "data/fixed.json",
59
- }
60
- }
61
-
62
- mock_bucket = MagicMock()
63
- mock_blob = MagicMock()
64
- mock_blob.name = "data/fixed.json"
65
- mock_bucket.blob.return_value = mock_blob
66
-
67
- with patch(
68
- "kiarina.lib.google.cloud_storage._get_blob.get_bucket",
69
- return_value=mock_bucket,
70
- ):
71
- blob = get_blob()
72
- assert blob.name == "data/fixed.json"
73
- mock_bucket.blob.assert_called_once_with("data/fixed.json")
74
-
75
-
76
- def test_get_blob_priority_blob_name_over_placeholders():
77
- """Test that blob_name takes precedence over placeholders."""
78
- settings_manager.user_config = {
79
- "default": {
80
- "bucket_name": "test-bucket",
81
- "blob_name_pattern": "users/{user_id}/files/{basename}",
82
- }
83
- }
84
-
85
- mock_bucket = MagicMock()
86
- mock_blob = MagicMock()
87
- mock_blob.name = "direct/path.json"
88
- mock_bucket.blob.return_value = mock_blob
89
-
90
- with patch(
91
- "kiarina.lib.google.cloud_storage._get_blob.get_bucket",
92
- return_value=mock_bucket,
93
- ):
94
- blob = get_blob(
95
- blob_name="direct/path.json",
96
- placeholders={"user_id": "123", "basename": "ignored.json"},
97
- )
98
- assert blob.name == "direct/path.json"
99
- mock_bucket.blob.assert_called_once_with("direct/path.json")
100
-
101
-
102
- def test_get_blob_with_missing_placeholder():
103
- """Test error when placeholder is missing."""
104
- settings_manager.user_config = {
105
- "default": {
106
- "bucket_name": "test-bucket",
107
- "blob_name_pattern": "users/{user_id}/files/{basename}",
108
- }
109
- }
110
-
111
- with pytest.raises(
112
- ValueError,
113
- match=r"Missing placeholder 'basename' in blob_name_pattern: "
114
- r"users/\{user_id\}/files/\{basename\}",
115
- ):
116
- get_blob(placeholders={"user_id": "123"})
117
-
118
-
119
- def test_get_blob_without_blob_name_and_pattern():
120
- """Test error when neither blob_name nor blob_name_pattern is provided."""
121
- settings_manager.user_config = {
122
- "default": {
123
- "bucket_name": "test-bucket",
124
- }
125
- }
126
-
127
- with pytest.raises(
128
- ValueError,
129
- match="blob_name is not provided, placeholders are not provided, "
130
- "and blob_name_pattern is not set in settings",
131
- ):
132
- get_blob()
133
-
134
-
135
- def test_get_blob_with_placeholders_but_no_pattern():
136
- """Test error when placeholders are provided but pattern is not set."""
137
- settings_manager.user_config = {
138
- "default": {
139
- "bucket_name": "test-bucket",
140
- }
141
- }
142
-
143
- with pytest.raises(
144
- ValueError,
145
- match="placeholders provided but blob_name_pattern is not set in settings",
146
- ):
147
- get_blob(placeholders={"user_id": "123"})
148
-
149
-
150
- def test_get_blob_with_custom_config_key():
151
- """Test get_blob with custom config_key."""
152
- settings_manager.user_config = {
153
- "custom": {
154
- "bucket_name": "custom-bucket",
155
- "blob_name_pattern": "custom/path.json",
156
- }
157
- }
158
-
159
- mock_bucket = MagicMock()
160
- mock_blob = MagicMock()
161
- mock_blob.name = "custom/path.json"
162
- mock_bucket.blob.return_value = mock_blob
163
-
164
- with patch(
165
- "kiarina.lib.google.cloud_storage._get_blob.get_bucket",
166
- return_value=mock_bucket,
167
- ) as mock_get_bucket:
168
- blob = get_blob(config_key="custom")
169
- assert blob.name == "custom/path.json"
170
- mock_get_bucket.assert_called_once_with("custom", auth_config_key=None)
171
-
172
-
173
- def test_get_blob_with_auth_config_key():
174
- """Test get_blob with custom auth_config_key."""
175
- settings_manager.user_config = {
176
- "default": {
177
- "bucket_name": "test-bucket",
178
- "blob_name_pattern": "data.json",
179
- }
180
- }
181
-
182
- mock_bucket = MagicMock()
183
- mock_blob = MagicMock()
184
- mock_blob.name = "data.json"
185
- mock_bucket.blob.return_value = mock_blob
186
-
187
- with patch(
188
- "kiarina.lib.google.cloud_storage._get_blob.get_bucket",
189
- return_value=mock_bucket,
190
- ) as mock_get_bucket:
191
- blob = get_blob(auth_config_key="custom_auth")
192
- assert blob.name == "data.json"
193
- mock_get_bucket.assert_called_once_with(None, auth_config_key="custom_auth")
194
-
195
-
196
- def test_get_blob_with_complex_pattern():
197
- """Test get_blob with complex multi-level pattern."""
198
- settings_manager.user_config = {
199
- "default": {
200
- "bucket_name": "test-bucket",
201
- "blob_name_pattern": "web/{user_id}/{agent_id}/files/{basename}",
202
- }
203
- }
204
-
205
- mock_bucket = MagicMock()
206
- mock_blob = MagicMock()
207
- mock_blob.name = "web/user123/agent456/files/document.pdf"
208
- mock_bucket.blob.return_value = mock_blob
209
-
210
- with patch(
211
- "kiarina.lib.google.cloud_storage._get_blob.get_bucket",
212
- return_value=mock_bucket,
213
- ):
214
- blob = get_blob(
215
- placeholders={
216
- "user_id": "user123",
217
- "agent_id": "agent456",
218
- "basename": "document.pdf",
219
- }
220
- )
221
- assert blob.name == "web/user123/agent456/files/document.pdf"
222
- mock_bucket.blob.assert_called_once_with(
223
- "web/user123/agent456/files/document.pdf"
224
- )
@@ -1,91 +0,0 @@
1
- from unittest.mock import MagicMock, patch
2
-
3
- import pytest
4
-
5
- from kiarina.lib.google.cloud_storage import get_bucket, settings_manager
6
-
7
-
8
- def test_get_bucket():
9
- # Setup settings
10
- settings_manager.user_config = {
11
- "default": {
12
- "bucket_name": "test-bucket",
13
- }
14
- }
15
-
16
- # Mock get_storage_client and bucket
17
- mock_client = MagicMock()
18
- mock_bucket = MagicMock()
19
- mock_bucket.name = "test-bucket"
20
- mock_client.bucket.return_value = mock_bucket
21
-
22
- with patch(
23
- "kiarina.lib.google.cloud_storage._get_bucket.get_storage_client",
24
- return_value=mock_client,
25
- ) as mock_get_client:
26
- bucket = get_bucket()
27
- assert bucket.name == "test-bucket"
28
-
29
- # Verify get_storage_client was called with None (default)
30
- mock_get_client.assert_called_once_with(None)
31
-
32
- # Verify bucket was called with correct bucket name
33
- mock_client.bucket.assert_called_once_with("test-bucket")
34
-
35
-
36
- def test_get_bucket_without_bucket_name():
37
- # Setup settings without bucket_name
38
- settings_manager.user_config = {"default": {}}
39
-
40
- with pytest.raises(ValueError, match="bucket_name is not set in the settings"):
41
- get_bucket()
42
-
43
-
44
- def test_get_bucket_with_custom_config_key():
45
- # Setup settings with custom config key
46
- settings_manager.user_config = {
47
- "custom": {
48
- "bucket_name": "custom-bucket",
49
- }
50
- }
51
-
52
- # Mock get_storage_client and bucket
53
- mock_client = MagicMock()
54
- mock_bucket = MagicMock()
55
- mock_bucket.name = "custom-bucket"
56
- mock_client.bucket.return_value = mock_bucket
57
-
58
- with patch(
59
- "kiarina.lib.google.cloud_storage._get_bucket.get_storage_client",
60
- return_value=mock_client,
61
- ) as mock_get_client:
62
- bucket = get_bucket(config_key="custom")
63
- assert bucket.name == "custom-bucket"
64
-
65
- # Verify get_storage_client was called with None (no auth_config_key specified)
66
- mock_get_client.assert_called_once_with(None)
67
-
68
-
69
- def test_get_bucket_with_auth_config_key():
70
- # Setup settings
71
- settings_manager.user_config = {
72
- "default": {
73
- "bucket_name": "test-bucket",
74
- }
75
- }
76
-
77
- # Mock get_storage_client and bucket
78
- mock_client = MagicMock()
79
- mock_bucket = MagicMock()
80
- mock_bucket.name = "test-bucket"
81
- mock_client.bucket.return_value = mock_bucket
82
-
83
- with patch(
84
- "kiarina.lib.google.cloud_storage._get_bucket.get_storage_client",
85
- return_value=mock_client,
86
- ) as mock_get_client:
87
- bucket = get_bucket(auth_config_key="custom_auth")
88
- assert bucket.name == "test-bucket"
89
-
90
- # Verify get_storage_client was called with custom auth config key
91
- mock_get_client.assert_called_once_with("custom_auth")
@@ -1,57 +0,0 @@
1
- from unittest.mock import MagicMock, patch
2
-
3
- from kiarina.lib.google.cloud_storage import get_storage_client
4
-
5
-
6
- def test_get_storage_client():
7
- # Mock get_credentials and storage.Client
8
- mock_credentials = MagicMock()
9
- mock_credentials.project_id = "test-project"
10
- mock_client = MagicMock()
11
- mock_client.project = "test-project"
12
-
13
- with (
14
- patch(
15
- "kiarina.lib.google.cloud_storage._get_storage_client.get_credentials",
16
- return_value=mock_credentials,
17
- ) as mock_get_credentials,
18
- patch(
19
- "kiarina.lib.google.cloud_storage._get_storage_client.storage.Client",
20
- return_value=mock_client,
21
- ) as mock_client_class,
22
- ):
23
- client = get_storage_client()
24
- assert client.project == "test-project"
25
-
26
- # Verify get_credentials was called with None (default)
27
- mock_get_credentials.assert_called_once_with(None)
28
-
29
- # Verify Client was called with correct credentials
30
- mock_client_class.assert_called_once_with(credentials=mock_credentials)
31
-
32
-
33
- def test_get_storage_client_with_auth_config_key():
34
- # Mock get_credentials and storage.Client
35
- mock_credentials = MagicMock()
36
- mock_credentials.project_id = "custom-project"
37
- mock_client = MagicMock()
38
- mock_client.project = "custom-project"
39
-
40
- with (
41
- patch(
42
- "kiarina.lib.google.cloud_storage._get_storage_client.get_credentials",
43
- return_value=mock_credentials,
44
- ) as mock_get_credentials,
45
- patch(
46
- "kiarina.lib.google.cloud_storage._get_storage_client.storage.Client",
47
- return_value=mock_client,
48
- ) as mock_client_class,
49
- ):
50
- client = get_storage_client(auth_config_key="custom_auth")
51
- assert client.project == "custom-project"
52
-
53
- # Verify get_credentials was called with custom auth config key
54
- mock_get_credentials.assert_called_once_with("custom_auth")
55
-
56
- # Verify Client was called with correct credentials
57
- mock_client_class.assert_called_once_with(credentials=mock_credentials)