podojo-cli 0.3.2__tar.gz → 0.4.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.
Files changed (33) hide show
  1. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/CHANGELOG.md +11 -0
  2. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/PKG-INFO +6 -1
  3. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/pyproject.toml +8 -1
  4. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/commands/auth.py +7 -1
  5. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/config.py +14 -9
  6. podojo_cli-0.4.1/tests/conftest.py +45 -0
  7. podojo_cli-0.4.1/tests/test_auth.py +53 -0
  8. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/uv.lock +93 -1
  9. podojo_cli-0.3.2/tests/conftest.py +0 -18
  10. podojo_cli-0.3.2/tests/test_auth.py +0 -42
  11. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/.github/workflows/publish.yml +0 -0
  12. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/.gitignore +0 -0
  13. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/CLAUDE.md +0 -0
  14. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/README.md +0 -0
  15. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/__init__.py +0 -0
  16. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/client.py +0 -0
  17. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/commands/__init__.py +0 -0
  18. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/commands/gdrive.py +0 -0
  19. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/commands/projects.py +0 -0
  20. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/commands/showreel.py +0 -0
  21. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/commands/transcripts.py +0 -0
  22. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/commands/usertests.py +0 -0
  23. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/commands/videos.py +0 -0
  24. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/gdrive/__init__.py +0 -0
  25. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/gdrive/list.py +0 -0
  26. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/gdrive/upload.py +0 -0
  27. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/main.py +0 -0
  28. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/video/__init__.py +0 -0
  29. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/src/podojo_cli/video/showreel.py +0 -0
  30. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/tests/test_gdrive.py +0 -0
  31. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/tests/test_projects.py +0 -0
  32. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/tests/test_showreel.py +0 -0
  33. {podojo_cli-0.3.2 → podojo_cli-0.4.1}/tests/test_usertests.py +0 -0
@@ -5,6 +5,17 @@ All notable changes to the Podojo CLI will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org).
7
7
 
8
+ ## [0.4.1] - 2026-04-05
9
+
10
+ ### Changed
11
+ - Add project URLs and author metadata for verified PyPI details
12
+
13
+ ## [0.4.0] - 2026-04-05
14
+
15
+ ### Changed
16
+ - Store API key in OS credential store (Keychain/Credential Locker) via keyring instead of plaintext ~/.podojo.toml
17
+ - `auth login` now prompts for key with hidden input instead of accepting it as a positional argument
18
+
8
19
  ## [0.3.2] - 2026-04-04
9
20
 
10
21
  ### Added
@@ -1,12 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: podojo-cli
3
- Version: 0.3.2
3
+ Version: 0.4.1
4
4
  Summary: CLI for the Podojo user research platform
5
+ Project-URL: Homepage, https://github.com/podojo/cli-podojo
6
+ Project-URL: Source, https://github.com/podojo/cli-podojo
7
+ Project-URL: Issues, https://github.com/podojo/cli-podojo/issues
8
+ Author: Jochen Ade
5
9
  License-Expression: MIT
6
10
  Requires-Python: >=3.12
7
11
  Requires-Dist: google-api-python-client>=2.0
8
12
  Requires-Dist: google-auth>=2.0
9
13
  Requires-Dist: httpx>=0.28
14
+ Requires-Dist: keyring>=25.0
10
15
  Requires-Dist: pyyaml>=6.0
11
16
  Requires-Dist: rich>=13.0
12
17
  Requires-Dist: typer>=0.15
@@ -1,10 +1,11 @@
1
1
  [project]
2
2
  name = "podojo-cli"
3
- version = "0.3.2"
3
+ version = "0.4.1"
4
4
  description = "CLI for the Podojo user research platform"
5
5
  readme = "README.md"
6
6
  license = "MIT"
7
7
  requires-python = ">=3.12"
