test_cli.py 13.4 KB
Newer Older
1

Markus Scheidgen's avatar
Markus Scheidgen committed
2
3
4
5
#
# Copyright The NOMAD Authors.
#
# This file is part of NOMAD. See https://nomad-lab.eu for further info.
6
7
8
9
10
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
Markus Scheidgen's avatar
Markus Scheidgen committed
11
#     http://www.apache.org/licenses/LICENSE-2.0
12
13
#
# Unless required by applicable law or agreed to in writing, software
Markus Scheidgen's avatar
Markus Scheidgen committed
14
# distributed under the License is distributed on an "AS IS" BASIS,
15
16
17
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Markus Scheidgen's avatar
Markus Scheidgen committed
18
#
19

20
import pytest
21
import click.testing
22
import json
23
import datetime
24
import time
25

26
from nomad import processing as proc, files
27
from nomad.search import search
28
from nomad.cli import cli
29
from nomad.cli.cli import POPO
30
from nomad.processing import Upload, Calc, ProcessStatus
31

32
from tests.utils import ExampleData
33
34
# TODO there is much more to test

35

36
37
38
39
def invoke_cli(*args, **kwargs):
    return click.testing.CliRunner().invoke(*args, obj=POPO(), **kwargs)


40
41
42
43
44
@pytest.mark.usefixtures('reset_config', 'nomad_logging')
class TestCli:
    def test_help(self, example_mainfile):

        start = time.time()
45
        result = invoke_cli(
46
47
48
49
50
            cli, ['--help'], catch_exceptions=False)
        assert result.exit_code == 0
        assert time.time() - start < 1


51
@pytest.mark.usefixtures('reset_config', 'nomad_logging')
Markus Scheidgen's avatar
Markus Scheidgen committed
52
53
54
class TestParse:
    def test_parser(self, example_mainfile):
        _, mainfile_path = example_mainfile
55
        result = invoke_cli(
Markus Scheidgen's avatar
Markus Scheidgen committed
56
57
58
59
            cli, ['parse', mainfile_path], catch_exceptions=False)
        assert result.exit_code == 0


60
@pytest.mark.usefixtures('reset_config', 'no_warn', 'mongo_infra', 'elastic_infra', 'raw_files_infra')
61
class TestAdmin:
62
    def test_reset(self, reset_infra):
63
        result = invoke_cli(
64
            cli, ['admin', 'reset', '--i-am-really-sure'], catch_exceptions=False)
65
66
67
        assert result.exit_code == 0

    def test_reset_not_sure(self):
68
        result = invoke_cli(
69
            cli, ['admin', 'reset'], catch_exceptions=False)
70
71
        assert result.exit_code == 1

72
73
    # TODO this has somekind of raise condition in it and the test fails every other time
    # on the CI/CD
74
75
    # def test_clean(self, published):
    #     upload_id = published.upload_id
76

77
78
79
    #     Upload.objects(upload_id=upload_id).delete()
    #     assert published.upload_files.exists()
    #     assert Calc.objects(upload_id=upload_id).first() is not None
80
    #     search.refresh()
81
    #     assert search.SearchRequest().search_parameter('upload_id', upload_id).execute()['total'] > 0
82
83
84
    #     # TODO test new index pair
    #     # assert es_search(owner=None, query=dict(upload_id=upload_id)).pagination.total == 0
    #     # assert es_search(owner=None, query=dict(upload_id=upload_id)).pagination.total == 0
85

86
    #     result = invoke_cli(
87
    #         cli, ['admin', 'clean', '--force', '--skip-es'], catch_exceptions=False)
88

89
90
91
    #     assert result.exit_code == 0
    #     assert not published.upload_files.exists()
    #     assert Calc.objects(upload_id=upload_id).first() is None
92
    #     search.refresh()
93
    #     assert search.SearchRequest().search_parameter('upload_id', upload_id).execute()['total'] > 0
