Skip to content

Commit 419bfce

Browse files
committed
feat(tests): add models tests and e2e tests
1 parent b35ffc4 commit 419bfce

12 files changed

Lines changed: 310 additions & 50 deletions

pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ dependencies = [
3939
"pydantic >=2.5.0",
4040
"httpx >=0.24.1",
4141
]
42+
[dev-dependencies]
43+
test = [
44+
"hatch~=1.12",
45+
"pytest~=8.3",
46+
]
4247

4348
[tool.hatch.version]
4449
path = "src/vaultwarden/__version__.py"
@@ -68,7 +73,7 @@ test = "coverage run --source=src/vaultwarden -m unittest discover -p 'test_*.py
6873
_coverage = ["test", "coverage xml", "coverage report --show-missing"]
6974
with-coverage = "test"
7075
[[tool.hatch.envs.test.matrix]]
71-
python = ["3.10", "3.11"]
76+
python = ["3.10", "3.11", "3.12"]
7277
type = ["default"]
7378
[tool.hatch.envs.test.overrides]
7479
matrix.type.scripts = [

tests/e2e/__init__.py

Whitespace-only changes.

tests/e2e/run_tests.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env bash
2+
3+
if [[ -z "${VAULTWARDEN_VERSION}" ]]; then
4+
VAULTWARDEN_VERSION="1.32.0"
5+
fi
6+
7+
temp_dir=$(mktemp -d)
8+
9+
# Copy fixtures db to tmp
10+
cp tests/fixtures/server/* $temp_dir
11+
12+
# Start Vaultwarden docker
13+
docker run -d --name vaultwarden -v $temp_dir:/data --env I_REALLY_WANT_VOLATILE_STORAGE=true --env ADMIN_TOKEN=admin --restart unless-stopped -p 80:80 vaultwarden/server:${VAULTWARDEN_VERSION}
14+
15+
#exit 0
16+
17+
# Wait for vaultwarden to start
18+
sleep 3
19+
20+
# Set env variables
21+
export VAULTWARDEN_URL="http://localhost:80"
22+
export VAULTWARDEN_ADMIN_TOKEN="admin"
23+
export BITWARDEN_URL="http://localhost:80"
24+
export BITWARDEN_EMAIL="test-account@example.com"
25+
export BITWARDEN_PASSWORD="test-account"
26+
export BITWARDEN_CLIENT_ID="user.a8be340c-856b-481f-8183-2b7712995da2"
27+
export BITWARDEN_CLIENT_SECRET="ag66paVUq4h7tBLbCbJOY5tJkQvUuT"
28+
export BITWARDEN_TEST_ORGANIZATION="cda840d2-1de0-4f31-bd49-b30dacd7e8b0"
29+
export BITWARDEN_DEVICE_ID="e54ba5f5-7d58-4830-8f2b-99194c70c14f"
30+
31+
# Run tests
32+
hatch run test:with-coverage
33+
34+
# store the exit code
35+
TEST_EXIT_CODE=$?
36+
37+
# Stop and remove vaultwarden docker
38+
docker stop vaultwarden
39+
docker rm vaultwarden
40+
41+
# Remove fixtures db from tmp
42+
rm -rf $temp_dir
43+
44+
# Exit with the test exit code
45+
exit $TEST_EXIT_CODE
Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
client_id = os.environ.get("BITWARDEN_CLIENT_ID", None)
1212
client_secret = os.environ.get("BITWARDEN_CLIENT_SECRET", None)
1313
device_id = os.environ.get("BITWARDEN_DEVICE_ID", None)
14-
1514
bitwarden = BitwardenAPIClient(
1615
url, email, password, client_id, client_secret, device_id
1716
)
@@ -27,35 +26,41 @@ def setUp(self) -> None:
2726
self.test_colls_ids = self.organization.collections()
2827
self.test_users = self.organization.users()
2928
self.test_org_ciphers = self.organization.ciphers()
30-
self.test_coll_1_cipher = self.organization.ciphers(
31-
self.test_colls_names.get("1_cipher").Id
29+
self.test_collection_1_ciphers = self.organization.ciphers(
30+
self.test_colls_names.get("test-collection").Id
3231
)
33-
self.test_coll_2_ciphers = self.organization.ciphers(
34-
self.test_colls_names.get("2_ciphers").Id
32+
self.test_collection_2_ciphers = self.organization.ciphers(
33+
self.test_colls_names.get("test-collection-2").Id
3534
)
36-
self.test_coll_1_user = self.test_colls_names.get("1_user").users()
37-
self.test_coll_2_users = self.test_colls_names.get("2_users").users()
35+
self.test_collection_1_users = self.test_colls_names.get(
36+
"test-collection"
37+
).users()
38+
self.test_collection_2_users = self.test_colls_names.get(
39+
"test-collection-2"
40+
).users()
3841

3942
def test_get_organization_users(self):
40-
self.assertEqual(len(self.test_users), 4)
43+
self.assertEqual(len(self.test_users), 2)
4144

4245
def test_get_organization_items(self):
43-
self.assertEqual(len(self.test_org_ciphers), 5)
46+
self.assertEqual(len(self.test_org_ciphers), 1)
4447

4548
def test_get_organization_collection_1_item(self):
46-
self.assertEqual(len(self.test_coll_1_cipher), 1)
49+
self.assertEqual(len(self.test_collection_1_ciphers), 0)
4750

4851
def test_get_organization_collection_2_items(self):
49-
self.assertEqual(len(self.test_coll_2_ciphers), 2)
52+
self.assertEqual(len(self.test_collection_2_ciphers), 1)
5053

5154
def test_get_organizations_collections(self):
52-
self.assertEqual(len(self.test_colls_ids), 5)
55+
# 2 test collections + default collection
56+
self.assertEqual(len(self.test_colls_ids), 3)
5357

5458
def test_get_users_of_collection_1(self):
55-
self.assertEqual(len(self.test_coll_1_user), 1)
59+
# 2 test collections + default collection
60+
self.assertEqual(len(self.test_collection_1_users), 0)
5661

5762
def test_get_users_of_collection_2(self):
58-
self.assertEqual(len(self.test_coll_2_users), 2)
63+
self.assertEqual(len(self.test_collection_2_users), 1)
5964

6065
def test_create_delete_collection(self):
6166
len_old_colls = len(self.organization.collections(force_refresh=True))
@@ -67,30 +72,49 @@ def test_create_delete_collection(self):
6772
self.assertEqual(len(new_colls), len_old_colls)
6873

6974
def test_set_users_of_collection(self):
70-
coll = self.test_colls_names.get("1_user")
71-
coll.set_users(self.test_coll_2_users)
72-
users = coll.users()
73-
self.assertEqual(len(users), 2)
74-
coll.set_users(self.test_coll_1_user)
75+
coll = self.test_colls_names.get("test-collection")
76+
coll.set_users(self.test_collection_2_users)
7577
users = coll.users()
7678
self.assertEqual(len(users), 1)
79+
coll.set_users(self.test_collection_1_users)
80+
users = coll.users()
81+
self.assertEqual(len(users), 0)
7782

7883
def test_add_remove_collection_from_user(self):
79-
user_org_id = self.test_coll_1_user[0].UserId
84+
user_org_id = self.test_collection_2_users[0].UserId
8085
user_infos = self.organization.user(user_org_id)
81-
coll_1_user = self.test_colls_names.get("1_user")
82-
user_infos.remove_collections([coll_1_user.Id])
86+
collection_2 = self.test_colls_names.get("test-collection-2")
87+
user_infos.remove_collections([collection_2.Id])
8388
self.assertEqual(
84-
len(coll_1_user.users()),
89+
len(collection_2.users()),
8590
0,
8691
)
87-
user_infos.add_collections([coll_1_user.Id])
88-
users = coll_1_user.users()
92+
user_infos.add_collections([collection_2.Id])
93+
users = collection_2.users()
8994
self.assertEqual(
9095
len(users),
9196
1,
9297
)
9398

99+
def test_invite_user_than_remove(self):
100+
resp = self.organization.invite("test-user-3@example.com")
101+
self.assertTrue(resp.is_success)
102+
user = self.organization.user_search(
103+
"test-user-3@example.com", force_refresh=True
104+
)
105+
self.assertIsNotNone(user)
106+
user.delete()
107+
108+
def test_add_remove_collection_cipher(self):
109+
cipher = self.test_org_ciphers[0]
110+
old_colls = cipher.CollectionIds
111+
collection_1 = self.test_colls_ids[0]
112+
cipher.add_collections(collections=[collection_1.Id])
113+
res = self.organization.ciphers(force_refresh=True)
114+
self.assertEqual(len(res), 1)
115+
self.assertEqual(len(res[0].CollectionIds), 2)
116+
cipher.update_collection(old_colls)
117+
94118
def test_deduplicate(self):
95119
# Todo build test fixtures and delete them at the end of the test
96120
return

tests/models/__init__.py

Whitespace-only changes.

tests/models/validation/__init__.py

Whitespace-only changes.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import unittest
2+
3+
from pydantic import TypeAdapter
4+
5+
from vaultwarden.models.bitwarden import (
6+
Organization,
7+
ResplistBitwarden,
8+
OrganizationUserDetails,
9+
CollectionUser,
10+
)
11+
12+
13+
class TestBitwardenModels(unittest.TestCase):
14+
@staticmethod
15+
def read_json_payload(file_path):
16+
with open(file_path, "r") as file:
17+
return file.read()
18+
19+
def test_organization(self):
20+
payload = self.read_json_payload(
21+
"tests/fixtures/test-organization/organization_camel.json"
22+
)
23+
data = Organization.model_validate_json(payload)
24+
assert data.Name == "Test Organization"
25+
26+
def test_organization_users(self):
27+
payload = self.read_json_payload(
28+
"tests/fixtures/test-organization/users_camel.json"
29+
)
30+
users = (
31+
ResplistBitwarden[OrganizationUserDetails]
32+
.model_validate_json(
33+
payload,
34+
context={"parent_id": "cda840d2-1de0-4f31-bd49-b30dacd7e8b0"},
35+
)
36+
.model_validate_json(payload)
37+
)
38+
assert len(users.Data) == 2
39+
assert users.Data[0].Email == "test-account@example.com"
40+
assert users.Data[1].Email == "test-account-2@example.com"
41+
42+
def test_organization_collections(self):
43+
payload1 = self.read_json_payload(
44+
"tests/fixtures/test-organization/collections/test-collection/users_camel.json"
45+
)
46+
payload2 = self.read_json_payload(
47+
"tests/fixtures/test-organization/collections/test-collection-2/users_camel.json"
48+
)
49+
collection1 = TypeAdapter(list[CollectionUser]).validate_json(
50+
payload1,
51+
context={"parent_id": "9ed17918-31f6-4ac5-ac82-c11541cd8a7c"},
52+
)
53+
collection2 = TypeAdapter(list[CollectionUser]).validate_json(
54+
payload2,
55+
context={"parent_id": "3c73f14f-5a01-4016-98bb-9605146a1a49"},
56+
)
57+
58+
assert len(collection1) == 0
59+
assert len(collection2) == 1
60+
61+
62+
if __name__ == "__main__":
63+
unittest.main()
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import unittest
2+
3+
from pydantic import TypeAdapter
4+
5+
from src.vaultwarden.models.sync import SyncData, VaultwardenUser
6+
from vaultwarden.models.bitwarden import (
7+
Organization,
8+
ResplistBitwarden,
9+
OrganizationCollection,
10+
)
11+
12+
13+
class TestModelCases(unittest.TestCase):
14+
@staticmethod
15+
def read_json_payload(file_path):
16+
with open(file_path, "r") as file:
17+
return file.read()
18+
19+
def test_organization(self):
20+
pascal_case_payload = self.read_json_payload(
21+
"tests/fixtures/test-organization/organization_pascal.json"
22+
)
23+
camel_case_payload = self.read_json_payload(
24+
"tests/fixtures/test-organization/organization_camel.json"
25+
)
26+
pascal = Organization.model_validate_json(pascal_case_payload)
27+
camel = Organization.model_validate_json(camel_case_payload)
28+
self.assertEqual(pascal.Name, camel.Name)
29+
30+
def test_collections(self):
31+
pascal_case_payload = self.read_json_payload(
32+
"tests/fixtures/test-organization/collections/collections_pascal.json"
33+
)
34+
pascal_collections = (
35+
ResplistBitwarden[OrganizationCollection]
36+
.model_validate_json(pascal_case_payload)
37+
.Data
38+
)
39+
camel_case_payload = self.read_json_payload(
40+
"tests/fixtures/test-organization/collections/collections_camel.json"
41+
)
42+
camel_collections = (
43+
ResplistBitwarden[OrganizationCollection]
44+
.model_validate_json(camel_case_payload)
45+
.Data
46+
)
47+
self.assertEqual(len(pascal_collections), len(camel_collections))
48+
self.assertEqual(pascal_collections[0].Name, camel_collections[0].Name)
49+
self.assertEqual(pascal_collections[1].Name, camel_collections[1].Name)
50+
51+
def test_sync_data(self):
52+
pascal_case_payload = self.read_json_payload(
53+
"tests/fixtures/test-account/sync_pascal.json"
54+
)
55+
camel_case_payload = self.read_json_payload(
56+
"tests/fixtures/test-account/sync_camel.json"
57+
)
58+
pascal = SyncData.model_validate_json(pascal_case_payload)
59+
camel = SyncData.model_validate_json(camel_case_payload)
60+
self.assertEqual(len(pascal.Ciphers), len(camel.Ciphers))
61+
self.assertEqual(len(pascal.Collections), len(camel.Collections))
62+
self.assertEqual(
63+
pascal.Collections[0].get("Name"), camel.Collections[0].get("name")
64+
)
65+
self.assertEqual(
66+
pascal.Collections[1].get("Name"), camel.Collections[1].get("name")
67+
)
68+
69+
def test_admin_users(self):
70+
pascal_case_payload = self.read_json_payload(
71+
"tests/fixtures/admin/users_pascal.json"
72+
)
73+
camel_case_payload = self.read_json_payload(
74+
"tests/fixtures/admin/users_camel.json"
75+
)
76+
pascal = TypeAdapter(list[VaultwardenUser]).validate_json(
77+
pascal_case_payload
78+
)
79+
camel = TypeAdapter(list[VaultwardenUser]).validate_json(
80+
camel_case_payload
81+
)
82+
self.assertEqual(len(pascal), len(camel))
83+
self.assertEqual(pascal[0].Name, camel[0].Name)
84+
self.assertEqual(pascal[1].Name, camel[1].Name)
85+
self.assertEqual(pascal[0].Email, camel[0].Email)
86+
self.assertEqual(pascal[1].Email, camel[1].Email)
87+
self.assertEqual(pascal[0].UserEnabled, camel[0].UserEnabled)
88+
self.assertEqual(pascal[1].UserEnabled, camel[1].UserEnabled)
89+
90+
91+
if __name__ == "__main__":
92+
unittest.main()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import unittest
2+
3+
from vaultwarden.models.sync import SyncData
4+
5+
6+
class TestSyncModels(unittest.TestCase):
7+
@staticmethod
8+
def read_json_payload(file_path):
9+
with open(file_path, "r") as file:
10+
return file.read()
11+
12+
def test_syncdata(self):
13+
payload = self.read_json_payload(
14+
"tests/fixtures/test-account/sync_camel.json"
15+
)
16+
data = SyncData.model_validate_json(payload)
17+
assert len(data.Ciphers) == 2
18+
assert len(data.Collections) == 3
19+
assert len(data.Profile.Organizations) == 1
20+
assert data.Profile.Organizations[0].Name == "Test Organization"
21+
assert len(data.Ciphers) == 2
22+
assert data.Profile.Email == "test-account@example.com"
23+
24+
25+
if __name__ == "__main__":
26+
unittest.main()

0 commit comments

Comments
 (0)