gotextlog

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

server_e2e_test.go (4543B)


      1 package tui
      2 
      3 import (
      4 	"context"
      5 	"crypto/ed25519"
      6 	"crypto/rand"
      7 	"os"
      8 	"strings"
      9 	"testing"
     10 	"time"
     11 
     12 	"git.ryansepassi.com/git/gotextlog.git/internal/sshkey"
     13 	"git.ryansepassi.com/git/gotextlog.git/internal/textlog"
     14 	"golang.org/x/crypto/ssh"
     15 )
     16 
     17 type generatedKeySource struct {
     18 	key    sshkey.Key
     19 	signer ssh.Signer
     20 }
     21 
     22 func newGeneratedKeySource(t *testing.T) *generatedKeySource {
     23 	t.Helper()
     24 	_, privateKey, err := ed25519.GenerateKey(rand.Reader)
     25 	if err != nil {
     26 		t.Fatal(err)
     27 	}
     28 	signer, err := ssh.NewSignerFromKey(privateKey)
     29 	if err != nil {
     30 		t.Fatal(err)
     31 	}
     32 	public := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(signer.PublicKey())))
     33 	return &generatedKeySource{
     34 		key:    sshkey.Key{PublicKey: public, Fingerprint: ssh.FingerprintSHA256(signer.PublicKey())},
     35 		signer: signer,
     36 	}
     37 }
     38 
     39 func (s *generatedKeySource) Keys(context.Context) ([]sshkey.Key, error) {
     40 	return []sshkey.Key{s.key}, nil
     41 }
     42 
     43 func (s *generatedKeySource) Sign(_ context.Context, _ sshkey.Key, message []byte) ([]byte, error) {
     44 	signature, err := s.signer.Sign(rand.Reader, message)
     45 	if err != nil {
     46 		return nil, err
     47 	}
     48 	return ssh.Marshal(signature), nil
     49 }
     50 
     51 func TestServerEndToEndWithTwoTUIBackends(t *testing.T) {
     52 	baseURL := os.Getenv("GOTEXTLOG_TEST_SERVER_URL")
     53 	if baseURL == "" {
     54 		t.Skip("set GOTEXTLOG_TEST_SERVER_URL to run against gotextlog-srv")
     55 	}
     56 	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
     57 	defer cancel()
     58 
     59 	aliceKeys := newGeneratedKeySource(t)
     60 	bobKeys := newGeneratedKeySource(t)
     61 	alice := NewClientBackendWithKeySource(textlog.NewClient(baseURL, "", nil), aliceKeys)
     62 	bob := NewClientBackendWithKeySource(textlog.NewClient(baseURL, "", nil), bobKeys)
     63 
     64 	aliceLogin, err := alice.Execute(ctx, Operation{Kind: OpKeyLogin, Key: aliceKeys.key, Handle: "alice"})
     65 	if err != nil || aliceLogin.Me == nil || aliceLogin.Me.Handle != "alice" {
     66 		t.Fatalf("register alice: result=%#v err=%v", aliceLogin, err)
     67 	}
     68 	bobLogin, err := bob.Execute(ctx, Operation{Kind: OpKeyLogin, Key: bobKeys.key, Handle: "bob"})
     69 	if err != nil || bobLogin.Me == nil || bobLogin.Me.Handle != "bob" {
     70 		t.Fatalf("register bob: result=%#v err=%v", bobLogin, err)
     71 	}
     72 	alice = NewClientBackendWithKeySource(textlog.NewClient(baseURL, "", nil), aliceKeys)
     73 	aliceLogin, err = alice.Execute(ctx, Operation{Kind: OpKeyLogin, Key: aliceKeys.key})
     74 	if err != nil || aliceLogin.Me == nil || aliceLogin.Me.Handle != "alice" {
     75 		t.Fatalf("login existing alice key: result=%#v err=%v", aliceLogin, err)
     76 	}
     77 
     78 	if _, err := bob.Execute(ctx, Operation{Kind: OpFollow, Handle: "alice", Enabled: true}); err != nil {
     79 		t.Fatalf("bob follows alice: %v", err)
     80 	}
     81 	created, err := alice.Execute(ctx, Operation{Kind: OpCreatePost, Text: "hello from #golang to @bob"})
     82 	if err != nil || created.Post == nil {
     83 		t.Fatalf("alice creates post: result=%#v err=%v", created, err)
     84 	}
     85 	rootID := created.Post.ID
     86 	reply, err := bob.Execute(ctx, Operation{Kind: OpCreatePost, Text: "hello back", ParentID: &rootID})
     87 	if err != nil || reply.Post == nil || reply.Post.ParentID == nil || *reply.Post.ParentID != rootID {
     88 		t.Fatalf("bob replies: result=%#v err=%v", reply, err)
     89 	}
     90 
     91 	thread, err := alice.Execute(ctx, Operation{Kind: OpThread, ID: rootID})
     92 	if err != nil || len(thread.Replies) != 1 || thread.Replies[0].ID != reply.Post.ID {
     93 		t.Fatalf("thread: result=%#v err=%v", thread, err)
     94 	}
     95 	feed, err := bob.Execute(ctx, Operation{Kind: OpFeed, Feed: "for-you"})
     96 	if err != nil || len(feed.Entries) == 0 {
     97 		t.Fatalf("bob for-you feed: result=%#v err=%v", feed, err)
     98 	}
     99 	toAlice, err := alice.Execute(ctx, Operation{Kind: OpFeed, Feed: "to-me"})
    100 	if err != nil || len(toAlice.Entries) < 2 {
    101 		t.Fatalf("alice activity: result=%#v err=%v", toAlice, err)
    102 	}
    103 	search, err := bob.Execute(ctx, Operation{Kind: OpSearch, Query: "golang"})
    104 	if err != nil || len(search.Posts) != 1 || search.Posts[0].ID != rootID {
    105 		t.Fatalf("search: result=%#v err=%v", search, err)
    106 	}
    107 	profile, err := bob.Execute(ctx, Operation{Kind: OpProfile, Handle: "alice"})
    108 	if err != nil || profile.Profile == nil || profile.Profile.PostCount != 1 {
    109 		t.Fatalf("profile: result=%#v err=%v", profile, err)
    110 	}
    111 	if _, err := bob.Execute(ctx, Operation{Kind: OpBlock, Handle: "alice", Enabled: true}); err != nil {
    112 		t.Fatalf("bob blocks alice: %v", err)
    113 	}
    114 	if _, err := bob.Execute(ctx, Operation{Kind: OpRevoke}); err != nil {
    115 		t.Fatalf("bob revokes session: %v", err)
    116 	}
    117 	if _, err := bob.Execute(ctx, Operation{Kind: OpMe}); err == nil {
    118 		t.Fatal("revoked session still authenticated")
    119 	}
    120 }