kleinkram 0.38.1.dev20241125112529__py3-none-any.whl → 0.38.1.dev20250113080249__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.
Potentially problematic release.
This version of kleinkram might be problematic. Click here for more details.
- kleinkram/__init__.py +33 -2
- kleinkram/api/client.py +21 -16
- kleinkram/api/deser.py +165 -0
- kleinkram/api/file_transfer.py +13 -24
- kleinkram/api/pagination.py +56 -0
- kleinkram/api/query.py +111 -0
- kleinkram/api/routes.py +270 -97
- kleinkram/auth.py +21 -20
- kleinkram/cli/__init__.py +0 -0
- kleinkram/{commands/download.py → cli/_download.py} +18 -44
- kleinkram/cli/_endpoint.py +58 -0
- kleinkram/{commands/list.py → cli/_list.py} +25 -38
- kleinkram/cli/_mission.py +153 -0
- kleinkram/cli/_project.py +99 -0
- kleinkram/cli/_upload.py +84 -0
- kleinkram/cli/_verify.py +56 -0
- kleinkram/{app.py → cli/app.py} +50 -22
- kleinkram/cli/error_handling.py +44 -0
- kleinkram/config.py +141 -107
- kleinkram/core.py +251 -3
- kleinkram/errors.py +13 -45
- kleinkram/main.py +1 -1
- kleinkram/models.py +48 -149
- kleinkram/printing.py +325 -0
- kleinkram/py.typed +0 -0
- kleinkram/types.py +9 -0
- kleinkram/utils.py +82 -27
- kleinkram/wrappers.py +401 -0
- {kleinkram-0.38.1.dev20241125112529.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/METADATA +3 -3
- kleinkram-0.38.1.dev20250113080249.dist-info/RECORD +48 -0
- {kleinkram-0.38.1.dev20241125112529.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/WHEEL +1 -1
- {kleinkram-0.38.1.dev20241125112529.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/top_level.txt +1 -0
- testing/__init__.py +0 -0
- testing/backend_fixtures.py +69 -0
- tests/conftest.py +7 -0
- tests/test_config.py +115 -0
- tests/test_core.py +165 -0
- tests/test_end_to_end.py +29 -39
- tests/test_fixtures.py +29 -0
- tests/test_printing.py +62 -0
- tests/test_query.py +138 -0
- tests/test_utils.py +34 -24
- tests/test_wrappers.py +71 -0
- kleinkram/api/parsing.py +0 -86
- kleinkram/commands/__init__.py +0 -1
- kleinkram/commands/endpoint.py +0 -62
- kleinkram/commands/mission.py +0 -69
- kleinkram/commands/project.py +0 -24
- kleinkram/commands/upload.py +0 -164
- kleinkram/commands/verify.py +0 -142
- kleinkram/consts.py +0 -8
- kleinkram/enums.py +0 -10
- kleinkram/resources.py +0 -158
- kleinkram-0.38.1.dev20241125112529.dist-info/LICENSE +0 -674
- kleinkram-0.38.1.dev20241125112529.dist-info/RECORD +0 -37
- tests/test_resources.py +0 -137
- {kleinkram-0.38.1.dev20241125112529.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from secrets import token_hex
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from kleinkram import create_mission
|
|
10
|
+
from kleinkram import create_project
|
|
11
|
+
from kleinkram import delete_mission
|
|
12
|
+
from kleinkram import delete_project
|
|
13
|
+
from kleinkram import list_files
|
|
14
|
+
from kleinkram import list_missions
|
|
15
|
+
from kleinkram import list_projects
|
|
16
|
+
from kleinkram import upload
|
|
17
|
+
|
|
18
|
+
# we expect the mission files to be in this folder that is not commited to the repo
|
|
19
|
+
DATA_PATH = Path(__file__).parent.parent / "data" / "testing"
|
|
20
|
+
DATA_FILES = [
|
|
21
|
+
DATA_PATH / "10_KB.bag",
|
|
22
|
+
DATA_PATH / "50_KB.bag",
|
|
23
|
+
DATA_PATH / "1_MB.bag",
|
|
24
|
+
DATA_PATH / "17_MB.bag",
|
|
25
|
+
DATA_PATH / "125_MB.bag",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
PROJECT_DESCRIPTION = "This is a test project"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
WAIT_BEOFORE_DELETION = 5
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture(scope="session")
|
|
35
|
+
def project():
|
|
36
|
+
project_name = token_hex(8)
|
|
37
|
+
|
|
38
|
+
print("here")
|
|
39
|
+
create_project(project_name, description="This is a test project")
|
|
40
|
+
|
|
41
|
+
project = list_projects(project_names=[project_name])[0]
|
|
42
|
+
|
|
43
|
+
yield project
|
|
44
|
+
|
|
45
|
+
time.sleep(WAIT_BEOFORE_DELETION)
|
|
46
|
+
delete_project(project.id)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.fixture
|
|
50
|
+
def mission(project):
|
|
51
|
+
mission_name = token_hex(8)
|
|
52
|
+
upload(
|
|
53
|
+
mission_name=mission_name,
|
|
54
|
+
project_id=project.id,
|
|
55
|
+
files=DATA_FILES,
|
|
56
|
+
create=True,
|
|
57
|
+
)
|
|
58
|
+
mission = list_missions(project_ids=[project.id], mission_names=[mission_name])[0]
|
|
59
|
+
|
|
60
|
+
yield mission
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pytest.fixture
|
|
64
|
+
def empty_mission(project):
|
|
65
|
+
mission_name = token_hex(8)
|
|
66
|
+
create_mission(mission_name, project.id, metadata={})
|
|
67
|
+
mission = list_missions(project_ids=[project.id], mission_names=[mission_name])[0]
|
|
68
|
+
|
|
69
|
+
yield mission
|
tests/conftest.py
ADDED
tests/test_config.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from tempfile import TemporaryDirectory
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from kleinkram.config import Config
|
|
9
|
+
from kleinkram.config import Endpoint
|
|
10
|
+
from kleinkram.config import _load_config
|
|
11
|
+
from kleinkram.config import add_endpoint
|
|
12
|
+
from kleinkram.config import check_config_compatibility
|
|
13
|
+
from kleinkram.config import endpoint_table
|
|
14
|
+
from kleinkram.config import get_config
|
|
15
|
+
from kleinkram.config import get_env
|
|
16
|
+
from kleinkram.config import get_shared_state
|
|
17
|
+
from kleinkram.config import save_config
|
|
18
|
+
from kleinkram.config import select_endpoint
|
|
19
|
+
|
|
20
|
+
CONFIG_FILENAME = "kleinkram.json"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def config_path():
|
|
25
|
+
with TemporaryDirectory() as tmpdir:
|
|
26
|
+
yield Path(tmpdir) / CONFIG_FILENAME
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_load_config_default(config_path):
|
|
30
|
+
config = _load_config(path=config_path)
|
|
31
|
+
|
|
32
|
+
assert not config_path.exists()
|
|
33
|
+
assert Config() == config
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_save_and_load_config(config_path):
|
|
37
|
+
|
|
38
|
+
config = Config(version="foo")
|
|
39
|
+
|
|
40
|
+
assert not config_path.exists()
|
|
41
|
+
save_config(config, path=config_path)
|
|
42
|
+
assert config_path.exists()
|
|
43
|
+
|
|
44
|
+
loaded_config = _load_config(path=config_path)
|
|
45
|
+
assert loaded_config == config
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_get_config_default(config_path):
|
|
49
|
+
config = get_config(path=config_path)
|
|
50
|
+
|
|
51
|
+
assert not config_path.exists()
|
|
52
|
+
assert Config() == config
|
|
53
|
+
assert config is get_config(path=config_path)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_get_config_after_save(config_path):
|
|
57
|
+
config = get_config(path=config_path)
|
|
58
|
+
config.version = "foo"
|
|
59
|
+
save_config(config, path=config_path)
|
|
60
|
+
|
|
61
|
+
assert config is get_config(path=config_path)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_get_shared_state():
|
|
65
|
+
state = get_shared_state()
|
|
66
|
+
assert state is get_shared_state()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_select_endpoint(config_path):
|
|
70
|
+
config = get_config(path=config_path)
|
|
71
|
+
save_config(config, path=config_path)
|
|
72
|
+
assert config.selected_endpoint == get_env().value
|
|
73
|
+
|
|
74
|
+
# select existing endpoint
|
|
75
|
+
select_endpoint(config, "prod", path=config_path)
|
|
76
|
+
assert config.selected_endpoint == "prod"
|
|
77
|
+
assert config == _load_config(path=config_path)
|
|
78
|
+
|
|
79
|
+
with pytest.raises(ValueError):
|
|
80
|
+
select_endpoint(config, "foo", path=config_path)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_add_endpoint(config_path):
|
|
84
|
+
config = get_config(path=config_path)
|
|
85
|
+
save_config(config, path=config_path)
|
|
86
|
+
assert config.selected_endpoint == get_env().value
|
|
87
|
+
|
|
88
|
+
with pytest.raises(ValueError):
|
|
89
|
+
select_endpoint(config, "foo", path=config_path)
|
|
90
|
+
|
|
91
|
+
ep = Endpoint("foo", "api", "s3")
|
|
92
|
+
add_endpoint(config, ep, path=config_path)
|
|
93
|
+
assert config.selected_endpoint == "foo"
|
|
94
|
+
assert config.endpoint == ep
|
|
95
|
+
assert config == _load_config(path=config_path)
|
|
96
|
+
|
|
97
|
+
select_endpoint(config, "dev", path=config_path)
|
|
98
|
+
assert config.selected_endpoint == "dev"
|
|
99
|
+
select_endpoint(config, "foo", path=config_path)
|
|
100
|
+
assert config.selected_endpoint == "foo"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_endpoint_table():
|
|
104
|
+
config = Config()
|
|
105
|
+
table = endpoint_table(config)
|
|
106
|
+
|
|
107
|
+
assert [c.header for c in table.columns] == ["Name", "API", "S3"]
|
|
108
|
+
assert len(table.rows) == 3
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_check_config_compatiblity(config_path):
|
|
112
|
+
assert check_config_compatibility(path=config_path)
|
|
113
|
+
with open(config_path, "w") as f:
|
|
114
|
+
f.write("foo") # invalid config
|
|
115
|
+
assert not check_config_compatibility(path=config_path)
|
tests/test_core.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from secrets import token_hex
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
import kleinkram.api.routes
|
|
9
|
+
import kleinkram.core
|
|
10
|
+
import kleinkram.errors
|
|
11
|
+
from kleinkram import list_files
|
|
12
|
+
from kleinkram import list_missions
|
|
13
|
+
from kleinkram import list_projects
|
|
14
|
+
from kleinkram.api.client import AuthenticatedClient
|
|
15
|
+
from kleinkram.api.query import FileQuery
|
|
16
|
+
from kleinkram.api.query import MissionQuery
|
|
17
|
+
from kleinkram.api.query import ProjectQuery
|
|
18
|
+
from kleinkram.errors import MissionNotFound
|
|
19
|
+
from kleinkram.models import FileVerificationStatus
|
|
20
|
+
from testing.backend_fixtures import DATA_FILES
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.slow
|
|
24
|
+
def test_upload_create(project):
|
|
25
|
+
mission_name = token_hex(8)
|
|
26
|
+
mission_query = MissionQuery(
|
|
27
|
+
patterns=[mission_name], project_query=ProjectQuery(ids=[project.id])
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
client = AuthenticatedClient()
|
|
31
|
+
kleinkram.core.upload(
|
|
32
|
+
client=client, query=mission_query, file_paths=DATA_FILES, create=True
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
mission = list_missions(mission_names=[mission_name])[0]
|
|
36
|
+
assert mission.project_id == project.id
|
|
37
|
+
assert mission.name == mission_name
|
|
38
|
+
|
|
39
|
+
files = list_files(mission_ids=[mission.id])
|
|
40
|
+
assert set([file.name for file in files if file.name.endswith(".bag")]) == set(
|
|
41
|
+
[file.name for file in DATA_FILES]
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@pytest.mark.slow
|
|
46
|
+
def test_upload_no_create(project):
|
|
47
|
+
mission_name = token_hex(8)
|
|
48
|
+
mission_query = MissionQuery(
|
|
49
|
+
patterns=[mission_name], project_query=ProjectQuery(ids=[project.id])
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
client = AuthenticatedClient()
|
|
53
|
+
with pytest.raises(MissionNotFound):
|
|
54
|
+
kleinkram.core.upload(
|
|
55
|
+
client=client, query=mission_query, file_paths=DATA_FILES, create=False
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.slow
|
|
60
|
+
def test_upload_to_existing_mission(empty_mission):
|
|
61
|
+
mission_query = MissionQuery(ids=[empty_mission.id])
|
|
62
|
+
|
|
63
|
+
client = AuthenticatedClient()
|
|
64
|
+
kleinkram.core.upload(client=client, query=mission_query, file_paths=DATA_FILES)
|
|
65
|
+
|
|
66
|
+
files = list_files(mission_ids=[empty_mission.id])
|
|
67
|
+
assert set([file.name for file in files if file.name.endswith(".bag")]) == set(
|
|
68
|
+
[file.name for file in DATA_FILES]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@pytest.mark.slow
|
|
73
|
+
def test_delete_existing_files(mission):
|
|
74
|
+
client = AuthenticatedClient()
|
|
75
|
+
files = list_files(mission_ids=[mission.id], file_names=["*.bag"])
|
|
76
|
+
kleinkram.core.delete_files(client=client, file_ids=[f.id for f in files])
|
|
77
|
+
assert not list_files(mission_ids=[mission.id], file_names=["*.bag"])
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@pytest.mark.slow
|
|
81
|
+
def test_delete_non_existing_files():
|
|
82
|
+
client = AuthenticatedClient()
|
|
83
|
+
|
|
84
|
+
with pytest.raises(kleinkram.errors.FileNotFound):
|
|
85
|
+
kleinkram.core.delete_files(client=client, file_ids=[uuid4()])
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@pytest.mark.slow
|
|
89
|
+
def test_create_update_delete_mission(project):
|
|
90
|
+
mission_name = token_hex(8)
|
|
91
|
+
|
|
92
|
+
client = AuthenticatedClient()
|
|
93
|
+
kleinkram.api.routes._create_mission(client, project.id, mission_name)
|
|
94
|
+
|
|
95
|
+
mission = list_missions(mission_names=[mission_name])[0]
|
|
96
|
+
|
|
97
|
+
assert mission.project_id == project.id
|
|
98
|
+
assert mission.name == mission_name
|
|
99
|
+
|
|
100
|
+
assert list_files(mission_ids=[mission.id]) == []
|
|
101
|
+
|
|
102
|
+
# TODO test update, for this we would need to add metadata types to the backend
|
|
103
|
+
|
|
104
|
+
kleinkram.core.delete_mission(client=client, mission_id=mission.id)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@pytest.mark.slow
|
|
108
|
+
def test_create_update_delete_project():
|
|
109
|
+
project_name = token_hex(8)
|
|
110
|
+
|
|
111
|
+
client = AuthenticatedClient()
|
|
112
|
+
project_id = kleinkram.api.routes._create_project(client, project_name, "test")
|
|
113
|
+
project = list_projects(project_ids=[project_id])[0]
|
|
114
|
+
|
|
115
|
+
assert list_missions(project_ids=[project.id]) == []
|
|
116
|
+
assert list_files(project_ids=[project.id]) == []
|
|
117
|
+
|
|
118
|
+
assert project.name == project_name
|
|
119
|
+
assert project.description == "test"
|
|
120
|
+
|
|
121
|
+
new_name = token_hex(8)
|
|
122
|
+
kleinkram.core.update_project(
|
|
123
|
+
client=client, project_id=project.id, new_name=new_name, description="new desc"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
project = list_projects(project_ids=[project.id])[0]
|
|
127
|
+
assert project.name == new_name
|
|
128
|
+
assert project.description == "new desc"
|
|
129
|
+
|
|
130
|
+
kleinkram.core.delete_project(client=client, project_id=project.id)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@pytest.mark.slow
|
|
134
|
+
def test_download(mission, tmp_path):
|
|
135
|
+
client = AuthenticatedClient()
|
|
136
|
+
|
|
137
|
+
query = FileQuery(mission_query=MissionQuery(ids=[mission.id]), patterns=["*.bag"])
|
|
138
|
+
kleinkram.core.download(client=client, query=query, base_dir=tmp_path)
|
|
139
|
+
files = list_files(mission_ids=[mission.id], file_names=["*.bag"])
|
|
140
|
+
|
|
141
|
+
assert set([f.name for f in tmp_path.iterdir()]) == set([f.name for f in files])
|
|
142
|
+
|
|
143
|
+
for file in files:
|
|
144
|
+
assert (tmp_path / file.name).stat().st_size == file.size
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@pytest.mark.slow
|
|
148
|
+
def test_verify(mission):
|
|
149
|
+
client = AuthenticatedClient()
|
|
150
|
+
query = MissionQuery(ids=[mission.id])
|
|
151
|
+
|
|
152
|
+
verify_status = kleinkram.core.verify(
|
|
153
|
+
client=client, query=query, file_paths=DATA_FILES, skip_hash=True
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
assert all(
|
|
157
|
+
status == FileVerificationStatus.UPLAODED for status in verify_status.values()
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@pytest.mark.slow
|
|
162
|
+
def test_update_file():
|
|
163
|
+
client = AuthenticatedClient()
|
|
164
|
+
with pytest.raises(NotImplementedError):
|
|
165
|
+
kleinkram.core.update_file(client=client, file_id=uuid4())
|
tests/test_end_to_end.py
CHANGED
|
@@ -6,18 +6,16 @@ import shutil
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
|
-
from kleinkram.api.routes import _get_api_version
|
|
10
9
|
from rich.console import Console
|
|
11
10
|
from rich.text import Text
|
|
12
11
|
|
|
12
|
+
from kleinkram.api.routes import _get_api_version
|
|
13
|
+
|
|
13
14
|
VERBOSE = True
|
|
14
15
|
|
|
15
16
|
CLI = "klein"
|
|
16
17
|
PROJECT_NAME = "automated-testing"
|
|
17
|
-
|
|
18
|
-
DATA_DIR = Path(__file__).parent.parent / "data" / "testing"
|
|
19
|
-
_IN_DIR = DATA_DIR / "in"
|
|
20
|
-
_OUT_DIR = DATA_DIR / "out"
|
|
18
|
+
DATA_PATH = Path(__file__).parent.parent / "data" / "testing"
|
|
21
19
|
|
|
22
20
|
|
|
23
21
|
@pytest.fixture(scope="session")
|
|
@@ -30,25 +28,6 @@ def api():
|
|
|
30
28
|
return False
|
|
31
29
|
|
|
32
30
|
|
|
33
|
-
@pytest.fixture(scope="session")
|
|
34
|
-
def in_dir():
|
|
35
|
-
return _IN_DIR
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@pytest.fixture(scope="session")
|
|
39
|
-
def out_dir():
|
|
40
|
-
try:
|
|
41
|
-
_OUT_DIR.mkdir(exist_ok=True)
|
|
42
|
-
yield _OUT_DIR
|
|
43
|
-
finally:
|
|
44
|
-
shutil.rmtree(_OUT_DIR)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
@pytest.fixture(scope="session")
|
|
48
|
-
def name():
|
|
49
|
-
return secrets.token_hex(8)
|
|
50
|
-
|
|
51
|
-
|
|
52
31
|
def run_cmd(command, *, verbose=VERBOSE):
|
|
53
32
|
msg = ("\n", "#" * 50, "\n\n", "running command:", Text(command, style="bold"))
|
|
54
33
|
Console().print(*msg)
|
|
@@ -61,45 +40,56 @@ def run_cmd(command, *, verbose=VERBOSE):
|
|
|
61
40
|
|
|
62
41
|
|
|
63
42
|
@pytest.mark.slow
|
|
64
|
-
def test_upload_verify_update_download_mission(
|
|
43
|
+
def test_upload_verify_update_download_mission(project, tmp_path, api):
|
|
65
44
|
assert api
|
|
66
45
|
|
|
67
|
-
|
|
68
|
-
|
|
46
|
+
mission_name = secrets.token_hex(8)
|
|
47
|
+
upload = f"{CLI} upload -p {project.name} -m {mission_name} --create {DATA_PATH.absolute()}/*.bag"
|
|
48
|
+
verify = (
|
|
49
|
+
f"{CLI} verify -p {project.name} -m {mission_name} {DATA_PATH.absolute()}/*.bag"
|
|
69
50
|
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
download = f"{CLI} download -p {PROJECT_NAME} -m {name} --dest {out_dir.absolute()}"
|
|
51
|
+
# update = f"{CLI} mission update -p {project.name} -m {mission_name} --metadata {DATA_PATH.absolute()}/metadata.yaml"
|
|
52
|
+
download = f"{CLI} download -p {project.name} -m {mission_name} --dest {tmp_path.absolute()}"
|
|
73
53
|
|
|
74
54
|
assert run_cmd(upload) == 0
|
|
75
55
|
assert run_cmd(verify) == 0
|
|
76
|
-
assert run_cmd(update) == 0
|
|
56
|
+
# assert run_cmd(update) == 0
|
|
77
57
|
assert run_cmd(download) == 0
|
|
78
58
|
|
|
79
59
|
|
|
80
60
|
@pytest.mark.slow
|
|
81
|
-
def test_list_files(api):
|
|
61
|
+
def test_list_files(project, mission, api):
|
|
82
62
|
assert api
|
|
83
|
-
assert run_cmd(f"{CLI} list files -p {
|
|
84
|
-
assert run_cmd(f"{CLI} list files -p {
|
|
63
|
+
assert run_cmd(f"{CLI} list files -p {project.name}") == 0
|
|
64
|
+
assert run_cmd(f"{CLI} list files -p {project.name} -m {mission.name}") == 0
|
|
85
65
|
assert run_cmd(f"{CLI} list files") == 0
|
|
86
|
-
assert run_cmd(f"{CLI} list files -p {
|
|
66
|
+
assert run_cmd(f"{CLI} list files -p {mission.name}") == 0
|
|
87
67
|
assert run_cmd(f'{CLI} list files -p "*" -m "*" "*"') == 0
|
|
88
68
|
|
|
89
69
|
|
|
90
70
|
@pytest.mark.slow
|
|
91
|
-
def test_list_missions(api):
|
|
71
|
+
def test_list_missions(api, project, mission):
|
|
92
72
|
assert api
|
|
93
|
-
|
|
73
|
+
|
|
74
|
+
assert run_cmd(f"{CLI} list missions -p {project.name} {mission.name}") == 0
|
|
75
|
+
assert run_cmd(f"{CLI} list missions -p {project.name} {secrets.token_hex(8)}") == 0
|
|
76
|
+
assert run_cmd(f"{CLI} list missions -p {project.name} {mission.id}") == 0
|
|
77
|
+
assert run_cmd(f"{CLI} list missions {secrets.token_hex(8)}") == 0
|
|
78
|
+
assert run_cmd(f"{CLI} list missions {mission.id}") == 0
|
|
79
|
+
assert run_cmd(f"{CLI} list missions {mission.name}") == 0
|
|
80
|
+
|
|
81
|
+
assert run_cmd(f"{CLI} list missions -p {project.name}") == 0
|
|
82
|
+
assert run_cmd(f"{CLI} list missions -p {project.id}") == 0
|
|
94
83
|
assert run_cmd(f"{CLI} list missions -p {secrets.token_hex(8)}") == 0
|
|
95
84
|
assert run_cmd(f"{CLI} list missions") == 0
|
|
96
85
|
assert run_cmd(f'{CLI} list missions -p "*" "*"') == 0
|
|
97
86
|
|
|
98
87
|
|
|
99
88
|
@pytest.mark.slow
|
|
100
|
-
def test_list_projects(api):
|
|
89
|
+
def test_list_projects(api, project):
|
|
101
90
|
assert api
|
|
102
91
|
assert run_cmd(f"{CLI} list projects") == 0
|
|
103
|
-
assert run_cmd(f"{CLI} list projects {
|
|
92
|
+
assert run_cmd(f"{CLI} list projects {project.name}") == 0
|
|
104
93
|
assert run_cmd(f"{CLI} list projects {secrets.token_hex(8)}") == 0
|
|
94
|
+
assert run_cmd(f"{CLI} list projects {project.id}") == 0
|
|
105
95
|
assert run_cmd(f'{CLI} list projects "*"') == 0
|
tests/test_fixtures.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from kleinkram import list_files
|
|
4
|
+
from kleinkram import list_missions
|
|
5
|
+
from kleinkram import list_projects
|
|
6
|
+
from testing.backend_fixtures import DATA_FILES
|
|
7
|
+
from testing.backend_fixtures import PROJECT_DESCRIPTION
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_project_fixture(project):
|
|
11
|
+
assert list_projects(project_ids=[project.id])[0].id == project.id
|
|
12
|
+
assert project.description == PROJECT_DESCRIPTION
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_mission_fixture(mission, project):
|
|
16
|
+
assert mission.project_id == project.id
|
|
17
|
+
assert list_missions(mission_ids=[mission.id])[0].id == mission.id
|
|
18
|
+
|
|
19
|
+
files = list_files(mission_ids=[mission.id])
|
|
20
|
+
|
|
21
|
+
assert set([file.name for file in files if file.name.endswith(".bag")]) == set(
|
|
22
|
+
[file.name for file in DATA_FILES]
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_empty_mission_fixture(empty_mission, project):
|
|
27
|
+
assert empty_mission.project_id == project.id
|
|
28
|
+
assert list_missions(mission_ids=[empty_mission.id])[0].id == empty_mission.id
|
|
29
|
+
assert not list_files(mission_ids=[empty_mission.id])
|
tests/test_printing.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from kleinkram.models import MetadataValue
|
|
8
|
+
from kleinkram.models import MetadataValueType
|
|
9
|
+
from kleinkram.printing import format_bytes
|
|
10
|
+
from kleinkram.printing import parse_metadata_value
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_format_bytes():
|
|
14
|
+
assert format_bytes(0) == "0 B"
|
|
15
|
+
assert format_bytes(1) == "1 B"
|
|
16
|
+
assert format_bytes(1000) == "1000 B"
|
|
17
|
+
assert format_bytes(1024) == "1.00 KB"
|
|
18
|
+
assert format_bytes(1025) == "1.00 KB"
|
|
19
|
+
assert format_bytes(2048) == "2.00 KB"
|
|
20
|
+
assert format_bytes(2**20) == "1.00 MB"
|
|
21
|
+
assert format_bytes(2**30) == "1.00 GB"
|
|
22
|
+
assert format_bytes(2**40) == "1.00 TB"
|
|
23
|
+
assert format_bytes(2**50) == "1.00 PB"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_parse_metadata_value():
|
|
27
|
+
mv = MetadataValue(type_=MetadataValueType.STRING, value="foo")
|
|
28
|
+
assert parse_metadata_value(mv) == "foo"
|
|
29
|
+
mv = MetadataValue(type_=MetadataValueType.LINK, value="foo")
|
|
30
|
+
assert parse_metadata_value(mv) == "foo"
|
|
31
|
+
mv = MetadataValue(type_=MetadataValueType.LOCATION, value="foo")
|
|
32
|
+
assert parse_metadata_value(mv) == "foo"
|
|
33
|
+
mv = MetadataValue(type_=MetadataValueType.NUMBER, value="1")
|
|
34
|
+
assert parse_metadata_value(mv) == 1.0
|
|
35
|
+
mv = MetadataValue(type_=MetadataValueType.BOOLEAN, value="true")
|
|
36
|
+
assert parse_metadata_value(mv) is True # noqa
|
|
37
|
+
mv = MetadataValue(type_=MetadataValueType.BOOLEAN, value="false")
|
|
38
|
+
assert parse_metadata_value(mv) is False # noqa
|
|
39
|
+
mv = MetadataValue(type_=MetadataValueType.DATE, value="2021-01-01T00:00:00Z")
|
|
40
|
+
assert parse_metadata_value(mv) == datetime.datetime(
|
|
41
|
+
2021, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@pytest.mark.skip
|
|
46
|
+
def test_projects_to_table(): ...
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.mark.skip
|
|
50
|
+
def test_missions_to_table(): ...
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.mark.skip
|
|
54
|
+
def test_files_to_table(): ...
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@pytest.mark.skip
|
|
58
|
+
def test_mission_info_table(): ...
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.mark.skip
|
|
62
|
+
def test_project_info_table(): ...
|