Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
5.0 kB · 204 lines
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205package devenv
import ( "bufio" "context" "encoding/json" "fmt" "net" "net/http" "os/exec" "path/filepath" "runtime" "strings" "testing" "time"
comatproto "github.com/bluesky-social/indigo/api/atproto" "github.com/bluesky-social/indigo/atproto/identity" "github.com/bluesky-social/indigo/xrpc" "github.com/cenkalti/backoff" "github.com/google/uuid" "github.com/stretchr/testify/require" "stream.place/streamplace/pkg/aqhttp" "stream.place/streamplace/pkg/log")
type DevEnv struct { PDSURL string `json:"pds-url"` PLCURL string `json:"plc-url"` Accounts []*DevEnvAccount `json:"accounts"`}
func WithDevEnv(t *testing.T) *DevEnv { _, filename, _, _ := runtime.Caller(0) cmd := exec.Command("node", "../../js/dev-env/run.mjs") cmd.Dir = filepath.Dir(filename)
// Start the command and get pipes for streaming output stdout, err := cmd.StdoutPipe() if err != nil { t.Logf("Error getting stdout pipe: %v", err) t.FailNow() }
if err := cmd.Start(); err != nil { t.Logf("Error starting dev env: %v", err) t.FailNow() }
var env DevEnv
scanner := bufio.NewScanner(stdout) scanner.Scan() err = json.Unmarshal(scanner.Bytes(), &env) if err != nil { t.Logf("Error unmarshalling dev-env stdout: %v", err) t.FailNow() } env.Accounts = []*DevEnvAccount{}
go func() { scanner := bufio.NewScanner(stdout) for scanner.Scan() { t.Logf("dev-env stdout: %s", scanner.Text()) if scanner.Err() != nil { return } } }()
// Ensure cleanup happens when test finishes t.Cleanup(func() { t.Logf("killing dev env") if cmd.Process != nil { _ = cmd.Process.Kill() _ = cmd.Wait() } })
return &env}
type DevEnvAccount struct { Handle string Email string Password string DID string XRPC *xrpc.Client}
func (d *DevEnv) CreateAccount(t *testing.T) *DevEnvAccount {
xrpcc := &xrpc.Client{ Host: d.PDSURL, Client: &aqhttp.Client, }
uu, err := uuid.NewRandom() require.NoError(t, err)
handle := fmt.Sprintf("sp-%s.test", uu.String()[:8]) email := fmt.Sprintf("%s@example.com", handle) password := "test"
out, err := comatproto.ServerCreateAccount(context.Background(), xrpcc, &comatproto.ServerCreateAccount_Input{ Handle: handle, Email: &email, Password: &password, }) require.NoError(t, err) log.Log(context.Background(), "created account", "did", out.Did, "handle", out.Handle)
session, err := comatproto.ServerCreateSession(context.Background(), xrpcc, &comatproto.ServerCreateSession_Input{ Identifier: out.Handle, Password: password, }) require.NoError(t, err)
xrpcc = &xrpc.Client{ Host: d.PDSURL, Client: &aqhttp.Client, Auth: &xrpc.AuthInfo{ Did: out.Did, AccessJwt: session.AccessJwt, RefreshJwt: session.RefreshJwt, Handle: out.Handle, }, } acct := &DevEnvAccount{ Handle: out.Handle, Email: email, Password: password, DID: out.Did, XRPC: xrpcc, } d.Accounts = append(d.Accounts, acct) return acct}
// Custom RoundTripper for intercepting .test domain requeststype TestRoundTripper struct { DevEnv *DevEnv}
func (d *DevEnv) TestHTTPClient() *http.Client { return &http.Client{ Transport: d.TestRoundTripper(), }}
func (d *DevEnv) TestRoundTripper() *TestRoundTripper { return &TestRoundTripper{DevEnv: d}}
func (rt *TestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if strings.HasSuffix(req.URL.Hostname(), ".test") { log.Log(context.Background(), "intercepting .test domain request", "url", req.URL.String()) upstreamURL := fmt.Sprintf("%s%s", rt.DevEnv.PDSURL, req.URL.Path) upstreamReq, err := http.NewRequest(req.Method, upstreamURL, req.Body) if err != nil { return nil, err } upstreamReq.Header = req.Header upstreamReq.Host = req.URL.Hostname() upstreamResp, err := http.DefaultTransport.RoundTrip(upstreamReq) if err != nil { return nil, err } return upstreamResp, nil } // For non-.test domains, use the default transport return http.DefaultTransport.RoundTrip(req)}
func (d *DevEnv) TestDirectory() identity.Directory { // We need to create a new directory with our custom client base := identity.BaseDirectory{ PLCURL: d.PLCURL, HTTPClient: *d.TestHTTPClient(), Resolver: net.Resolver{ Dial: func(ctx context.Context, network, address string) (net.Conn, error) { d := net.Dialer{Timeout: time.Second * 3} return d.DialContext(ctx, network, address) }, }, TryAuthoritativeDNS: true, SkipDNSDomainSuffixes: []string{".bsky.social"}, } return &base}
// More aggressive backoff for testsfunc NewExponentialBackOff() *backoff.ExponentialBackOff { b := &backoff.ExponentialBackOff{ InitialInterval: 100 * time.Millisecond, RandomizationFactor: backoff.DefaultRandomizationFactor, Multiplier: backoff.DefaultMultiplier, MaxInterval: 2 * time.Second, MaxElapsedTime: 10 * time.Second, Clock: backoff.SystemClock, } b.Reset() return b}