backend_test.go (4320B)
1 package tui 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "net/http/httptest" 8 "net/url" 9 "reflect" 10 "testing" 11 12 "git.ryansepassi.com/git/gotextlog.git/internal/sshkey" 13 "git.ryansepassi.com/git/gotextlog.git/internal/textlog" 14 ) 15 16 func TestThreadBackendLoadsRootAndAllReplyPagesForFocusedReply(t *testing.T) { 17 type request struct { 18 path string 19 query url.Values 20 } 21 var requests []request 22 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 requests = append(requests, request{path: r.URL.Path, query: r.URL.Query()}) 24 w.Header().Set("Content-Type", "application/json") 25 switch r.URL.Path { 26 case "/api/v1/posts/3": 27 fmt.Fprint(w, `{"data":{"id":3,"top_id":1,"parent_id":2,"body":"focused","author":{"handle":"three"}}}`) 28 case "/api/v1/posts/1": 29 fmt.Fprint(w, `{"data":{"id":1,"body":"root","author":{"handle":"one"}}}`) 30 case "/api/v1/posts/1/replies": 31 if r.URL.Query().Get("cursor") == "next" { 32 fmt.Fprint(w, `{"data":[{"id":3,"top_id":1,"parent_id":2,"body":"focused","author":{"handle":"three"},"depth":2}],"pagination":{"next_cursor":null}}`) 33 } else { 34 fmt.Fprint(w, `{"data":[{"id":2,"top_id":1,"parent_id":1,"body":"parent","author":{"handle":"two"},"depth":1}],"pagination":{"next_cursor":"next"}}`) 35 } 36 default: 37 http.NotFound(w, r) 38 } 39 })) 40 defer server.Close() 41 42 backend := NewClientBackend(textlog.NewClient(server.URL, "", server.Client())) 43 result, err := backend.Execute(context.Background(), Operation{Kind: OpThread, ID: 3}) 44 if err != nil { 45 t.Fatal(err) 46 } 47 if result.Post == nil || result.Post.ID != 3 || result.Root == nil || result.Root.ID != 1 { 48 t.Fatalf("thread result = %#v", result) 49 } 50 if len(result.Replies) != 2 || result.Replies[0].ID != 2 || result.Replies[1].ID != 3 { 51 t.Fatalf("thread replies = %#v", result.Replies) 52 } 53 want := []request{ 54 {path: "/api/v1/posts/3", query: url.Values{}}, 55 {path: "/api/v1/posts/1", query: url.Values{}}, 56 {path: "/api/v1/posts/1/replies", query: url.Values{"depth": {"5"}, "limit": {"100"}}}, 57 {path: "/api/v1/posts/1/replies", query: url.Values{"cursor": {"next"}, "depth": {"5"}, "limit": {"100"}}}, 58 } 59 if !reflect.DeepEqual(requests, want) { 60 t.Fatalf("requests = %#v, want %#v", requests, want) 61 } 62 } 63 64 type fixedKeySource struct { 65 keys []sshkey.Key 66 message []byte 67 } 68 69 func (s *fixedKeySource) Keys(context.Context) ([]sshkey.Key, error) { return s.keys, nil } 70 func (s *fixedKeySource) Sign(_ context.Context, _ sshkey.Key, message []byte) ([]byte, error) { 71 s.message = append([]byte(nil), message...) 72 return []byte("signed-message"), nil 73 } 74 75 func TestKeyLoginBackendRequestsSignsAndVerifiesChallenge(t *testing.T) { 76 var requests []string 77 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 78 requests = append(requests, r.URL.Path) 79 w.Header().Set("Content-Type", "application/json") 80 switch r.URL.Path { 81 case "/api/v1/auth/key/challenge": 82 fmt.Fprint(w, `{"data":{"challenge_id":"challenge-1","message":"YXV0aC1tZXNzYWdl","expires_at":"2030-01-01T00:00:00Z","registered":false}}`) 83 case "/api/v1/auth/key/verify": 84 fmt.Fprint(w, `{"data":{"token":"key-token","expires_at":"2030-01-01T00:00:00Z","user":{"handle":"alice","can_post":true}}}`) 85 default: 86 http.NotFound(w, r) 87 } 88 })) 89 defer server.Close() 90 91 source := &fixedKeySource{keys: []sshkey.Key{{Path: "/tmp/id_ed25519.pub", PublicKey: "ssh-ed25519 AAAA", Fingerprint: "SHA256:key"}}} 92 backend := NewClientBackendWithKeySource(textlog.NewClient(server.URL, "", server.Client()), source) 93 listed, err := backend.Execute(context.Background(), Operation{Kind: OpListKeys}) 94 if err != nil || len(listed.Keys) != 1 { 95 t.Fatalf("list keys: result=%#v err=%v", listed, err) 96 } 97 result, err := backend.Execute(context.Background(), Operation{Kind: OpKeyLogin, Key: source.keys[0], Handle: "alice"}) 98 if err != nil { 99 t.Fatal(err) 100 } 101 if result.Session == nil || result.Session.Token != "key-token" || result.Me == nil || result.Me.Handle != "alice" { 102 t.Fatalf("result=%#v", result) 103 } 104 if string(source.message) != "auth-message" { 105 t.Fatalf("signed message %q", source.message) 106 } 107 if want := []string{"/api/v1/auth/key/challenge", "/api/v1/auth/key/verify"}; !reflect.DeepEqual(requests, want) { 108 t.Fatalf("requests=%v want=%v", requests, want) 109 } 110 }