94
95
    #     # TODO test new index pair
    #     # assert es_search(owner=None, query=dict(upload_id=upload_id)).pagination.total == 0
96

97
    @pytest.mark.parametrize('publish_time,dry,lifted', [
98
99
100
        (datetime.datetime.now(), False, False),
        (datetime.datetime(year=2012, month=1, day=1), True, False),
        (datetime.datetime(year=2012, month=1, day=1), False, True)])
101
    def test_lift_embargo(self, published, publish_time, dry, lifted):
102
        upload_id = published.upload_id
103
        published.publish_time = publish_time
104
105
106
107
        published.save()
        calc = Calc.objects(upload_id=upload_id).first()

        assert published.upload_files.exists()
108
        assert published.with_embargo
109
110

        assert search(owner='public', query=dict(upload_id=upload_id)).pagination.total == 0
111

112
        result = invoke_cli(
113
            cli, ['admin', 'lift-embargo'] + (['--dry'] if dry else []),
114
            catch_exceptions=False)
115
116

        assert result.exit_code == 0
David Sikter's avatar
David Sikter committed
117
        published.block_until_complete()
118
        assert not published.with_embargo == lifted
119
        assert (search(owner='public', query=dict(upload_id=upload_id)).pagination.total > 0) == lifted
120
        if lifted:
121
122
            with files.UploadFiles.get(upload_id=upload_id).read_archive(calc_id=calc.calc_id) as archive:
                assert calc.calc_id in archive
123

124
125
126
127
    def test_delete_entry(self, published):
        upload_id = published.upload_id
        calc = Calc.objects(upload_id=upload_id).first()

128
        result = invoke_cli(
129
            cli, ['admin', 'entries', 'rm', calc.calc_id], catch_exceptions=False)
130
131
132
133
134
135

        assert result.exit_code == 0
        assert 'deleting' in result.stdout
        assert Upload.objects(upload_id=upload_id).first() is not None
        assert Calc.objects(calc_id=calc.calc_id).first() is None

136

137
138
139
140
141
def transform_for_index_test(calc):
    calc.comment = 'specific'
    return calc


142
@pytest.mark.usefixtures('reset_config', 'no_warn')
143
144
class TestAdminUploads:

Markus Scheidgen's avatar
Markus Scheidgen committed
145
146
147
148
    def test_query_mongo(self, published):
        upload_id = published.upload_id

        query = dict(upload_id=upload_id)
149
        result = invoke_cli(
150
            cli, ['admin', 'uploads', '--entries-mongo-query', json.dumps(query), 'ls'],
151
            catch_exceptions=False)
Markus Scheidgen's avatar
Markus Scheidgen committed
152
153
154
155

        assert result.exit_code == 0
        assert '1 uploads selected' in result.stdout

156
    def test_ls(self, published):
157
158
        upload_id = published.upload_id

159
        result = invoke_cli(
160
            cli, ['admin', 'uploads', 'ls', upload_id], catch_exceptions=False)
161
162
163
164

        assert result.exit_code == 0
        assert '1 uploads selected' in result.stdout

165
166
167
    def test_ls_query(self, published):
        upload_id = published.upload_id

168
        result = invoke_cli(
169
            cli, ['admin', 'uploads', '--entries-es-query', f'{{"upload_id":"{upload_id}"}}', 'ls'], catch_exceptions=False)
170
171
172
        assert result.exit_code == 0
        assert '1 uploads selected' in result.stdout

173
    def test_rm(self, published):
174
175
        upload_id = published.upload_id

176
        result = invoke_cli(
177
            cli, ['admin', 'uploads', 'rm', upload_id], catch_exceptions=False)
178
179
180
181
182
183

        assert result.exit_code == 0
        assert 'deleting' in result.stdout
        assert Upload.objects(upload_id=upload_id).first() is None
        assert Calc.objects(upload_id=upload_id).first() is None

