circfirm 4.0.0__tar.gz → 4.0.1__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.
- {circfirm-4.0.0 → circfirm-4.0.1}/PKG-INFO +3 -2
- circfirm-4.0.1/VERSION +1 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/backend/cache.py +0 -1
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/backend/s3.py +0 -1
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/cli/cache.py +5 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/cli/query.py +24 -5
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/cli/update.py +5 -2
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm.egg-info/PKG-INFO +3 -2
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/backend/test_backend.py +1 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/backend/test_backend_device.py +5 -10
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/cli/test_cli_cache.py +33 -19
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/cli/test_cli_current.py +2 -5
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/cli/test_cli_detect.py +4 -8
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/cli/test_cli_install.py +14 -15
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/cli/test_cli_query.py +77 -20
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/cli/test_cli_update.py +40 -22
- circfirm-4.0.1/tests/conftest.py +169 -0
- circfirm-4.0.1/tests/helpers.py +96 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/test_startup.py +0 -1
- circfirm-4.0.0/VERSION +0 -1
- circfirm-4.0.0/tests/conftest.py +0 -53
- circfirm-4.0.0/tests/helpers.py +0 -191
- {circfirm-4.0.0 → circfirm-4.0.1}/.github/workflows/codeql.yml +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/.github/workflows/publish.yml +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/.github/workflows/push.yml +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/.gitignore +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/.pre-commit-config.yaml +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/.readthedocs.yaml +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/.reuse/dep5 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/LICENSE +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/LICENSES/MIT.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/LICENSES/Unlicense.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/Makefile +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/NOTICE +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/NOTICE.license +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/README.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/VERSION.license +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/__init__.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/backend/__init__.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/backend/device.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/backend/github.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/cli/__init__.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/cli/about.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/cli/config.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/cli/current.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/cli/detect.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/cli/install.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/py.typed +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/startup.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/templates/settings.yaml +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm/templates/settings.yaml.license +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm.egg-info/SOURCES.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm.egg-info/dependency_links.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm.egg-info/entry_points.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm.egg-info/requires.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/circfirm.egg-info/top_level.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/commands/cache.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/commands/config.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/commands/current.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/commands/detect.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/commands/install.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/commands/query.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/commands/update.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/conf.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/examples/update_many.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/examples/weekly_cache.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/index.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/index.rst.license +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/infrastructure/ci.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/infrastructure/codecheck.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/infrastructure/docs.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/infrastructure/license.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/infrastructure/notice.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/docs/infrastructure/tests.rst +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/examples/bash/update_many.sh +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/examples/bash/weekly_cache.sh +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/examples/powershell/update_many.ps1 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/examples/powershell/weekly_cache.ps1 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/pyproject.toml +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/requirements-dev.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/requirements.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/scripts/rmdir.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/setup.cfg +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/boot_out.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.0.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.1.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.2.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.0.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.1.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.2.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.0.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.1.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.2.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.0.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.1.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.2.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.0.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.1.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.2.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.0.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.1.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.2.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.0.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.1.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.2.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.0.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.1.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.2.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.0.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.1.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.2.0.uf2 +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/info_uf2.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/responses/full_list.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/responses/full_list.txt.license +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/responses/specific_board.txt +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/assets/responses/specific_board.txt.license +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/backend/test_backend_cache.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/backend/test_backend_github.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/backend/test_backend_s3.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/cli/test_cli_about.py +0 -0
- {circfirm-4.0.0 → circfirm-4.0.1}/tests/cli/test_cli_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: circfirm
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.1
|
|
4
4
|
Summary: CLI tool for install firmware for CircuitPython boards
|
|
5
5
|
Author-email: Alec Delaney <tekktrik@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -46,6 +46,7 @@ Requires-Dist: pytest~=8.3; extra == "dev"
|
|
|
46
46
|
Requires-Dist: sphinx~=7.4; extra == "dev"
|
|
47
47
|
Requires-Dist: sphinx-tabs~=3.4; extra == "dev"
|
|
48
48
|
Requires-Dist: sphinx-rtd-theme~=3.0; extra == "dev"
|
|
49
|
+
Dynamic: license-file
|
|
49
50
|
|
|
50
51
|
..
|
|
51
52
|
SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
|
circfirm-4.0.1/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4.0.1
|
|
@@ -17,7 +17,6 @@ import packaging.version
|
|
|
17
17
|
from mypy_boto3_s3 import S3ServiceResource
|
|
18
18
|
|
|
19
19
|
import circfirm.backend
|
|
20
|
-
import circfirm.backend.cache
|
|
21
20
|
|
|
22
21
|
S3_CONFIG = botocore.client.Config(signature_version=botocore.UNSIGNED)
|
|
23
22
|
S3_RESOURCE: S3ServiceResource = boto3.resource("s3", config=S3_CONFIG)
|
|
@@ -13,6 +13,7 @@ import re
|
|
|
13
13
|
import shutil
|
|
14
14
|
from typing import Optional
|
|
15
15
|
|
|
16
|
+
import botocore.exceptions
|
|
16
17
|
import click
|
|
17
18
|
|
|
18
19
|
import circfirm
|
|
@@ -153,3 +154,7 @@ def cache_latest(board_id: str, language: str, pre_release: bool) -> None:
|
|
|
153
154
|
)
|
|
154
155
|
except ConnectionError as err:
|
|
155
156
|
raise click.exceptions.ClickException(err.args[0])
|
|
157
|
+
except botocore.exceptions.ConnectionError:
|
|
158
|
+
raise click.exceptions.ClickException(
|
|
159
|
+
"Could not connect to the S3 bucket - check network connection"
|
|
160
|
+
)
|
|
@@ -9,6 +9,7 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import re
|
|
11
11
|
|
|
12
|
+
import botocore.exceptions
|
|
12
13
|
import click
|
|
13
14
|
import requests
|
|
14
15
|
|
|
@@ -51,12 +52,18 @@ def query_board_ids(regex: str) -> None:
|
|
|
51
52
|
except ValueError as err:
|
|
52
53
|
raise click.ClickException(err.args[0])
|
|
53
54
|
except requests.ConnectionError as err:
|
|
55
|
+
print("Triggered!")
|
|
54
56
|
raise click.ClickException(
|
|
55
57
|
"Issue with requesting information from git repository, check network connection"
|
|
56
58
|
)
|
|
57
59
|
for board in boards:
|
|
58
60
|
board_id = board.strip()
|
|
59
|
-
|
|
61
|
+
try:
|
|
62
|
+
result = re.search(regex, board_id)
|
|
63
|
+
except re.PatternError:
|
|
64
|
+
raise click.exceptions.ClickException(
|
|
65
|
+
"Regex pattern error - please check the regex syntax"
|
|
66
|
+
)
|
|
60
67
|
if result:
|
|
61
68
|
click.echo(board_id)
|
|
62
69
|
|
|
@@ -69,7 +76,16 @@ def query_board_ids(regex: str) -> None:
|
|
|
69
76
|
)
|
|
70
77
|
def query_versions(board_id: str, language: str, regex: str) -> None:
|
|
71
78
|
"""Query the CircuitPython versions available for a board."""
|
|
72
|
-
|
|
79
|
+
try:
|
|
80
|
+
versions = circfirm.backend.s3.get_board_versions(
|
|
81
|
+
board_id, language, regex=regex
|
|
82
|
+
)
|
|
83
|
+
except botocore.exceptions.ConnectionError as err:
|
|
84
|
+
raise click.exceptions.ClickException(err.args[0])
|
|
85
|
+
except re.PatternError:
|
|
86
|
+
raise click.exceptions.ClickException(
|
|
87
|
+
"Regex pattern error - please check the regex syntax"
|
|
88
|
+
)
|
|
73
89
|
for version in reversed(versions):
|
|
74
90
|
click.echo(version)
|
|
75
91
|
|
|
@@ -86,8 +102,11 @@ def query_versions(board_id: str, language: str, regex: str) -> None:
|
|
|
86
102
|
)
|
|
87
103
|
def query_latest(board_id: str, language: str, pre_release: bool) -> None:
|
|
88
104
|
"""Query the latest CircuitPython versions available."""
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
try:
|
|
106
|
+
version = circfirm.backend.s3.get_latest_board_version(
|
|
107
|
+
board_id, language, pre_release
|
|
108
|
+
)
|
|
109
|
+
except botocore.exceptions.ConnectionError as err:
|
|
110
|
+
raise click.exceptions.ClickException(err.args[0])
|
|
92
111
|
if version:
|
|
93
112
|
click.echo(version)
|
|
@@ -9,12 +9,12 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
from typing import Optional
|
|
11
11
|
|
|
12
|
+
import botocore.exceptions
|
|
12
13
|
import click
|
|
13
14
|
import packaging.version
|
|
14
15
|
|
|
15
16
|
import circfirm.backend.device
|
|
16
17
|
import circfirm.backend.s3
|
|
17
|
-
import circfirm.cli.install
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@click.command()
|
|
@@ -79,7 +79,10 @@ def cli( # noqa: PLR0913
|
|
|
79
79
|
except OSError as err:
|
|
80
80
|
raise click.ClickException(err.args[0])
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
try:
|
|
83
|
+
new_versions = circfirm.backend.s3.get_board_versions(board_id, language)
|
|
84
|
+
except botocore.exceptions.ConnectionError as err:
|
|
85
|
+
raise click.exceptions.ClickException(err.args[0])
|
|
83
86
|
|
|
84
87
|
if not pre_release:
|
|
85
88
|
new_versions = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: circfirm
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.1
|
|
4
4
|
Summary: CLI tool for install firmware for CircuitPython boards
|
|
5
5
|
Author-email: Alec Delaney <tekktrik@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -46,6 +46,7 @@ Requires-Dist: pytest~=8.3; extra == "dev"
|
|
|
46
46
|
Requires-Dist: sphinx~=7.4; extra == "dev"
|
|
47
47
|
Requires-Dist: sphinx-tabs~=3.4; extra == "dev"
|
|
48
48
|
Requires-Dist: sphinx-rtd-theme~=3.0; extra == "dev"
|
|
49
|
+
Dynamic: license-file
|
|
49
50
|
|
|
50
51
|
..
|
|
51
52
|
SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
|
|
@@ -13,39 +13,34 @@ import circfirm.backend.device
|
|
|
13
13
|
import tests.helpers
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
def test_find_circuitpy() -> None:
|
|
16
|
+
def test_find_circuitpy(mock_with_circuitpy: None) -> None:
|
|
18
17
|
"""Tests finding a CircuitPython device when boot_out.txt is present."""
|
|
19
18
|
mount_location = tests.helpers.get_mount()
|
|
20
19
|
circuitpy = circfirm.backend.device.find_circuitpy()
|
|
21
20
|
assert circuitpy == mount_location
|
|
22
21
|
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
def test_find_circuitpy_absent() -> None:
|
|
23
|
+
def test_find_circuitpy_absent(mock_with_no_device: None) -> None:
|
|
26
24
|
"""Tests finding a CircuitPython device when boot_out.txt is absent."""
|
|
27
25
|
circuitpy = circfirm.backend.device.find_circuitpy()
|
|
28
26
|
assert circuitpy is None
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
def test_find_bootloader() -> None:
|
|
29
|
+
def test_find_bootloader(mock_with_bootloader: None) -> None:
|
|
33
30
|
"""Tests finding a CircuitPython device in bootloader mode when info_uf2.txt is present."""
|
|
34
31
|
mount_location = tests.helpers.get_mount()
|
|
35
32
|
bootloader = circfirm.backend.device.find_bootloader()
|
|
36
33
|
assert bootloader == mount_location
|
|
37
34
|
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
def test_find_bootloader_absent() -> None:
|
|
36
|
+
def test_find_bootloader_absent(mock_with_no_device: None) -> None:
|
|
41
37
|
"""Tests finding a CircuitPython device in bootloader mode when info_uf2.txt is absent."""
|
|
42
38
|
bootloader = circfirm.backend.device.find_bootloader()
|
|
43
39
|
assert bootloader is None
|
|
44
40
|
tests.helpers.copy_uf2_info()
|
|
45
41
|
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
def test_get_board_info() -> None:
|
|
43
|
+
def test_get_board_info(mock_with_circuitpy: None) -> None:
|
|
49
44
|
"""Tests getting the board ID and firmware version from the UF2 info file."""
|
|
50
45
|
# Test successful parsing
|
|
51
46
|
mount_location = tests.helpers.get_mount()
|
|
@@ -10,12 +10,12 @@ Author(s): Alec Delaney
|
|
|
10
10
|
import os
|
|
11
11
|
import pathlib
|
|
12
12
|
import shutil
|
|
13
|
+
from typing import NoReturn
|
|
13
14
|
|
|
14
15
|
from click.testing import CliRunner
|
|
15
16
|
|
|
16
17
|
import circfirm
|
|
17
18
|
import circfirm.backend.cache
|
|
18
|
-
import tests.helpers
|
|
19
19
|
from circfirm.cli import cli
|
|
20
20
|
|
|
21
21
|
RUNNER = CliRunner()
|
|
@@ -28,8 +28,7 @@ def test_cache_list_empty() -> None:
|
|
|
28
28
|
assert result.output == "Versions have not been cached yet for any boards.\n"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
def test_cache_list_all() -> None:
|
|
31
|
+
def test_cache_list_all(mock_with_firmwares_archived: None) -> None:
|
|
33
32
|
"""Tests the cache list command with an non-empty cache."""
|
|
34
33
|
with open("tests/assets/responses/full_list.txt", encoding="utf-8") as respfile:
|
|
35
34
|
expected_response = respfile.read()
|
|
@@ -38,8 +37,7 @@ def test_cache_list_all() -> None:
|
|
|
38
37
|
assert result.output == expected_response
|
|
39
38
|
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
def test_cache_list_specific_board_found() -> None:
|
|
40
|
+
def test_cache_list_specific_board_found(mock_with_firmwares_archived: None) -> None:
|
|
43
41
|
"""Tests the cache list command with an non-empty cache for a specific board."""
|
|
44
42
|
with open(
|
|
45
43
|
"tests/assets/responses/specific_board.txt", encoding="utf-8"
|
|
@@ -50,8 +48,7 @@ def test_cache_list_specific_board_found() -> None:
|
|
|
50
48
|
assert result.output == expected_response
|
|
51
49
|
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
def test_cache_list_none_found() -> None:
|
|
51
|
+
def test_cache_list_none_found(mock_with_firmwares_archived: None) -> None:
|
|
55
52
|
"""Tests the cache list command with an non-empty cache and no matches."""
|
|
56
53
|
fake_board = "does_not_exist"
|
|
57
54
|
|
|
@@ -92,8 +89,7 @@ def test_cache_save() -> None:
|
|
|
92
89
|
)
|
|
93
90
|
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
def test_cache_clear() -> None:
|
|
92
|
+
def test_cache_clear(mock_with_firmwares_archived: None) -> None:
|
|
97
93
|
"""Tests the cache clear command."""
|
|
98
94
|
board = "feather_m4_express"
|
|
99
95
|
version = "7.1.0"
|
|
@@ -136,8 +132,7 @@ def test_cache_clear() -> None:
|
|
|
136
132
|
assert len(list(board_folder.parent.glob("*"))) == 0
|
|
137
133
|
|
|
138
134
|
|
|
139
|
-
|
|
140
|
-
def test_cache_clear_regex_board_id() -> None:
|
|
135
|
+
def test_cache_clear_regex_board_id(mock_with_firmwares_archived: None) -> None:
|
|
141
136
|
"""Tests the cache clear command when using a regex flag for board ID."""
|
|
142
137
|
board = "feather_m4_express"
|
|
143
138
|
board_regex = "m4"
|
|
@@ -160,8 +155,7 @@ def test_cache_clear_regex_board_id() -> None:
|
|
|
160
155
|
assert not board_folder.exists()
|
|
161
156
|
|
|
162
157
|
|
|
163
|
-
|
|
164
|
-
def test_cache_clear_regex_version() -> None:
|
|
158
|
+
def test_cache_clear_regex_version(mock_with_firmwares_archived: None) -> None:
|
|
165
159
|
"""Tests the cache clear command when using a regex flag for version."""
|
|
166
160
|
version = "7.1.0"
|
|
167
161
|
version_regex = r".\.1"
|
|
@@ -184,8 +178,7 @@ def test_cache_clear_regex_version() -> None:
|
|
|
184
178
|
assert not list(board_folder.glob(f"*{version}*"))
|
|
185
179
|
|
|
186
180
|
|
|
187
|
-
|
|
188
|
-
def test_cache_clear_regex_language() -> None:
|
|
181
|
+
def test_cache_clear_regex_language(mock_with_firmwares_archived: None) -> None:
|
|
189
182
|
"""Tests the cache clear command when using a regex flag for language."""
|
|
190
183
|
language = "zh_Latn_pinyin"
|
|
191
184
|
language_regex = ".*Latn"
|
|
@@ -208,8 +201,7 @@ def test_cache_clear_regex_language() -> None:
|
|
|
208
201
|
assert not list(board_folder.glob(f"*{language}*"))
|
|
209
202
|
|
|
210
203
|
|
|
211
|
-
|
|
212
|
-
def test_cache_clear_regex_combination() -> None:
|
|
204
|
+
def test_cache_clear_regex_combination(mock_with_firmwares_archived: None) -> None:
|
|
213
205
|
"""Tests the cache clear command when using a regex flag for language."""
|
|
214
206
|
board_regex = "feather"
|
|
215
207
|
version = "7.2.0"
|
|
@@ -270,13 +262,35 @@ def test_cache_latest() -> None:
|
|
|
270
262
|
assert result.exit_code == 0
|
|
271
263
|
assert os.path.exists(uf2_filepath)
|
|
272
264
|
|
|
273
|
-
# Save the latest version (unsuccessful)
|
|
265
|
+
# Save the latest version (unsuccessful, does not exist)
|
|
274
266
|
result = RUNNER.invoke(
|
|
275
267
|
cli, ["cache", "latest", board, "--language", "doesnotexist"]
|
|
276
268
|
)
|
|
277
|
-
assert result.exit_code
|
|
269
|
+
assert result.exit_code != 0
|
|
278
270
|
|
|
279
271
|
finally:
|
|
280
272
|
board_folder = circfirm.backend.cache.get_board_folder(board)
|
|
281
273
|
if board_folder.exists():
|
|
282
274
|
shutil.rmtree(board_folder)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def test_cache_latest_no_internet(mock_s3_no_internet: NoReturn) -> None:
|
|
278
|
+
"""Test the update command when in CIRCUITPY mode when there is no internet connection."""
|
|
279
|
+
board = "feather_m0_express"
|
|
280
|
+
language = "cs"
|
|
281
|
+
expected_version = "6.1.0"
|
|
282
|
+
|
|
283
|
+
uf2_filepath = circfirm.backend.cache.get_uf2_filepath(
|
|
284
|
+
board, expected_version, language
|
|
285
|
+
)
|
|
286
|
+
assert not os.path.exists(uf2_filepath)
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
# Save the latest version (unsuccessful, no internet connection)
|
|
290
|
+
result = RUNNER.invoke(cli, ["cache", "latest", board, "--language", language])
|
|
291
|
+
assert result.exit_code != 0
|
|
292
|
+
|
|
293
|
+
finally:
|
|
294
|
+
board_folder = circfirm.backend.cache.get_board_folder(board)
|
|
295
|
+
if board_folder.exists(): # pragma: no cover
|
|
296
|
+
shutil.rmtree(board_folder)
|
|
@@ -9,14 +9,12 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
from click.testing import CliRunner
|
|
11
11
|
|
|
12
|
-
import tests.helpers
|
|
13
12
|
from circfirm.cli import cli
|
|
14
13
|
|
|
15
14
|
RUNNER = CliRunner()
|
|
16
15
|
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
def test_current() -> None:
|
|
17
|
+
def test_current(mock_with_circuitpy: None) -> None:
|
|
20
18
|
"""Tests the current name and version commands."""
|
|
21
19
|
# Test when connected in CIRCUITPY mode
|
|
22
20
|
result = RUNNER.invoke(cli, ["current", "board-id"])
|
|
@@ -28,8 +26,7 @@ def test_current() -> None:
|
|
|
28
26
|
assert result.output == "8.0.0-beta.6\n"
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
def test_current_in_bootloader() -> None:
|
|
29
|
+
def test_current_in_bootloader(mock_with_bootloader: None) -> None:
|
|
33
30
|
"""Tests the current command whenn connected in bootloader mode."""
|
|
34
31
|
result = RUNNER.invoke(cli, ["current", "board-id"])
|
|
35
32
|
assert result.exit_code != 0
|
|
@@ -17,8 +17,7 @@ from circfirm.cli import cli
|
|
|
17
17
|
RUNNER = CliRunner()
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
def test_detect_circuitpy_found() -> None:
|
|
20
|
+
def test_detect_circuitpy_found(mock_with_circuitpy: None) -> None:
|
|
22
21
|
"""Tests the ability of the detect circuitpy command to find a connected board."""
|
|
23
22
|
result = RUNNER.invoke(cli, ["detect", "circuitpy"])
|
|
24
23
|
assert result.exit_code == 0
|
|
@@ -28,15 +27,13 @@ def test_detect_circuitpy_found() -> None:
|
|
|
28
27
|
assert circuitpy == mount
|
|
29
28
|
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
def test_detect_circuitpy_not_found() -> None:
|
|
30
|
+
def test_detect_circuitpy_not_found(mock_with_no_device: None) -> None:
|
|
33
31
|
"""Tests the detect circuitpy command without a connected board."""
|
|
34
32
|
result = RUNNER.invoke(cli, ["detect", "circuitpy"])
|
|
35
33
|
assert result.output == "No board connected in CIRCUITPY or equivalent mode\n"
|
|
36
34
|
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
def test_detect_bootloader_found() -> None:
|
|
36
|
+
def test_detect_bootloader_found(mock_with_bootloader: None) -> None:
|
|
40
37
|
"""Tests the ability of the detect bootloader command to find a connected board."""
|
|
41
38
|
import time
|
|
42
39
|
|
|
@@ -49,8 +46,7 @@ def test_detect_bootloader_found() -> None:
|
|
|
49
46
|
assert bootloader == mount
|
|
50
47
|
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
def test_detect_bootloader_not_found() -> None:
|
|
49
|
+
def test_detect_bootloader_not_found(mock_with_no_device: None) -> None:
|
|
54
50
|
"""Tests the detect bootloader command without a connected board."""
|
|
55
51
|
result = RUNNER.invoke(cli, ["detect", "bootloader"])
|
|
56
52
|
assert result.output == "No board connected in bootloader mode\n"
|
|
@@ -9,7 +9,6 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
11
|
import shutil
|
|
12
|
-
import threading
|
|
13
12
|
import time
|
|
14
13
|
|
|
15
14
|
from click.testing import CliRunner
|
|
@@ -28,17 +27,19 @@ ERR_UF2_DOWNLOAD = 4
|
|
|
28
27
|
VERSION = "8.0.0-beta.6"
|
|
29
28
|
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
def test_install_successful() -> None:
|
|
30
|
+
def test_install_successful(mock_with_circuitpy: None) -> None:
|
|
33
31
|
"""Tests the successful use of the install command."""
|
|
34
32
|
try:
|
|
35
33
|
# Test successfully installing the firmware
|
|
36
|
-
|
|
34
|
+
tests.helpers.start_bootloader_copy_thread()
|
|
35
|
+
|
|
37
36
|
result = RUNNER.invoke(cli, ["install", VERSION])
|
|
37
|
+
|
|
38
38
|
assert result.exit_code == 0
|
|
39
39
|
expected_uf2_filename = circfirm.backend.get_uf2_filename(
|
|
40
40
|
"feather_m4_express", VERSION
|
|
41
41
|
)
|
|
42
|
+
|
|
42
43
|
expected_uf2_filepath = tests.helpers.get_mount_node(expected_uf2_filename)
|
|
43
44
|
assert os.path.exists(expected_uf2_filepath)
|
|
44
45
|
os.remove(expected_uf2_filepath)
|
|
@@ -57,8 +58,7 @@ def test_install_successful() -> None:
|
|
|
57
58
|
shutil.rmtree(board_folder)
|
|
58
59
|
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
def test_install_no_mount() -> None:
|
|
61
|
+
def test_install_no_mount(mock_with_no_device: None) -> None:
|
|
62
62
|
"""Tests the install command when a mounted drive is not found."""
|
|
63
63
|
result = RUNNER.invoke(
|
|
64
64
|
cli, ["install", VERSION, "--board-id", "feather_m4_express"]
|
|
@@ -66,8 +66,7 @@ def test_install_no_mount() -> None:
|
|
|
66
66
|
assert result.exit_code == ERR_NOT_FOUND
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
def test_install_as_circuitpy() -> None:
|
|
69
|
+
def test_install_as_circuitpy(mock_with_circuitpy: None) -> None:
|
|
71
70
|
"""Tests the install command when a mounted CIRCUITPY drive is found."""
|
|
72
71
|
result = RUNNER.invoke(
|
|
73
72
|
cli, ["install", VERSION, "--board-id", "feather_m4_express"]
|
|
@@ -75,8 +74,7 @@ def test_install_as_circuitpy() -> None:
|
|
|
75
74
|
assert result.exit_code == ERR_FOUND_CIRCUITPY
|
|
76
75
|
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
def test_install_bad_version() -> None:
|
|
77
|
+
def test_install_bad_version(mock_with_bootloader: None) -> None:
|
|
80
78
|
"""Tests the install command using a bad board version."""
|
|
81
79
|
result = RUNNER.invoke(
|
|
82
80
|
cli, ["install", "doesnotexist", "--board-id", "feather_m4_express"]
|
|
@@ -88,16 +86,18 @@ def test_install_bad_version() -> None:
|
|
|
88
86
|
assert result.exit_code == ERR_IN_BOOTLOADER
|
|
89
87
|
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
def test_install_with_timeout() -> None:
|
|
89
|
+
def test_install_with_timeout(mock_with_circuitpy: None) -> None:
|
|
93
90
|
"""Tests the install command using the timeout option."""
|
|
94
91
|
try:
|
|
95
|
-
|
|
92
|
+
tests.helpers.start_bootloader_copy_thread()
|
|
93
|
+
|
|
96
94
|
result = RUNNER.invoke(cli, ["install", VERSION, "--timeout", "60"])
|
|
95
|
+
|
|
97
96
|
assert result.exit_code == 0
|
|
98
97
|
expected_uf2_filename = circfirm.backend.get_uf2_filename(
|
|
99
98
|
"feather_m4_express", VERSION
|
|
100
99
|
)
|
|
100
|
+
|
|
101
101
|
expected_uf2_filepath = tests.helpers.get_mount_node(expected_uf2_filename)
|
|
102
102
|
assert os.path.exists(expected_uf2_filepath)
|
|
103
103
|
os.remove(expected_uf2_filepath)
|
|
@@ -108,8 +108,7 @@ def test_install_with_timeout() -> None:
|
|
|
108
108
|
shutil.rmtree(board_folder)
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
def test_install_with_timeout_failure() -> None:
|
|
111
|
+
def test_install_with_timeout_failure(mock_with_circuitpy: None) -> None:
|
|
113
112
|
"""Tests the install command using the timeout option that causes a failure."""
|
|
114
113
|
timeout = 3
|
|
115
114
|
start_time = time.time()
|
|
@@ -11,23 +11,15 @@ import os
|
|
|
11
11
|
from typing import NoReturn
|
|
12
12
|
|
|
13
13
|
import pytest
|
|
14
|
-
import requests
|
|
15
14
|
from click.testing import CliRunner
|
|
16
15
|
|
|
17
|
-
import circfirm.backend.github
|
|
18
16
|
import tests.helpers
|
|
19
17
|
from circfirm.cli import cli
|
|
20
18
|
|
|
21
19
|
RUNNER = CliRunner()
|
|
22
20
|
|
|
23
21
|
|
|
24
|
-
def
|
|
25
|
-
"""Simulate a network error by raising requests.ConnectionError."""
|
|
26
|
-
raise requests.ConnectionError
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@tests.helpers.with_token(os.environ["GH_TOKEN"])
|
|
30
|
-
def test_query_board_ids() -> None:
|
|
22
|
+
def test_query_board_ids(token: None) -> None:
|
|
31
23
|
"""Tests the ability to query the boards using the CLI."""
|
|
32
24
|
# Test an authenticated request with supporting text
|
|
33
25
|
board_ids = tests.helpers.get_board_ids_from_git()
|
|
@@ -45,33 +37,52 @@ def test_query_board_ids() -> None:
|
|
|
45
37
|
assert result.output == expected_output
|
|
46
38
|
|
|
47
39
|
# Test an authenticated request without supporting text
|
|
40
|
+
preexisting = RUNNER.invoke(cli, ["config", "view", "output.supporting.silence"])
|
|
48
41
|
assert result.exit_code == 0
|
|
42
|
+
preexisting = preexisting.output.strip()
|
|
43
|
+
assert preexisting in ("true", "false")
|
|
44
|
+
|
|
45
|
+
# Suppress supporting text
|
|
49
46
|
result = RUNNER.invoke(cli, ["config", "edit", "output.supporting.silence", "true"])
|
|
50
47
|
assert result.exit_code == 0
|
|
48
|
+
|
|
49
|
+
# Test command
|
|
51
50
|
result = RUNNER.invoke(cli, ["query", "board-ids"])
|
|
52
51
|
assert result.exit_code == 0
|
|
53
52
|
assert result.output == pre_expected_output
|
|
54
53
|
|
|
54
|
+
# Reset the supporting text setting
|
|
55
|
+
result = RUNNER.invoke(
|
|
56
|
+
cli, ["config", "edit", "output.supporting.silence", preexisting]
|
|
57
|
+
)
|
|
58
|
+
assert result.exit_code == 0
|
|
59
|
+
|
|
60
|
+
# Check results of reset
|
|
61
|
+
final = RUNNER.invoke(cli, ["config", "view", "output.supporting.silence"])
|
|
62
|
+
assert result.exit_code == 0
|
|
63
|
+
final = final.output.strip()
|
|
64
|
+
assert final == preexisting
|
|
65
|
+
|
|
55
66
|
|
|
56
|
-
@
|
|
57
|
-
def test_query_board_ids_bad_token() -> None:
|
|
67
|
+
@pytest.mark.parametrize("token", ["badtoken"], indirect=True)
|
|
68
|
+
def test_query_board_ids_bad_token(token: None) -> None:
|
|
58
69
|
"""Test a request with a faulty token."""
|
|
59
70
|
result = RUNNER.invoke(cli, ["query", "board-ids"])
|
|
60
71
|
assert result.exit_code != 0
|
|
61
72
|
|
|
62
73
|
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
def test_query_board_ids_no_internet(
|
|
75
|
+
token: None, mock_requests_no_internet: NoReturn
|
|
76
|
+
) -> None:
|
|
65
77
|
"""Tests failure when cannot fetch results due to no network connection."""
|
|
66
|
-
monkeypatch.setattr(
|
|
67
|
-
circfirm.backend.github, "get_board_id_list", simulate_no_connection
|
|
68
|
-
)
|
|
69
78
|
result = RUNNER.invoke(cli, ["query", "board-ids"])
|
|
70
79
|
assert result.exit_code != 0
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_query_board_ids_bad_regex(token: None) -> None:
|
|
83
|
+
"""Tests failure when a bad regex pattern is provided."""
|
|
84
|
+
result = RUNNER.invoke(cli, ["query", "board-ids", "--regex", "*badregex"])
|
|
85
|
+
assert result.exit_code != 0
|
|
75
86
|
|
|
76
87
|
|
|
77
88
|
def test_query_versions() -> None:
|
|
@@ -115,6 +126,33 @@ def test_query_versions() -> None:
|
|
|
115
126
|
assert result.output == "6.2.0-beta.1\n"
|
|
116
127
|
|
|
117
128
|
|
|
129
|
+
def test_query_versions_no_internet(mock_s3_no_internet: NoReturn) -> None:
|
|
130
|
+
"""Tests the ability to query firmware versions using the CLI."""
|
|
131
|
+
board = "adafruit_feather_rp2040"
|
|
132
|
+
language = "cs"
|
|
133
|
+
|
|
134
|
+
result = RUNNER.invoke(
|
|
135
|
+
cli,
|
|
136
|
+
[
|
|
137
|
+
"query",
|
|
138
|
+
"versions",
|
|
139
|
+
board,
|
|
140
|
+
"--language",
|
|
141
|
+
language,
|
|
142
|
+
],
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
assert result.exit_code != 0
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_query_versions_bad_regex(token: None) -> None:
|
|
149
|
+
"""Tests failure when a bad regex pattern is provided."""
|
|
150
|
+
result = RUNNER.invoke(
|
|
151
|
+
cli, ["query", "versions", "adafruit_feather_rp2040", "--regex", "*badregex"]
|
|
152
|
+
)
|
|
153
|
+
assert result.exit_code != 0
|
|
154
|
+
|
|
155
|
+
|
|
118
156
|
def test_query_latest() -> None:
|
|
119
157
|
"""Tests the ability to query the latest version of the firmware using the CLI."""
|
|
120
158
|
board = "adafruit_feather_rp2040"
|
|
@@ -142,3 +180,22 @@ def test_query_latest() -> None:
|
|
|
142
180
|
)
|
|
143
181
|
assert result.exit_code == 0
|
|
144
182
|
assert result.output == expected_output
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_query_latest_no_internet(mock_s3_no_internet: NoReturn) -> None:
|
|
186
|
+
"""Tests the ability to query the latest version of the firmware using the CLI."""
|
|
187
|
+
board = "adafruit_feather_rp2040"
|
|
188
|
+
language = "cs"
|
|
189
|
+
|
|
190
|
+
# Test without pre-releases included
|
|
191
|
+
result = RUNNER.invoke(
|
|
192
|
+
cli,
|
|
193
|
+
[
|
|
194
|
+
"query",
|
|
195
|
+
"latest",
|
|
196
|
+
board,
|
|
197
|
+
"--language",
|
|
198
|
+
language,
|
|
199
|
+
],
|
|
200
|
+
)
|
|
201
|
+
assert result.exit_code != 0
|