8
+ authors = [{ name = "Jochen Ade" }]
8
9
  dependencies = [
9
10
  "typer>=0.15",
10
11
  "httpx>=0.28",
@@ -12,8 +13,14 @@ dependencies = [
12
13
  "pyyaml>=6.0",
13
14
  "google-auth>=2.0",
14
15
  "google-api-python-client>=2.0",
16
+ "keyring>=25.0",
15
17
  ]
16
18
 
19
+ [project.urls]
20
+ Homepage = "https://github.com/podojo/cli-podojo"
21
+ Source = "https://github.com/podojo/cli-podojo"
22
+ Issues = "https://github.com/podojo/cli-podojo/issues"
23
+
17
24
  [project.scripts]
18
25
  podojo = "podojo_cli.main:app"
19
26
 
@@ -24,8 +24,14 @@ def _verify_key(api_key: str) -> bool:
24
24
 
25
25
 
26
26
  @app.command("login")
27
- def login(api_key: str = typer.Argument(help="Your Podojo API key")):
27
+ def login(
28
+ api_key: str = typer.Option(
29
+ None, "--key", "-k", help="Your Podojo API key (prompted if omitted)"
30
+ ),
31
+ ):
28
32
  """Save your API key and verify it works."""
33
+ if not api_key:
34
+ api_key = typer.prompt("API Key", hide_input=True)
29
35
  console.print("Verifying key…", end=" ")
30
36
  if not _verify_key(api_key):
31
37
  console.print("[red]failed[/red]")
@@ -1,9 +1,11 @@
1
1
  import os
2
2
  from pathlib import Path
3
3
 
4
+ import keyring
4
5
  import tomllib
5
6
 
6
-
7
+ KEYRING_SERVICE = "podojo-cli"
8
+ KEYRING_USERNAME = "api_key"
7
9
  CONFIG_PATH = Path.home() / ".podojo.toml"
8
10
  DEFAULT_BASE_URL = "https://podojo-fastapi-mcp.onrender.com"
9
11
 
@@ -13,21 +15,24 @@ def load_config() -> dict:
13
15
  if CONFIG_PATH.exists():
14
16
  config = tomllib.loads(CONFIG_PATH.read_text())
15
17
  config.setdefault("base_url", os.getenv("PODOJO_BASE_URL", DEFAULT_BASE_URL))
16
- config.setdefault("api_key", os.getenv("PODOJO_API_KEY", ""))
18
+ api_key = (
19
+ os.getenv("PODOJO_API_KEY")
20
+ or keyring.get_password(KEYRING_SERVICE, KEYRING_USERNAME)
21
+ or ""
22
+ )
23
+ config.setdefault("api_key", api_key)
17
24
  return config
18
25
 
19
26
 
20
27
  def save_config(api_key: str):
21
- config = {}
22
- if CONFIG_PATH.exists():
23
- config = tomllib.loads(CONFIG_PATH.read_text())
24
- config["api_key"] = api_key
25
- config.setdefault("base_url", DEFAULT_BASE_URL)
26
- lines = "\n".join(f'{k} = "{v}"' for k, v in config.items())
27
- CONFIG_PATH.write_text(lines + "\n")
28
+ keyring.set_password(KEYRING_SERVICE, KEYRING_USERNAME, api_key)
28
29
 
29
30
 
30
31
  def clear_api_key():
32
+ try:
33
+ keyring.delete_password(KEYRING_SERVICE, KEYRING_USERNAME)
34
+ except keyring.errors.PasswordDeleteError:
35
+ pass
31
36
  if not CONFIG_PATH.exists():
32
37
  return
33
38
  config = tomllib.loads(CONFIG_PATH.read_text())
@@ -0,0 +1,45 @@
1
+ from pathlib import Path
2
+
3
+ import keyring
4
+ import pytest
5
+ from typer.testing import CliRunner
6
+
7
+ from podojo_cli.main import app
8
+
9
+
10
+ class InMemoryKeyring(keyring.backend.KeyringBackend):
11
+ priority = 10
12
+
13
+ def __init__(self):
14
+ self._store = {}
15
+
16
+ def set_password(self, service, username, password):
17
+ self._store[(service, username)] = password
18
+
19
+ def get_password(self, service, username):
20
+ return self._store.get((service, username))
21
+
22
+ def delete_password(self, service, username):
23
+ try:
24
+ del self._store[(service, username)]
25
+ except KeyError:
26
+ raise keyring.errors.PasswordDeleteError()
27
+
28
+
29
+ @pytest.fixture
30
+ def runner():
31
+ return CliRunner()
32
+
33
+
34
+ @pytest.fixture
35
+ def mock_keyring():
36
+ backend = InMemoryKeyring()
37
+ keyring.set_keyring(backend)
38
+ return backend
39
+
40
+
41
+ @pytest.fixture(autouse=True)
42
+ def mock_config(monkeypatch):
43
+ monkeypatch.setenv("PODOJO_BASE_URL", "http://test.local")
44
+ monkeypatch.setenv("PODOJO_API_KEY", "test-key")
45
+ monkeypatch.setattr("podojo_cli.config.CONFIG_PATH", Path("/nonexistent/.podojo.toml"))
@@ -0,0 +1,53 @@
1
+ from unittest.mock import patch
2
+
3
+ from podojo_cli.config import KEYRING_SERVICE, KEYRING_USERNAME
4
+ from podojo_cli.main import app
5
+
6
+
7
+ def test_login_valid_key(runner, httpx_mock, mock_keyring):
8
+ httpx_mock.add_response(
9
+ url="http://test.local/api/v1/projects?limit=1",
10
+ status_code=200,
11
+ json=[],
12
+ )
13
+
14
+ result = runner.invoke(app, ["auth", "login", "--key", "valid-key-abc"])
15
+
16
+ assert result.exit_code == 0
17
+ assert "Logged in successfully" in result.output
18
+ assert mock_keyring.get_password(KEYRING_SERVICE, KEYRING_USERNAME) == "valid-key-abc"
19
+
20
+
21
+ def test_login_prompted(runner, httpx_mock, mock_keyring):
22
+ httpx_mock.add_response(
23
+ url="http://test.local/api/v1/projects?limit=1",
24
+ status_code=200,
25
+ json=[],
26
+ )
27
+
28
+ result = runner.invoke(app, ["auth", "login"], input="prompted-key\n")
29
+
30
+ assert result.exit_code == 0
31
+ assert "Logged in successfully" in result.output
32
+ assert mock_keyring.get_password(KEYRING_SERVICE, KEYRING_USERNAME) == "prompted-key"
33
+
34
+
35
+ def test_login_invalid_key(runner, httpx_mock):
36
+ httpx_mock.add_response(
37
+ url="http://test.local/api/v1/projects?limit=1",
38
+ status_code=401,
39
+ )
40
+
41
+ result = runner.invoke(app, ["auth", "login", "--key", "bad-key"])
42
+
43
+ assert result.exit_code != 0
44
+ assert "Invalid API key" in result.output
45
+
46
+
47
+ def test_logout(runner, mock_keyring):
48
+ mock_keyring.set_password(KEYRING_SERVICE, KEYRING_USERNAME, "some-key")
49
+
50
+ result = runner.invoke(app, ["auth", "logout"])
51
+
52
+ assert result.exit_code == 0
53
+ assert mock_keyring.get_password(KEYRING_SERVICE, KEYRING_USERNAME) is None
@@ -363,6 +363,65 @@ wheels = [
363
363
  { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
364
364
  ]
365
365
 
366
+ [[package]]
367
+ name = "jaraco-classes"
368
+ version = "3.4.0"
369
+ source = { registry = "https://pypi.org/simple" }
370
+ dependencies = [
371
+ { name = "more-itertools" },
372
+ ]
373
+ sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" }
374
+ wheels = [
375
+ { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" },
376
+ ]
377
+
378
+ [[package]]
379
+ name = "jaraco-context"
380
+ version = "6.1.2"
381
+ source = { registry = "https://pypi.org/simple" }
382
+ sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" }
383
+ wheels = [
384
+ { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" },
385
+ ]
386
+
387
+ [[package]]
388
+ name = "jaraco-functools"
389
+ version = "4.4.0"
390
+ source = { registry = "https://pypi.org/simple" }
391
+ dependencies = [
392
+ { name = "more-itertools" },
393
+ ]
394
+ sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" }
395
+ wheels = [
396
+ { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" },
397
+ ]
398
+
399
+ [[package]]
400
+ name = "jeepney"
401
+ version = "0.9.0"
402
+ source = { registry = "https://pypi.org/simple" }
403
+ sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" }
404
+ wheels = [
405
+ { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" },
406
+ ]
407
+
408
+ [[package]]
409
+ name = "keyring"
410
+ version = "25.7.0"
411
+ source = { registry = "https://pypi.org/simple" }
412
+ dependencies = [
413
+ { name = "jaraco-classes" },
414
+ { name = "jaraco-context" },
415
+ { name = "jaraco-functools" },
416
+ { name = "jeepney", marker = "sys_platform == 'linux'" },
417
+ { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
418
+ { name = "secretstorage", marker = "sys_platform == 'linux'" },
419
+ ]
420
+ sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" }
421
+ wheels = [
422
+ { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" },
423
+ ]
424
+
366
425
  [[package]]
367
426
  name = "markdown-it-py"
368
427
  version = "4.0.0"
@@ -384,6 +443,15 @@ wheels = [
384
443
  { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
385
444
  ]
386
445
 
446
+ [[package]]
447
+ name = "more-itertools"
448
+ version = "11.0.1"
449
+ source = { registry = "https://pypi.org/simple" }
450
+ sdist = { url = "https://files.pythonhosted.org/packages/24/24/e0acc4bf54cba50c1d432c70a72a3df96db4a321b2c4c68432a60759044f/more_itertools-11.0.1.tar.gz", hash = "sha256:fefaf25b7ab08f0b45fa9f1892cae93b9fc0089ef034d39213bce15f1cc9e199", size = 144739, upload-time = "2026-04-02T16:17:45.061Z" }
451
+ wheels = [
452
+ { url = "https://files.pythonhosted.org/packages/d8/f4/5e52c7319b8087acef603ed6e50dc325c02eaa999355414830468611f13c/more_itertools-11.0.1-py3-none-any.whl", hash = "sha256:eaf287826069452a8f61026c597eae2428b2d1ba2859083abbf240b46842ce6d", size = 72182, upload-time = "2026-04-02T16:17:43.724Z" },
453
+ ]
454
+
387
455
  [[package]]
388
456
  name = "packaging"
389
457
  version = "26.0"
@@ -404,12 +472,13 @@ wheels = [
404
472
 
405
473
  [[package]]
406
474
  name = "podojo-cli"
407
- version = "0.3.0"
475
+ version = "0.3.2"
408
476
  source = { editable = "." }
409
477
  dependencies = [
410
478
  { name = "google-api-python-client" },
411
479
  { name = "google-auth" },
412
480
  { name = "httpx" },
481
+ { name = "keyring" },
413
482
  { name = "pyyaml" },
414
483
  { name = "rich" },
415
484
  { name = "typer" },
@@ -426,6 +495,7 @@ requires-dist = [
426
495
  { name = "google-api-python-client", specifier = ">=2.0" },
427
496
  { name = "google-auth", specifier = ">=2.0" },
428
497
  { name = "httpx", specifier = ">=0.28" },
498
+ { name = "keyring", specifier = ">=25.0" },
429
499
  { name = "pyyaml", specifier = ">=6.0" },
430
500
  { name = "rich", specifier = ">=13.0" },
431
501
  { name = "typer", specifier = ">=0.15" },
@@ -541,6 +611,15 @@ wheels = [
541
611
  { url = "https://files.pythonhosted.org/packages/e2/d2/1eb1ea9c84f0d2033eb0b49675afdc71aa4ea801b74615f00f3c33b725e3/pytest_httpx-0.36.0-py3-none-any.whl", hash = "sha256:bd4c120bb80e142df856e825ec9f17981effb84d159f9fa29ed97e2357c3a9c8", size = 20229, upload-time = "2025-12-02T16:34:56.45Z" },
542
612
  ]
543
613
 
614
+ [[package]]
615
+ name = "pywin32-ctypes"
616
+ version = "0.2.3"
617
+ source = { registry = "https://pypi.org/simple" }
618
+ sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
619
+ wheels = [
620
+ { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
621
+ ]
622
+
544
623
  [[package]]
545
624
  name = "pyyaml"
546
625
  version = "6.0.3"
@@ -627,6 +706,19 @@ wheels = [
627
706
  { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" },
628
707
  ]
629
708
 
709
+ [[package]]
710
+ name = "secretstorage"
711
+ version = "3.5.0"
712
+ source = { registry = "https://pypi.org/simple" }
713
+ dependencies = [
714
+ { name = "cryptography" },
715
+ { name = "jeepney" },
716
+ ]
717
+ sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" }
718
+ wheels = [
719
+ { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" },
720
+ ]
721
+
630
722
  [[package]]
631
723
  name = "shellingham"
632
724
  version = "1.5.4"
@@ -1,18 +0,0 @@
1
- from pathlib import Path
2
-
3
- import pytest
4
- from typer.testing import CliRunner
5
-
6
- from podojo_cli.main import app
7
-
8
-
9
- @pytest.fixture
10
- def runner():
11
- return CliRunner()
12
-
13
-
14
- @pytest.fixture(autouse=True)
15
- def mock_config(monkeypatch):
16
- monkeypatch.setenv("PODOJO_BASE_URL", "http://test.local")
17
- monkeypatch.setenv("PODOJO_API_KEY", "test-key")
18
- monkeypatch.setattr("podojo_cli.config.CONFIG_PATH", Path("/nonexistent/.podojo.toml"))
@@ -1,42 +0,0 @@
1
- from unittest.mock import patch
2
-
3
- from podojo_cli.main import app
4
-
5
-
6
- def test_login_valid_key(runner, httpx_mock, tmp_path):
7
- httpx_mock.add_response(
8
- url="http://test.local/api/v1/projects?limit=1",
9
- status_code=200,
10
- json=[],
11
- )
12
-
13
- config_path = tmp_path / ".podojo.toml"
14
- with patch("podojo_cli.config.CONFIG_PATH", config_path):
15
- result = runner.invoke(app, ["auth", "login", "valid-key-abc"])
16
-
17
- assert result.exit_code == 0
18
- assert "Logged in successfully" in result.output
19
- assert 'api_key = "valid-key-abc"' in config_path.read_text()
20
-
21
-
22
- def test_login_invalid_key(runner, httpx_mock):
23
- httpx_mock.add_response(
24
- url="http://test.local/api/v1/projects?limit=1",
25
- status_code=401,
26
- )
27
-
28
- result = runner.invoke(app, ["auth", "login", "bad-key"])
29
-
30
- assert result.exit_code != 0
31
- assert "Invalid API key" in result.output
32
-
33
-
34
- def test_logout(runner, tmp_path):
35
- config_path = tmp_path / ".podojo.toml"
36
- config_path.write_text('api_key = "some-key"\n')
37
-
38
- with patch("podojo_cli.config.CONFIG_PATH", config_path):
39
- result = runner.invoke(app, ["auth", "logout"])
40
-
41
- assert result.exit_code == 0
42
- assert "api_key" not in config_path.read_text() if config_path.exists() else True
File without changes
File without changes
File without changes