184
185
186
    def test_index(self, published):
        upload_id = published.upload_id
        calc = Calc.objects(upload_id=upload_id).first()
David Sikter's avatar
David Sikter committed
187
        calc.comment = 'specific'
188
189
        calc.save()

190
        assert search(owner='all', query=dict(comment='specific')).pagination.total == 0
191

192
        result = invoke_cli(
193
            cli, ['admin', 'uploads', 'index', upload_id], catch_exceptions=False)
194
195
196
        assert result.exit_code == 0
        assert 'index' in result.stdout

197
        assert search(owner='all', query=dict(comment='specific')).pagination.total == 1
198
199
200

    def test_index_with_transform(self, published):
        upload_id = published.upload_id
201
        assert search(owner='all', query=dict(comment='specific')).pagination.total == 0
202

203
        result = invoke_cli(
204
205
206
207
208
209
210
211
            cli, [
                'admin', 'uploads', 'index',
                '--transformer', 'tests.test_cli.transform_for_index_test',
                upload_id],
            catch_exceptions=False)
        assert result.exit_code == 0
        assert 'index' in result.stdout

212
        assert search(owner='all', query=dict(comment='specific')).pagination.total == 1
213

214
    def test_re_process(self, published, monkeypatch):
215
        monkeypatch.setattr('nomad.config.meta.version', 'test_version')
216
217
        upload_id = published.upload_id
        calc = Calc.objects(upload_id=upload_id).first()
218
        assert calc.nomad_version != 'test_version'
219

220
        result = invoke_cli(
221
            cli, ['admin', 'uploads', 'process', '--parallel', '2', upload_id], catch_exceptions=False)
222
223

        assert result.exit_code == 0
224
        assert 'processing' in result.stdout
225
        calc.reload()
226
        assert calc.nomad_version == 'test_version'
227

228
229
230
    def test_re_pack(self, published, monkeypatch):
        upload_id = published.upload_id
        calc = Calc.objects(upload_id=upload_id).first()
231
        assert published.with_embargo
232
233
        published.embargo_length = 0
        published.save()
234

235
        result = invoke_cli(
236
            cli, ['admin', 'uploads', 're-pack', upload_id], catch_exceptions=False)
237
238
239
240
241

        assert result.exit_code == 0
        assert 're-pack' in result.stdout
        calc.reload()
        upload_files = files.PublicUploadFiles(upload_id)
242
243
        for path_info in upload_files.raw_directory_list(recursive=True, files_only=True):
            with upload_files.raw_file(path_info.path) as f:
244
245
                f.read()
        for calc in Calc.objects(upload_id=upload_id):
246
247
            with upload_files.read_archive(calc.calc_id) as archive:
                assert calc.calc_id in archive
248

249
        published.reload()
250
        assert published.process_status == ProcessStatus.SUCCESS
251

252
    def test_chown(self, published: Upload, test_user, other_test_user):
253
        upload_id = published.upload_id
254
        assert published.main_author == test_user.user_id
255
256
        with published.entries_metadata() as entries_metadata:
            for entry_metadata in entries_metadata:
257
                assert entry_metadata.main_author.user_id == test_user.user_id
258

259
        result = invoke_cli(
260
            cli, ['admin', 'uploads', 'chown', other_test_user.username, upload_id], catch_exceptions=False)
261
262
263
264

        assert result.exit_code == 0
        assert 'changing' in result.stdout

265
        published.block_until_complete()
266

267
        assert published.main_author == other_test_user.user_id
268
269
        with published.entries_metadata() as entries_metadata:
            for entry_metadata in entries_metadata:
270
                assert entry_metadata.main_author.user_id == other_test_user.user_id
271

272
273
274
275
276
277
    @pytest.mark.parametrize('with_calcs,success,failure', [
        (True, False, False),
        (False, False, False),
        (True, True, False),
        (False, False, True)])
    def test_reset(self, non_empty_processed, with_calcs, success, failure):
