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
33

# TODO there is much more to test

34

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


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

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


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


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

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

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

76
77
78
    #     Upload.objects(upload_id=upload_id).delete()
    #     assert published.upload_files.exists()
    #     assert Calc.objects(upload_id=upload_id).first() is not None
79
    #     search.refresh()
80
    #     assert search.SearchRequest().search_parameter('upload_id', upload_id).execute()['total'] > 0
81
82
83
    #     # 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
84

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

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

96
    @pytest.mark.parametrize('publish_time,dry,lifted', [
97
98
99
        (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)])
100
    def test_lift_embargo(self, published, publish_time, dry, lifted):
101
        upload_id = published.upload_id
102
        published.publish_time = publish_time
103
104
105
106
        published.save()
        calc = Calc.objects(upload_id=upload_id).first()

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

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

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

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

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

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

        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

135

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


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

144
    @pytest.mark.parametrize('programs, count', [
145
146
147
        (['VASP'], 1),
        (['doesNotExist'], 0),
        (['VASP', 'doesNotExist'], 1)])
148
149
150
151
152
    def test_uploads_program(self, published, programs, count):
        programs_args = []
        for program in programs:
            programs_args.append('--program-name')
            programs_args.append(program)
153
        result = invoke_cli(
154
            cli, ['admin', 'uploads'] + programs_args + ['ls'], catch_exceptions=False)
155
156
157
158

        assert result.exit_code == 0
        assert '%d uploads selected' % count in result.stdout

Markus Scheidgen's avatar
Markus Scheidgen committed
159
160
161
162
    def test_query_mongo(self, published):
        upload_id = published.upload_id

        query = dict(upload_id=upload_id)
163
        result = invoke_cli(
Markus Scheidgen's avatar
Markus Scheidgen committed
164
            cli, ['admin', 'uploads', '--query-mongo', 'ls', json.dumps(query)],
165
            catch_exceptions=False)
Markus Scheidgen's avatar
Markus Scheidgen committed
166
167
168
169

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

170
    def test_ls(self, published):
171
172
        upload_id = published.upload_id

173
        result = invoke_cli(
174
            cli, ['admin', 'uploads', 'ls', upload_id], catch_exceptions=False)
175
176
177
178

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

179
180
181
    def test_ls_query(self, published):
        upload_id = published.upload_id

182
        result = invoke_cli(
183
            cli, ['admin', 'uploads', 'ls', '{"match":{"upload_id":"%s"}}' % upload_id], catch_exceptions=False)
184
185
186
        assert result.exit_code == 0
        assert '1 uploads selected' in result.stdout

187
    def test_rm(self, published):
188
189
        upload_id = published.upload_id

190
        result = invoke_cli(
191
            cli, ['admin', 'uploads', 'rm', upload_id], catch_exceptions=False)
192
193
194
195
196
197

        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

198
199
200
    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
201
        calc.comment = 'specific'
202
203
        calc.save()

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

206
        result = invoke_cli(
207
            cli, ['admin', 'uploads', 'index', upload_id], catch_exceptions=False)
208
209
210
        assert result.exit_code == 0
        assert 'index' in result.stdout

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

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

217
        result = invoke_cli(
218
219
220
221
222
223
224
225
            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

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

228
    def test_re_process(self, published, monkeypatch):
229
        monkeypatch.setattr('nomad.config.meta.version', 'test_version')
230
231
        upload_id = published.upload_id
        calc = Calc.objects(upload_id=upload_id).first()
232
        assert calc.nomad_version != 'test_version'
233

234
        result = invoke_cli(
235
            cli, ['admin', 'uploads', 're-process', '--parallel', '2', upload_id], catch_exceptions=False)
236
237

        assert result.exit_code == 0
238
        assert 'processing' in result.stdout
239
        calc.reload()
240
        assert calc.nomad_version == 'test_version'
241

242
243
244
    def test_re_pack(self, published, monkeypatch):
        upload_id = published.upload_id
        calc = Calc.objects(upload_id=upload_id).first()
245
        assert published.with_embargo
246
247
        published.embargo_length = 0
        published.save()
248

249
        result = invoke_cli(
250
            cli, ['admin', 'uploads', 're-pack', '--parallel', '2', upload_id], catch_exceptions=False)
251
252
253
254
255

        assert result.exit_code == 0
        assert 're-pack' in result.stdout
        calc.reload()
        upload_files = files.PublicUploadFiles(upload_id)
256
257
        for path_info in upload_files.raw_directory_list(recursive=True, files_only=True):
            with upload_files.raw_file(path_info.path) as f:
258
259
                f.read()
        for calc in Calc.objects(upload_id=upload_id):
260
261
            with upload_files.read_archive(calc.calc_id) as archive:
                assert calc.calc_id in archive
262

263
        published.reload()
264
        assert published.process_status == ProcessStatus.SUCCESS
265

266
    def test_chown(self, published: Upload, test_user, other_test_user):
267
        upload_id = published.upload_id
268
        assert published.main_author == test_user.user_id
269
270
        with published.entries_metadata() as entries_metadata:
            for entry_metadata in entries_metadata:
271
                assert entry_metadata.main_author.user_id == test_user.user_id
272

273
        result = invoke_cli(
274
            cli, ['admin', 'uploads', 'chown', other_test_user.username, upload_id], catch_exceptions=False)
275
276
277
278

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

279
        published.block_until_complete()
280

281
        assert published.main_author == other_test_user.user_id
282
283
        with published.entries_metadata() as entries_metadata:
            for entry_metadata in entries_metadata:
284
                assert entry_metadata.main_author.user_id == other_test_user.user_id
285

286
287
288
289
290
291
    @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):
292
293
        upload_id = non_empty_processed.upload_id

294
295
        upload = Upload.objects(upload_id=upload_id).first()
        calc = Calc.objects(upload_id=upload_id).first()
296
297
        assert upload.process_status == ProcessStatus.SUCCESS
        assert calc.process_status == ProcessStatus.SUCCESS
298
299
300
301
302
303

        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)
304
        result = invoke_cli(cli, args, catch_exceptions=False)
305
306
307
308
309
310

        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()

311
312
313
314
        expected_state = ProcessStatus.READY
        if success: expected_state = ProcessStatus.SUCCESS
        if failure: expected_state = ProcessStatus.FAILURE
        assert upload.process_status == expected_state
315
        if not with_calcs:
316
            assert calc.process_status == ProcessStatus.SUCCESS
317
        else:
318
            assert calc.process_status == expected_state
319

320

321
@pytest.mark.usefixtures('reset_config')
322
323
class TestClient:

324
325
    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
326
            cli,
327
328
            [
                'client', '-u', admin_user.username, '--token-via-api',
329
                'upload', '--upload-name', 'test_upload', '--local-path',
330
                non_empty_example_upload],
331
            catch_exceptions=False)
Markus Scheidgen's avatar
Markus Scheidgen committed
332

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

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

343
        assert result.exit_code == 0, result.output
Markus Scheidgen's avatar
Markus Scheidgen committed
344

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

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

        assert result.exit_code == 0, result.output
        assert 'Calculations, e.g. total energies' in result.output
353
        assert 'Geometries' in result.output
354
355
356
357
358
        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