weaviate-cli 3.1.4__tar.gz → 3.2.0__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 (70) hide show
  1. weaviate_cli-3.2.0/.github/workflows/main.yaml +132 -0
  2. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/PKG-INFO +6 -3
  3. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/README.md +1 -0
  4. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/requirements-dev.txt +2 -1
  5. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/setup.cfg +2 -1
  6. weaviate_cli-3.2.0/test/integration/test_auth_integration.py +56 -0
  7. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/integration/test_integration.py +1 -6
  8. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/unittests/test_defaults.py +17 -5
  9. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/unittests/test_managers/test_data_manager.py +1 -0
  10. weaviate_cli-3.2.0/test/unittests/test_managers/test_user_manager.py +325 -0
  11. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/unittests/test_utils.py +16 -0
  12. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/assign.py +12 -2
  13. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/create.py +90 -0
  14. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/delete.py +47 -2
  15. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/get.py +53 -15
  16. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/query.py +7 -1
  17. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/revoke.py +12 -2
  18. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/update.py +127 -2
  19. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/defaults.py +31 -9
  20. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/managers/collection_manager.py +19 -4
  21. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/managers/config_manager.py +24 -1
  22. weaviate_cli-3.2.0/weaviate_cli/managers/data_manager.py +1245 -0
  23. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/managers/role_manager.py +18 -5
  24. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/managers/tenant_manager.py +56 -48
  25. weaviate_cli-3.2.0/weaviate_cli/managers/user_manager.py +173 -0
  26. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/utils.py +29 -11
  27. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli.egg-info/PKG-INFO +6 -3
  28. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli.egg-info/SOURCES.txt +2 -0
  29. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli.egg-info/requires.txt +2 -1
  30. weaviate_cli-3.1.4/.github/workflows/main.yaml +0 -77
  31. weaviate_cli-3.1.4/weaviate_cli/managers/data_manager.py +0 -637
  32. weaviate_cli-3.1.4/weaviate_cli/managers/user_manager.py +0 -54
  33. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/.github/dependabot.yml +0 -0
  34. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/.github/workflows/release.yaml +0 -0
  35. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/.gitignore +0 -0
  36. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/.pre-commit-config.yaml +0 -0
  37. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/CONTRIBUTING.md +0 -0
  38. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/Dockerfile +0 -0
  39. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/LICENSE +0 -0
  40. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/MANIFEST.in +0 -0
  41. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/Makefile +0 -0
  42. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/cli.py +0 -0
  43. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/publish.md +0 -0
  44. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/pyproject.toml +0 -0
  45. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/setup.py +0 -0
  46. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/README.md +0 -0
  47. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/__init__.py +0 -0
  48. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/unittests/conftest.py +0 -0
  49. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/unittests/test_cli.py +0 -0
  50. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/unittests/test_managers/test_collection_manager.py +0 -0
  51. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/unittests/test_managers/test_config_manager.py +0 -0
  52. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/unittests/test_managers/test_node_manager.py +0 -0
  53. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/test/unittests/test_managers/test_shard_manager.py +0 -0
  54. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/__init__.py +0 -0
  55. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/__init__.py +0 -0
  56. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/cancel.py +0 -0
  57. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/commands/restore.py +0 -0
  58. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/completion/__init__.py +0 -0
  59. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/completion/complete.py +0 -0
  60. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/datasets/__init__.py +0 -0
  61. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/datasets/movies.json +0 -0
  62. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/managers/__init__.py +0 -0
  63. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/managers/backup_manager.py +0 -0
  64. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/managers/node_manager.py +0 -0
  65. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/managers/shard_manager.py +0 -0
  66. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli/types/models.py +0 -0
  67. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli.egg-info/dependency_links.txt +0 -0
  68. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli.egg-info/entry_points.txt +0 -0
  69. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli.egg-info/not-zip-safe +0 -0
  70. {weaviate_cli-3.1.4 → weaviate_cli-3.2.0}/weaviate_cli.egg-info/top_level.txt +0 -0