278
279
        upload_id = non_empty_processed.upload_id

280
281
        upload = Upload.objects(upload_id=upload_id).first()
        calc = Calc.objects(upload_id=upload_id).first()
282
283
        assert upload.process_status == ProcessStatus.SUCCESS
        assert calc.process_status == ProcessStatus.SUCCESS
284
285
286
287
288
289

        args = ['admin', 'uploads', 'reset']
        if with_calcs: args.append('--with-calcs')
        if success: args.append('--success')
        if failure: args.append('--failure')
        args.append(upload_id)
290
        result = invoke_cli(cli, args, catch_exceptions=False)
291
292
293
294
295
296

        assert result.exit_code == 0
        assert 'reset' in result.stdout
        upload = Upload.objects(upload_id=upload_id).first()
        calc = Calc.objects(upload_id=upload_id).first()

297
298
299
300
        expected_state = ProcessStatus.READY
        if success: expected_state = ProcessStatus.SUCCESS
        if failure: expected_state = ProcessStatus.FAILURE
        assert upload.process_status == expected_state
301
        if not with_calcs:
302
            assert calc.process_status == ProcessStatus.SUCCESS
303
        else:
304
            assert calc.process_status == expected_state
305

306
307
308
309
310
311
312
313
314
315
316
317
    @pytest.mark.parametrize('indexed', [True, False])
    def test_integrity_entry_index(self, test_user, mongo, elastic, indexed):
        data = ExampleData(main_author=test_user)
        data.create_upload(upload_id='test_upload')
        data.create_entry(upload_id='test_upload')
        data.save(with_es=indexed, with_files=False)

        result = invoke_cli(cli, 'admin uploads integrity entry-index', catch_exceptions=True)

        assert result.exit_code == 0
        assert ('test_upload' in result.output) != indexed

318

319
@pytest.mark.usefixtures('reset_config')
320
321
class TestClient:

322
323
    def test_upload(self, non_empty_example_upload, admin_user, proc_infra, client_with_api_v1):
        result = invoke_cli(
Markus Scheidgen's avatar
Markus Scheidgen committed
324
            cli,
325
326
            [
                'client', '-u', admin_user.username, '--token-via-api',
327
                'upload', '--upload-name', 'test_upload', '--local-path',
328
                non_empty_example_upload],
329
            catch_exceptions=False)
Markus Scheidgen's avatar
Markus Scheidgen committed
330

331
        assert result.exit_code == 0, result.output
Markus Scheidgen's avatar
Markus Scheidgen committed
332
        assert '1/0/1' in result.output
333
        assert proc.Upload.objects(upload_name='test_upload').first() is not None
Markus Scheidgen's avatar
Markus Scheidgen committed
334

335
336
    def test_local(self, published_wo_user_metadata, client_with_api_v1):
        result = invoke_cli(
Markus Scheidgen's avatar
Markus Scheidgen committed
337
            cli,
338
339
            ['client', 'local', published_wo_user_metadata.calcs[0].calc_id],
            catch_exceptions=True)
Markus Scheidgen's avatar
Markus Scheidgen committed
340

341
        assert result.exit_code == 0, result.output
Markus Scheidgen's avatar
Markus Scheidgen committed
342

343
    @pytest.mark.skip('Disabled. Tested code is temporaely commented.')
Markus Scheidgen's avatar
Markus Scheidgen committed
344
    def test_statistics(self):
345

346
        result = invoke_cli(
347
            cli, ['client', 'statistics-table'], catch_exceptions=True)
348
349
350

        assert result.exit_code == 0, result.output
        assert 'Calculations, e.g. total energies' in result.output
351
        assert 'Geometries' in result.output
352
353
354
355
356
        assert 'Bulk crystals' in result.output
        assert '2D / Surfaces' in result.output
        assert 'Atoms / Molecules' in result.output
        assert 'DOS' in result.output
        assert 'Band structures' in result.output