Skip to content

Commit 5e13833

Browse files
Merge pull request #33 from chriscarrollsmith/fix/32-upload-response-parsing
Fix upload response parsing AttributeError
2 parents e161afa + 08343fb commit 5e13833

6 files changed

Lines changed: 97 additions & 16 deletions

File tree

.github/workflows/publish.yml

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,45 @@ permissions:
99
contents: read
1010

1111
jobs:
12+
test:
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
python-version: ["3.10", "3.13"]
17+
os: [ubuntu-latest]
18+
defaults:
19+
run:
20+
shell: bash
21+
runs-on: ${{ matrix.os }}
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Install uv
26+
uses: astral-sh/setup-uv@v5
27+
28+
- uses: actions/setup-python@v5
29+
with:
30+
python-version: ${{ matrix.python-version }}
31+
32+
- name: Install dependencies and run tests
33+
env:
34+
ZOTERO_API_KEY: ${{ secrets.ZOTERO_API_KEY }}
35+
ZOTERO_LIBRARY_ID: ${{ secrets.ZOTERO_LIBRARY_ID }}
36+
ZOTERO_LIBRARY_TYPE: ${{ secrets.ZOTERO_LIBRARY_TYPE }}
37+
ZOTERO_USERNAME: ${{ secrets.ZOTERO_USERNAME }}
38+
ZOTERO_TEST_GROUP_ID: ${{ secrets.ZOTERO_TEST_GROUP_ID }}
39+
run: |
40+
uv sync
41+
uv run pytest tests
42+
1243
release:
44+
needs: test
1345
runs-on: ubuntu-latest
1446
environment:
1547
name: pypi
1648
steps:
1749
- uses: actions/checkout@v4
18-
50+
1951
- name: Install uv
2052
uses: astral-sh/setup-uv@v5
2153

@@ -34,4 +66,3 @@ jobs:
3466
uses: pypa/gh-action-pypi-publish@release/v1
3567
with:
3668
packages-dir: dist/
37-

.github/workflows/test.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: test
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
7+
jobs:
8+
build:
9+
strategy:
10+
fail-fast: false
11+
matrix:
12+
python-version: ["3.10", "3.13"]
13+
os: [ubuntu-latest]
14+
defaults:
15+
run:
16+
shell: bash
17+
runs-on: ${{ matrix.os }}
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- name: Install uv
22+
uses: astral-sh/setup-uv@v5
23+
24+
- uses: actions/setup-python@v5
25+
with:
26+
python-version: ${{ matrix.python-version }}
27+
28+
- name: Install dependencies and verify package builds
29+
run: |
30+
uv sync
31+
uv run python -c "import pyzotero_cli; print('Import OK')"
32+
33+
integration-test:
34+
runs-on: ubuntu-latest
35+
environment: test
36+
steps:
37+
- uses: actions/checkout@v4
38+
39+
- name: Install uv
40+
uses: astral-sh/setup-uv@v5
41+
42+
- uses: actions/setup-python@v5
43+
with:
44+
python-version: "3.13"
45+
46+
- name: Install dependencies and run integration tests
47+
env:
48+
ZOTERO_API_KEY: ${{ secrets.ZOTERO_API_KEY }}
49+
ZOTERO_LIBRARY_ID: ${{ secrets.ZOTERO_LIBRARY_ID }}
50+
ZOTERO_LIBRARY_TYPE: ${{ secrets.ZOTERO_LIBRARY_TYPE }}
51+
ZOTERO_USERNAME: ${{ secrets.ZOTERO_USERNAME }}
52+
ZOTERO_TEST_GROUP_ID: ${{ secrets.ZOTERO_TEST_GROUP_ID }}
53+
run: |
54+
uv sync
55+
uv run pytest tests -v

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,3 @@ __pycache__/
1717
*.egg-info/
1818
repomix-output.*
1919
dist/
20-
21-
# temporarily ignored till mock implementation is done
22-
.github/workflows/test.yml

pyzotero_cli/file_cmds.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,10 @@ def upload_files(ctx, paths_to_local_file, parent_item_id, filename_option):
8989
if response:
9090
click.echo("Upload results:")
9191
if 'success' in response and response['success']:
92-
# attachment_simple/both success value is a dict {index: {details}}
93-
for _index, details in response['success'].items():
94-
# Use filename from details if available, fallback to index as key placeholder
92+
for details in response['success']:
9593
click.echo(f" Successfully uploaded: {details.get('filename', '?')} (Key: {details.get('key', '?')})")
9694
if 'failure' in response and response['failure']:
97-
for _index, details in response['failure'].items():
95+
for details in response['failure']:
9896
click.echo(f" Failed to upload: {details.get('filename', '?')}. Reason: {details.get('message', 'Unknown error')}", err=True)
9997
if 'unchanged' in response and response['unchanged']:
10098
# unchanged value is a list of dicts [{details}]