@@ -0,0 +1,132 @@
1
+ name: Main
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ jobs:
10
+ lint-and-format:
11
+ name: Run Linter and Formatter
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.11"
18
+ - run: pip install -r requirements-dev.txt
19
+ - name: "Black"
20
+ run: black --check cli.py weaviate_cli test
21
+ - name: "Check release for pypi"
22
+ run: |
23
+ python -m build
24
+ python -m twine check dist/*
25
+
26
+ unit-tests:
27
+ name: Run Unit Tests
28
+ needs: [lint-and-format]
29
+ runs-on: ubuntu-latest
30
+ strategy:
31
+ matrix:
32
+ version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ - uses: actions/setup-python@v5
36
+ with:
37
+ python-version: ${{ matrix.version }}
38
+ - run: pip install -e .
39
+ - name: Run unit tests with pytest
40
+ run: |
41
+ pip install pytest-html
42
+ pytest test/unittests --html=test-report-${{ matrix.version }}.html --self-contained-html
43
+ - name: Upload test results
44
+ if: always()
45
+ uses: actions/upload-artifact@v4
46
+ with:
47
+ name: test-results-${{ matrix.version }}
48
+ path: test-report-${{ matrix.version }}.html
49
+
50
+ get-latest-weaviate-version:
51
+ runs-on: ubuntu-latest
52
+ needs: [unit-tests]
53
+ name: Get latest Weaviate version
54
+ outputs:
55
+ LATEST_WEAVIATE_VERSION: ${{ steps.latest-version.outputs.latest_weaviate_version }}
56
+ steps:
57
+ - name: Retrieve latest Weaviate version
58
+ id: latest-version
59
+ uses: weaviate/github-common-actions/.github/actions/get-latest-weaviate-version@main
60
+
61
+ integration-tests:
62
+ needs: [unit-tests, get-latest-weaviate-version]
63
+ env:
64
+ WEAVIATE_VERSION: ${{ needs.get-latest-weaviate-version.outputs.LATEST_WEAVIATE_VERSION }}
65
+ MODULES: "text2vec-transformers,text2vec-contextionary"
66
+ name: Run Integration Tests
67
+ runs-on: ubuntu-latest
68
+ strategy:
69
+ matrix:
70
+ version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
71
+ steps:
72
+ - uses: actions/checkout@v4
73
+ - uses: actions/setup-python@v5
74
+ with:
75
+ python-version: ${{ matrix.version }}
76
+ - run: pip install -e .
77
+ - name: Start up Weaviate cluster
78
+ uses: weaviate/weaviate-local-k8s@v2
79
+ with:
80
+ workers: 1
81
+ replicas: 1
82
+ weaviate-version: ${{ env.WEAVIATE_VERSION }}
83
+ modules: ${{ env.MODULES }}
84
+ enable-backup: true
85
+ dynamic-users: true
86
+ - name: Run integration tests with pytest
87
+ run: |
88
+ pip install pytest-html
89
+ pytest test/integration/test_integration.py --html=test-report-${{ matrix.version }}.html --self-contained-html
90
+ integration-auth-tests:
91
+ needs: [unit-tests, get-latest-weaviate-version]
92
+ env:
93
+ WEAVIATE_VERSION: ${{ needs.get-latest-weaviate-version.outputs.LATEST_WEAVIATE_VERSION }}
94
+ MODULES: "text2vec-transformers,text2vec-contextionary"
95
+ name: Run Integration Tests Auth
96
+ runs-on: ubuntu-latest
97
+ strategy:
98
+ matrix:
99
+ version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
100
+ steps:
101
+ - uses: actions/checkout@v4
102
+ - uses: actions/setup-python@v5
103
+ with:
104
+ python-version: ${{ matrix.version }}
105
+ - run: pip install -e .
106
+ - name: Start up Weaviate cluster
107
+ uses: weaviate/weaviate-local-k8s@v2
108
+ with:
109
+ workers: 1
110
+ replicas: 1
111
+ weaviate-version: ${{ env.WEAVIATE_VERSION }}
112
+ modules: ${{ env.MODULES }}
113
+ enable-backup: true
114
+ rbac: true
115
+ dynamic-users: true
116
+ - name: Create config directory
117
+ run: mkdir -p ~/.config/weaviate
118
+ - name: Create config file
119
+ run: |
120
+ echo '{
121
+ "host": "localhost",
122
+ "http_port": "8080",
123
+ "grpc_port": "50051",
124
+ "auth": {
125
+ "type": "api_key",
126
+ "api_key": "admin-key"
127
+ }
128
+ }' > ~/.config/weaviate/config.json
129
+ - name: Run integration auth tests with pytest
130
+ run: |
131
+ pip install pytest-html
132
+ pytest test/integration/test_auth_integration.py --html=test-auth-report-${{ matrix.version }}.html --self-contained-html
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: weaviate-cli
3
- Version: 3.1.4
3
+ Version: 3.2.0
4
4
  Summary: Command line interface to interact with weaviate
5
5
  Home-page: https://github.com/weaviate/weaviate-cli
6
6
  Download-URL: https://github.com/weaviate/weaviate-cli
@@ -28,13 +28,15 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
28
28
  Requires-Python: >=3.9
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
- Requires-Dist: weaviate-client>=4.11.0
31
+ Requires-Dist: weaviate-client>=4.12.0
32
32
  Requires-Dist: click==8.1.7
33
33
  Requires-Dist: semver>=3.0.2
34
34
  Requires-Dist: numpy>=1.24.0
35
35
  Requires-Dist: importlib-resources>=5.0.0
36
36
  Requires-Dist: prettytable>=3.1.0
37
+ Requires-Dist: faker>=20.0.0
37
38
  Dynamic: download-url
39
+ Dynamic: license-file
38
40
 
39
41
  # Weaviate CLI
40
42
 
@@ -118,6 +120,7 @@ The configuration file should be a JSON file with the following structure:
118
120
  "host": "your-weaviate-host",
119
121
  "http_port": "your-http-port",
120
122
  "grpc_port": "your-grpc-port",
123
+ "grpc_host": "your-grpc-host",
121
124
  "auth": {
122
125
  "type": "api_key",
123
126
  "api_key": "your-api-key"
@@ -80,6 +80,7 @@ The configuration file should be a JSON file with the following structure:
80
80
  "host": "your-weaviate-host",
81
81
  "http_port": "your-http-port",
82
82
  "grpc_port": "your-grpc-port",
83
+ "grpc_host": "your-grpc-host",
83
84
  "auth": {
84
85
  "type": "api_key",
85
86
  "api_key": "your-api-key"
@@ -1,4 +1,4 @@
1
- weaviate-client>=4.11.0
1
+ weaviate-client>=4.12.0
2
2
  click==8.1.7
3
3
  twine
4
4
  pytest
@@ -12,3 +12,4 @@ setuptools_scm
12
12
  black==24.3.0
13
13
  pre-commit==3.6.2
14
14
  prettytable>=3.1.0
15
+ faker>=20.0.0
@@ -36,12 +36,13 @@ classifiers =
36
36
  include_package_data = True
37
37
  python_requires = >=3.9
38
38
  install_requires =
39
- weaviate-client>=4.11.0
39
+ weaviate-client>=4.12.0
40
40
  click==8.1.7
41
41
  semver>=3.0.2
42
42
  numpy>=1.24.0
43
43
  importlib-resources>=5.0.0
44
44
  prettytable>=3.1.0
45
+ faker>=20.0.0
45
46
  setup_requires =
46
47
  pip
47
48
  setuptools
@@ -0,0 +1,56 @@
1
+ import pytest
2
+ import weaviate
3
+ import click
4
+ from click.testing import CliRunner
5
+ from weaviate_cli.managers.collection_manager import CollectionManager
6
+ from weaviate_cli.managers.config_manager import ConfigManager
7
+ from weaviate_cli.managers.user_manager import UserManager
8
+ import weaviate.classes.config as wvc
9
+
10
+
11
+ @pytest.fixture
12
+ def client() -> weaviate.Client:
13
+ config = ConfigManager()
14
+ return config.get_client()
15
+
16
+
17
+ @pytest.fixture
18
+ def collection_manager(client: weaviate.Client) -> CollectionManager:
19
+ return CollectionManager(client)
20
+
21
+
22
+ @pytest.fixture
23
+ def user_manager(client: weaviate.Client) -> UserManager:
24
+ return UserManager(client)
25
+
26
+
27
+ def test_user_lifecycle(user_manager: UserManager):
28
+ try:
29
+ user_name = "test_user"
30
+ # Create user
31
+ api_key = user_manager.create_user(user_name=user_name)
32
+ assert user_name in [user.user_id for user in user_manager.get_all_users()]
33
+ assert api_key is not None
34
+
35
+ # Update user
36
+ new_api_key = user_manager.update_user(user_name=user_name, rotate_api_key=True)
37
+ assert new_api_key is not None
38
+ assert new_api_key != api_key
39
+
40
+ # Deactivate user
41
+ user_manager.update_user(user_name=user_name, deactivate=True)
42
+ user = user_manager.get_user(user_name=user_name)
43
+ assert not user.active
44
+
45
+ # Activate user
46
+ user_manager.update_user(user_name=user_name, activate=True)
47
+ user = user_manager.get_user(user_name=user_name)
48
+ assert user.active
49
+
50
+ # Print user
51
+ user_manager.print_user(user=user_name)
52
+
53
+ finally:
54
+ # Delete user
55
+ user_manager.delete_user(user_name=user_name)
56
+ assert user_name not in [user.user_id for user in user_manager.get_all_users()]
@@ -1,6 +1,5 @@
1
1
  import pytest
2
2
  import weaviate
3
- import click
4
3
  from click.testing import CliRunner
5
4
  from weaviate_cli.managers.collection_manager import CollectionManager
6
5
  from weaviate_cli.managers.config_manager import ConfigManager
@@ -12,11 +11,7 @@ import weaviate.classes.config as wvc
12
11
  @pytest.fixture
13
12
  def client() -> weaviate.Client:
14
13
  config = ConfigManager()
15
- return weaviate.connect_to_local(
16
- host=config.config["host"],
17
- port=int(config.config["http_port"]),
18
- grpc_port=int(config.config["grpc_port"]),
19
- )
14
+ return config.get_client()
20
15
 
21
16
 
22
17
  @pytest.fixture
@@ -92,7 +92,7 @@ def test_create_tenants_defaults(runner):
92
92
  mock_collection.tenants.get.return_value = empty_tenants
93
93
 
94
94
  mock_tenants = {
95
- f"{CreateTenantsDefaults.tenant_suffix}{i}": MagicMock(
95
+ f"{CreateTenantsDefaults.tenant_suffix}-{i}": MagicMock(
96
96
  activity_status=TenantActivityStatus.ACTIVE
97
97
  )
98
98
  for i in range(CreateTenantsDefaults.number_tenants)
@@ -134,7 +134,19 @@ def test_create_data_defaults(runner):
134
134
  # Create mock context with config
135
135
  ctx = click.Context(create_data_cli)
136
136
  ctx.obj = {"config": MagicMock()}
137
- ctx.obj["config"].get_client = MagicMock()
137
+ mock_client = MagicMock()
138
+ mock_client.collections.exists.return_value = True
139
+
140
+ # Mock collection object
141
+ mock_collection = MagicMock()
142
+ mock_client.collections.get.return_value = mock_collection
143
+
144
+ # Mock multi-tenancy config
145
+ mock_config = MagicMock()
146
+ mock_config.multi_tenancy_config.enabled = False
147
+ mock_collection.config.get.return_value = mock_config
148
+
149
+ ctx.obj["config"].get_client.return_value = mock_client
138
150
 
139
151
  result = runner.invoke(create_data_cli, obj=ctx.obj)
140
152
  assert result.exit_code == 0, result.output
@@ -179,7 +191,7 @@ def test_delete_tenants_defaults(runner):
179
191
 
180
192
  # Mock tenants with side effect to return different values on subsequent calls
181
193
  mock_tenants = {
182
- f"{DeleteTenantsDefaults.tenant_suffix}{i}": MagicMock()
194
+ f"{DeleteTenantsDefaults.tenant_suffix}-{i}": MagicMock()
183
195
  for i in range(1, DeleteTenantsDefaults.number_tenants + 1)
184
196
  }
185
197
  empty_tenants = {}
@@ -365,7 +377,7 @@ def test_update_tenants_defaults(runner):
365
377
 
366
378
  # Mock tenants with side effect to return different values on subsequent calls
367
379
  mock_tenants = {
368
- f"{UpdateTenantsDefaults.tenant_suffix}{i}": MagicMock(
380
+ f"{UpdateTenantsDefaults.tenant_suffix}-{i}": MagicMock(
369
381
  activity_status=TenantActivityStatus.ACTIVE
370
382
  )
371
383
  for i in range(UpdateTenantsDefaults.number_tenants)
@@ -373,7 +385,7 @@ def test_update_tenants_defaults(runner):
373
385
  mock_collection.tenants.get.return_value = mock_tenants
374
386
 
375
387
  updated_tenants = {
376
- f"{UpdateTenantsDefaults.tenant_suffix}{i}": MagicMock(
388
+ f"{UpdateTenantsDefaults.tenant_suffix}-{i}": MagicMock(
377
389
  activity_status=TenantActivityStatus.ACTIVE
378
390
  )
379
391
  for i in range(UpdateTenantsDefaults.number_tenants)
@@ -22,6 +22,7 @@ def test_ingest_data(mock_client):
22
22
  collection="TestCollection",
23
23
  limit=100,
24
24
  randomize=True,
25
+ auto_tenants=10,
25
26
  )
26
27
 
27
28
  mock_client.collections.get.assert_called_once_with("TestCollection")
@@ -0,0 +1,325 @@
1
+ import pytest
2
+ from unittest.mock import Mock, patch
3
+ from weaviate_cli.managers.user_manager import UserManager
4
+ from weaviate.exceptions import WeaviateConnectionError
5
+
6
+
7
+ @pytest.fixture
8
+ def mock_client():
9
+ client = Mock()
10
+ return client
11
+
12
+
13
+ @pytest.fixture
14
+ def user_manager(mock_client):
15
+ return UserManager(mock_client)
16
+
17
+
18
+ def test_get_user_from_role_success(user_manager):
19
+ # Arrange
20
+ role_name = "test_role"
21
+ expected_users = ["user1", "user2"]
22
+ user_manager.client.roles.get_assigned_user_ids.return_value = expected_users
23
+
24
+ # Act
25
+ result = user_manager.get_user_from_role(role_name)
26
+
27
+ # Assert
28
+ assert result == expected_users
29
+ user_manager.client.roles.get_assigned_user_ids.assert_called_once_with(
30
+ role_name=role_name
31
+ )
32
+
33
+
34
+ def test_get_user_from_role_error(user_manager):
35
+ # Arrange
36
+ role_name = "test_role"
37
+ user_manager.client.roles.get_assigned_user_ids.side_effect = Exception(
38
+ "Test error"
39
+ )
40
+
41
+ # Act & Assert
42
+ with pytest.raises(Exception) as exc_info:
43
+ user_manager.get_user_from_role(role_name)
44
+ assert (
45
+ str(exc_info.value) == f"Error getting users for role '{role_name}': Test error"
46
+ )
47
+
48
+
49
+ def test_create_user_success(user_manager):
50
+ # Arrange
51
+ user_name = "test_user"
52
+ expected_api_key = "test_api_key"
53
+ user_manager.client.users.db.create.return_value = expected_api_key
54
+
55
+ # Act
56
+ result = user_manager.create_user(user_name)
57
+
58
+ # Assert
59
+ assert result == expected_api_key
60
+ user_manager.client.users.db.create.assert_called_once_with(user_id=user_name)
61
+
62
+
63
+ def test_create_user_no_name(user_manager):
64
+ # Act & Assert
65
+ with pytest.raises(Exception) as exc_info:
66
+ user_manager.create_user(None)
67
+ assert str(exc_info.value) == "User name is required."
68
+
69
+
70
+ def test_create_user_error(user_manager):
71
+ # Arrange
72
+ user_name = "test_user"
73
+ user_manager.client.users.db.create.side_effect = Exception("Test error")
74
+
75
+ # Act & Assert
76
+ with pytest.raises(Exception) as exc_info:
77
+ user_manager.create_user(user_name)
78
+ assert str(exc_info.value) == f"Error creating user '{user_name}': Test error"
79
+
80
+
81
+ def test_update_user_rotate_key_success(user_manager):
82
+ # Arrange
83
+ user_name = "test_user"
84
+ expected_api_key = "new_api_key"
85
+ user_manager.client.users.db.rotate_key.return_value = expected_api_key
86
+
87
+ # Act
88
+ result = user_manager.update_user(user_name=user_name, rotate_api_key=True)
89
+
90
+ # Assert
91
+ assert result == expected_api_key
92
+ user_manager.client.users.db.rotate_key.assert_called_once_with(user_id=user_name)
93
+
94
+
95
+ def test_update_user_activate_success(user_manager):
96
+ # Arrange
97
+ user_name = "test_user"
98
+ user_manager.client.users.db.activate.return_value = None
99
+
100
+ # Act
101
+ result = user_manager.update_user(user_name=user_name, activate=True)
102
+
103
+ # Assert
104
+ assert result is None
105
+ user_manager.client.users.db.activate.assert_called_once_with(user_id=user_name)
106
+
107
+
108
+ def test_update_user_deactivate_success(user_manager):
109
+ # Arrange
110
+ user_name = "test_user"
111
+ user_manager.client.users.db.deactivate.return_value = None
112
+
113
+ # Act
114
+ result = user_manager.update_user(user_name=user_name, deactivate=True)
115
+
116
+ # Assert
117
+ assert result is None
118
+ user_manager.client.users.db.deactivate.assert_called_once_with(user_id=user_name)
119
+
120
+
121
+ def test_update_user_invalid_combination(user_manager):
122
+ # Arrange
123
+ user_name = "test_user"
124
+
125
+ # Act & Assert
126
+ with pytest.raises(Exception) as exc_info:
127
+ user_manager.update_user(
128
+ user_name=user_name, rotate_api_key=True, activate=True
129
+ )
130
+ assert (
131
+ str(exc_info.value)
132
+ == "Cannot rotate api key and activate or deactivate user at the same time."
133
+ )
134
+
135
+
136
+ def test_update_user_activate_deactivate(user_manager):
137
+ # Arrange
138
+ user_name = "test_user"
139
+
140
+ # Act & Assert
141
+ with pytest.raises(Exception) as exc_info:
142
+ user_manager.update_user(user_name=user_name, activate=True, deactivate=True)
143
+ assert (
144
+ str(exc_info.value) == "Cannot activate and deactivate user at the same time."
145
+ )
146
+
147
+
148
+ def test_update_user_no_name(user_manager):
149
+ # Act & Assert
150
+ with pytest.raises(Exception) as exc_info:
151
+ user_manager.update_user(None)
152
+ assert str(exc_info.value) == "User name is required."
153
+
154
+
155
+ def test_update_user_error(user_manager):
156
+ # Arrange
157
+ user_name = "test_user"
158
+ user_manager.client.users.db.rotate_key.side_effect = Exception("Test error")
159
+
160
+ # Act & Assert
161
+ with pytest.raises(Exception) as exc_info:
162
+ user_manager.update_user(user_name=user_name, rotate_api_key=True)
163
+ assert str(exc_info.value) == f"Error updating user '{user_name}': Test error"
164
+
165
+
166
+ def test_delete_user_success(user_manager):
167
+ # Arrange
168
+ user_name = "test_user"
169
+
170
+ # Act
171
+ user_manager.delete_user(user_name)
172
+
173
+ # Assert
174
+ user_manager.client.users.db.delete.assert_called_once_with(user_id=user_name)
175
+
176
+
177
+ def test_delete_user_no_name(user_manager):
178
+ # Act & Assert
179
+ with pytest.raises(Exception) as exc_info:
180
+ user_manager.delete_user(None)
181
+ assert str(exc_info.value) == "User name is required."
182
+
183
+
184
+ def test_delete_user_error(user_manager):
185
+ # Arrange
186
+ user_name = "test_user"
187
+ user_manager.client.users.db.delete.side_effect = Exception("Test error")
188
+
189
+ # Act & Assert
190
+ with pytest.raises(Exception) as exc_info:
191
+ user_manager.delete_user(user_name)
192
+ assert str(exc_info.value) == f"Error deleting user '{user_name}': Test error"
193
+
194
+
195
+ def test_add_role_db_success(user_manager):
196
+ # Arrange
197
+ role_name = ("test_role",)
198
+ user_name = "test_user"
199
+ user_manager.client.get_meta.return_value = {"version": "1.30.0"}
200
+
201
+ # Act
202
+ user_manager.add_role(role_name, user_name, "db")
203
+
204
+ # Assert
205
+ user_manager.client.users.db.assign_roles.assert_called_once_with(
206
+ user_id=user_name, role_names=list(role_name)
207
+ )
208
+
209
+
210
+ def test_add_role_db_success_pre_130(user_manager):
211
+ # Arrange
212
+ role_name = ("test_role",)
213
+ user_name = "test_user"
214
+ user_manager.client.get_meta.return_value = {"version": "1.29.0"}
215
+
216
+ # Act
217
+ user_manager.add_role(role_name, user_name, "db")
218
+
219
+ # Assert
220
+ user_manager.client.users.assign_roles.assert_called_once_with(
221
+ user_id=user_name, role_names=list(role_name)
222
+ )
223
+
224
+
225
+ def test_add_role_oidc_success(user_manager):
226
+ # Arrange
227
+ role_name = ("test_role",)
228
+ user_name = "test_user"
229
+ user_manager.client.get_meta.return_value = {"version": "1.30.0"}
230
+
231
+ # Act
232
+ user_manager.add_role(role_name, user_name, "oidc")
233
+
234
+ # Assert
235
+ user_manager.client.users.oidc.assign_roles.assert_called_once_with(
236
+ user_id=user_name, role_names=list(role_name)
237
+ )
238
+
239
+
240
+ def test_add_role_error(user_manager):
241
+ # Arrange
242
+ role_name = ("test_role",)
243
+ user_name = "test_user"
244
+ user_manager.client.get_meta.return_value = {"version": "1.30.0"}
245
+ user_manager.client.users.db.assign_roles.side_effect = Exception("Test error")
246
+
247
+ # Act & Assert
248
+ with pytest.raises(Exception) as exc_info:
249
+ user_manager.add_role(role_name, user_name, "db")
250
+ assert (
251
+ str(exc_info.value)
252
+ == f"Error assigning db role '{role_name}' to user '{user_name}': Test error"
253
+ )
254
+
255
+
256
+ def test_revoke_role_db_success(user_manager):
257
+ # Arrange
258
+ role_name = ("test_role",)
259
+ user_name = "test_user"
260
+ user_manager.client.get_meta.return_value = {"version": "1.30.0"}
261
+
262
+ # Act
263
+ user_manager.revoke_role(role_name, user_name, "db")
264
+
265
+ # Assert
266
+ user_manager.client.users.db.revoke_roles.assert_called_once_with(
267
+ user_id=user_name, role_names=list(role_name)
268
+ )
269
+
270
+
271
+ def test_revoke_role_db_success_pre_130(user_manager):
272
+ # Arrange
273
+ role_name = ("test_role",)
274
+ user_name = "test_user"
275
+ user_manager.client.get_meta.return_value = {"version": "1.29.0"}
276
+
277
+ # Act
278
+ user_manager.revoke_role(role_name, user_name, "db")
279
+
280
+ # Assert
281
+ user_manager.client.users.revoke_roles.assert_called_once_with(
282
+ user_id=user_name, role_names=list(role_name)
283
+ )
284
+
285
+
286
+ def test_revoke_role_oidc_success(user_manager):
287
+ # Arrange
288
+ role_name = ("test_role",)
289
+ user_name = "test_user"
290
+ user_manager.client.get_meta.return_value = {"version": "1.30.0"}
291
+ # Act
292
+ user_manager.revoke_role(role_name, user_name, "oidc")
293
+
294
+ # Assert
295
+ user_manager.client.users.oidc.revoke_roles.assert_called_once_with(
296
+ user_id=user_name, role_names=list(role_name)
297
+ )
298
+
299
+
300
+ def test_revoke_role_error(user_manager):
301
+ # Arrange
302
+ role_name = ("test_role",)
303
+ user_name = "test_user"
304
+ user_manager.client.users.db.revoke_roles.side_effect = Exception("Test error")
305
+ user_manager.client.get_meta.return_value = {"version": "1.30.0"}
306
+
307
+ # Act & Assert
308
+ with pytest.raises(Exception) as exc_info:
309
+ user_manager.revoke_role(role_name, user_name, "db")
310
+ assert (
311
+ str(exc_info.value)
312
+ == f"Error revoking db role '{role_name}' from user '{user_name}': Test error"
313
+ )
314
+
315
+
316
+ def test_print_user(user_manager, capsys):
317
+ # Arrange
318
+ user = "test_user"
319
+
320
+ # Act
321
+ user_manager.print_user(user)
322
+
323
+ # Assert
324
+ captured = capsys.readouterr()
325
+ assert captured.out == f"User: {user}\n"
@@ -222,10 +222,26 @@ def test_parse_permission_roles():
222
222
 
223
223
 
224
224
  def test_parse_permission_users():
225
+ assert parse_permission("create_users:admin") == Permissions.users(
226
+ user="admin", create=True
227
+ )
228
+
229
+ assert parse_permission("crud_users:admin") == Permissions.users(
230
+ user="admin", create=True, read=True, update=True, delete=True
231
+ )
232
+
225
233
  assert parse_permission("assign_and_revoke_users:admin") == Permissions.users(
226
234
  user="admin", assign_and_revoke=True
227
235
  )
228
236
 
237
+ assert parse_permission("cr_users:admin1,admin2,viewer1") == Permissions.users(
238
+ user=["admin1", "admin2", "viewer1"],
239
+ create=True,
240
+ read=True,
241
+ update=False,
242
+ delete=False,
243
+ )
244
+
229
245
  assert parse_permission(
230
246
  "assign_and_revoke_users:admin,admin1,admin2"
231
247
  ) == Permissions.users(user=["admin", "admin1", "admin2"], assign_and_revoke=True)