okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

nekomimi.petcolab: refresh tokens in the daemon, share the websocket and google oauth code, drop the ssh config editsc9cb3a0

main
3.0 KiB86 linesraw
1// A refresh moves a stored token, so it is checked against a local token endpoint rather than a scripted model turn.
2import gleam/string
3import gleeunit/should
4
5const stale =
6  "{\"type\":\"oauth\",\"access\":\"old\",\"refresh\":\"r1\",\"expires\":0,\"email\":\"a@example.com\"}"
7
8const other =
9  "{\"type\":\"oauth\",\"access\":\"keep\",\"refresh\":\"r2\",\"expires\":0,\"email\":\"b@example.com\"}"
10
11const fresh = "99999999999999"
12
13fn accounts(items: List(String)) -> String {
14  "[" <> string.join(items, ",") <> "]"
15}
16
17pub fn a_stale_account_is_refreshed_saved_and_the_others_left_alone_test() -> Nil {
18  let #(result, stored, form) =
19    refresh(accounts([stale, other]), #(
20      200,
21      "{\"access_token\":\"new\",\"expires_in\":3600}",
22    ))
23  result |> should.equal(Ok("new"))
24  string.contains(stored, "\"access\":\"new\"") |> should.be_true
25  string.contains(stored, "\"refresh\":\"r1\"") |> should.be_true
26  string.contains(stored, "\"access\":\"keep\"") |> should.be_true
27  string.contains(form, "grant_type=refresh_token") |> should.be_true
28  string.contains(form, "refresh_token=r1") |> should.be_true
29}
30
31pub fn a_rotated_refresh_token_replaces_the_old_one_test() -> Nil {
32  let #(_, stored, _) =
33    refresh(accounts([stale]), #(
34      200,
35      "{\"access_token\":\"new\",\"refresh_token\":\"r9\",\"expires_in\":3600}",
36    ))
37  string.contains(stored, "\"refresh\":\"r9\"") |> should.be_true
38  string.contains(stored, "r1") |> should.be_false
39}
40
41pub fn a_rejected_refresh_keeps_the_stored_account_test() -> Nil {
42  let #(result, stored, _) =
43    refresh(accounts([stale]), #(400, "{\"error\":\"invalid_grant\"}"))
44  result |> should.equal(Error("Google Colab token refresh failed (400)"))
45  string.contains(stored, "\"access\":\"old\"") |> should.be_true
46}
47
48pub fn an_account_without_a_refresh_token_is_invalid_test() -> Nil {
49  let #(result, _, _) =
50    refresh(
51      accounts(["{\"type\":\"oauth\",\"access\":\"old\",\"expires\":0}"]),
52      #(200, "{}"),
53    )
54  result |> should.equal(Error("invalid"))
55}
56
57pub fn colab_uses_the_selected_account_and_names_its_email_test() -> Nil {
58  let selected =
59    "{\"type\":\"oauth\",\"access\":\"second\",\"refresh\":\"r2\",\"expires\":"
60    <> fresh
61    <> ",\"email\":\"b@example.com\",\"selected\":true}"
62  let first =
63    "{\"type\":\"oauth\",\"access\":\"first\",\"refresh\":\"r1\",\"expires\":"
64    <> fresh
65    <> ",\"email\":\"a@example.com\"}"
66  signed_in(accounts([first, selected]))
67  |> should.equal(Ok(#("second", "b@example.com")))
68  signed_in(accounts([first, first]))
69  |> should.equal(Ok(#("first", "a@example.com")))
70}
71
72pub fn colab_with_no_account_says_to_sign_in_test() -> Nil {
73  signed_in("[]")
74  |> should.equal(Error(
75    "Google Colab is not authenticated; run /login and add a Google Colab account",
76  ))
77}
78
79@external(erlang, "albedo_google_oauth_test_support", "refresh")
80fn refresh(
81  accounts: String,
82  answer: #(Int, String),
83) -> #(Result(String, String), String, String)
84
85@external(erlang, "albedo_google_oauth_test_support", "signed_in")
86fn signed_in(accounts: String) -> Result(#(String, String), String)