tests/test_item_cmds.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import uuid
66

77
# Test for `zot items list`
8-
def test_item_list_default_json_output(runner: CliRunner):
8+
def test_item_list_default_json_output(runner: CliRunner, active_profile_with_real_credentials):
99
"""Test `zot items list` returns JSON by default and is not empty."""
1010
result = runner.invoke(zot, ['items', 'list', '--limit', '1'])
1111
assert result.exit_code == 0
@@ -19,7 +19,7 @@ def test_item_list_default_json_output(runner: CliRunner):
1919
except json.JSONDecodeError:
2020
pytest.fail("Output was not valid JSON.")
2121

22-
def test_item_list_top_flag(runner: CliRunner):
22+
def test_item_list_top_flag(runner: CliRunner, active_profile_with_real_credentials):
2323
"""Test `zot items list --top`."""
2424
result = runner.invoke(zot, ['items', 'list', '--top', '--limit', '1'])
2525
assert result.exit_code == 0
@@ -29,7 +29,7 @@ def test_item_list_top_flag(runner: CliRunner):
2929
except json.JSONDecodeError:
3030
pytest.fail("Output was not valid JSON for --top flag.")
3131

32-
def test_item_list_output_table(runner: CliRunner):
32+
def test_item_list_output_table(runner: CliRunner, active_profile_with_real_credentials):
3333
"""Test `zot items list --output table`."""
3434
result = runner.invoke(zot, ['items', 'list', '--limit', '1', '--output', 'table'])
3535
assert result.exit_code == 0
@@ -71,7 +71,7 @@ def test_item_get_single_item(runner: CliRunner, temp_item_with_tags):
7171
except json.JSONDecodeError:
7272
pytest.fail("Output was not valid JSON for item get.")
7373

74-
def test_item_get_non_existent_item(runner: CliRunner):
74+
def test_item_get_non_existent_item(runner: CliRunner, active_profile_with_real_credentials):
7575
"""Test `zot items get <item_key>` for a non-existent item."""
7676
non_existent_key = "NONEXIST" # A key that is unlikely to exist
7777
result = runner.invoke(zot, ['items', 'get', non_existent_key])
@@ -478,7 +478,7 @@ def test_item_children(runner: CliRunner, zot_instance):
478478
print(f"Error cleaning up parent item {parent_item_key}: {e_parent}")
479479

480480
# Test for `items count`
481-
def test_item_count(runner: CliRunner):
481+
def test_item_count(runner: CliRunner, active_profile_with_real_credentials):
482482
"""Test `zot items count`."""
483483
result = runner.invoke(zot, ['items', 'count'])
484484
assert result.exit_code == 0

tests/test_tag_cmds.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def test_list_item_tags_json_output(temp_item_with_tags, runner: CliRunner):
128128
except json.JSONDecodeError:
129129
pytest.fail(f"Output was not valid JSON: {result.output}")
130130

131-
def test_list_item_tags_non_existent_item(runner: CliRunner):
131+
def test_list_item_tags_non_existent_item(runner: CliRunner, active_profile_with_real_credentials):
132132
non_existent_key = f"NONEXISTENTKEY{uuid.uuid4()}" # Ensure truly non-existent
133133
result = runner.invoke(zot, ['tags', 'list-for-item', non_existent_key])
134134
assert result.exit_code == 1 # Should exit 1 for "not found" errors
@@ -229,7 +229,7 @@ def test_delete_tag_no_interaction_flag(temp_tag_in_library, zot_instance, runne
229229
assert "Are you sure you want to delete" not in result.output # Prompt should be skipped
230230
assert tag_to_delete not in zot_api_client.tags()
231231

232-
def test_delete_non_existent_tag_force(runner: CliRunner):
232+
def test_delete_non_existent_tag_force(runner: CliRunner, active_profile_with_real_credentials):
233233
non_existent_tag = f"non-existent-tag-{uuid.uuid4()}"
234234
result = runner.invoke(zot, ['tags', 'delete', non_existent_tag, '--force'])
235235
assert result.exit_code == 0

0 commit comments

Comments
 (0)