gotextlog

gotextlog
git clone https://git.ryansepassi.com/git/gotextlog.git
Log | Files | Refs | README

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 }