File size: 4,399 Bytes
ee0b555
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import json
import stat
from datetime import datetime, timedelta, timezone

import pytest

from pwc_cli.edit_credentials import CredentialStore
from pwc_cli.edit_transport import EditClient, NoRedirect, edit_base_url
from pwc_cli.edits import EditUsageError
from pwc_cli import edits
from pwc_cli.cli import build_parser
from pwc_cli.transport import Client, ResponseError


def test_credential_origin_paper_and_permissions(tmp_path, monkeypatch):
    monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
    store = CredentialStore("https://paperswithcode.co/api/v1")
    store.save(1, {"paper_id": 1, "access_token": "secret"})
    assert stat.S_IMODE(store.path(1).stat().st_mode) == 0o600
    assert store.load(1)["access_token"] == "secret"
    with pytest.raises(ResponseError):
        store.load(2)
    with pytest.raises(ResponseError):
        CredentialStore("https://other.example/api/v1").load(1)
    store.path(1).chmod(0o644)
    with pytest.raises(ResponseError):
        store.load(1)


def test_edit_transport_rejects_unsafe_origins_and_redirects():
    for url in (
        "http://example.org/api/v1",
        "https://user:pass@example.org/api/v1",
        "https://example.org/api?token=secret",
    ):
        with pytest.raises(ResponseError):
            edit_base_url(url)
    assert edit_base_url("http://127.0.0.1:8989/api/v1").startswith("http:")
    assert (
        NoRedirect().redirect_request(None, None, 302, "", {}, "https://other.example")
        is None
    )


def test_submit_omits_reference_material_and_keeps_retry_key(
    tmp_path, monkeypatch, capsys
):
    monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
    client = Client()
    store = CredentialStore(client.base_url)
    store.save(
        1,
        {
            "paper_id": 1,
            "access_token": "top-secret",
            "expires_at": (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
        },
    )
    document = {
        "paper_id": 1,
        "version": "a" * 64,
        "idempotency_key": "stable-key",
        "current": {"private_reference": True},
        "operations": [{"section": "tasks", "payload": {"task_ids": [3]}}],
    }
    path = tmp_path / "edits.json"
    path.write_text(json.dumps(document))
    calls = []

    def request(self, method, endpoint, payload=None):
        calls.append((method, endpoint, payload))
        return {"status": "published"}

    monkeypatch.setattr(EditClient, "request", request)
    args = build_parser().parse_args(
        ["paper", "edit", "submit", "1", "--file", str(path)]
    )
    assert edits.edit(args, client) == 0
    assert calls[0][2]["idempotency_key"] == "stable-key"
    assert "current" not in calls[0][2]
    assert "top-secret" not in capsys.readouterr().out
    document["paper_id"] = 2
    path.write_text(json.dumps(document))
    with pytest.raises(EditUsageError):
        edits.edit(args, client)
    assert len(calls) == 1


def test_login_requires_browser_approval_and_never_prints_token(
    tmp_path, monkeypatch, capsys
):
    monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
    responses = iter(
        [
            {
                "device_code": "device-secret",
                "verification_path": "/authorize-paper-edit/abc",
                "user_code": "ABC",
                "expires_in": 600,
                "interval": 5,
            },
            {"status": "authorization_pending"},
            {
                "status": "authorized",
                "paper_id": 1,
                "username": "alice",
                "access_token": "access-secret",
                "expires_at": (
                    datetime.now(timezone.utc) + timedelta(hours=1)
                ).isoformat(),
            },
        ]
    )
    monkeypatch.setattr(EditClient, "request", lambda *a, **kw: next(responses))
    monkeypatch.setattr(edits.time, "sleep", lambda _: None)
    monkeypatch.setattr(
        edits.webbrowser, "open", lambda _: pytest.fail("no-browser ignored")
    )
    args = build_parser().parse_args(["auth", "login", "--paper", "1", "--no-browser"])
    assert edits.auth(args, Client()) == 0
    output = capsys.readouterr()
    assert "access-secret" not in output.out + output.err
    assert "device-secret" not in output.out + output.err
    assert CredentialStore(Client().base_url).load(1)["access_token"] == "access-secret"