Skip to content

Commit

Permalink
save local testing progress
Browse files Browse the repository at this point in the history
  • Loading branch information
conneroisu committed Oct 20, 2024
1 parent 900d516 commit 658095e
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 55 deletions.
44 changes: 18 additions & 26 deletions extensions/e2b/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,6 @@ import (
)

type (
// Requester is an interface for an instance that sends requests to a filesystem.
//
// Implementations should be conccurent safe.
Requester interface {
// Write writes a file to the filesystem.
Write(
ctx context.Context,
method Method,
params []any,
respCh chan []byte,
)
// Read reads a file from the filesystem.
Read(
ctx context.Context,
path string,
) (string, error)
}
// Receiver is an interface for a constantly receiving instance.
//
// Implementations should be conccurent safe.
Expand All @@ -37,11 +20,28 @@ type (
}
// Sandboxer is an interface for a sandbox.
Sandboxer interface {
Lifer
// KeepAlive keeps the underlying interface alive.
//
// If the context is cancelled before requesting the timeout,
// the error will be ctx.Err().
KeepAlive(ctx context.Context, timeout time.Duration) error
// NewProcess creates a new process.
NewProcess(
cmd string,
) (*Processor, error)

// Write writes a file to the filesystem.
Write(
ctx context.Context,
method Method,
params []any,
respCh chan<- []byte,
)
// Read reads a file from the filesystem.
Read(
ctx context.Context,
path string,
) (string, error)
}
// Processor is an interface for a process.
Processor interface {
Expand All @@ -56,14 +56,6 @@ type (
eCh chan<- Event,
)
}
// Lifer is an interface for keeping sandboxes alive.
Lifer interface {
// KeepAlive keeps the underlying interface alive.
//
// If the context is cancelled before requesting the timeout,
// the error will be ctx.Err().
KeepAlive(ctx context.Context, timeout time.Duration) error
}
// Watcher is an interface for a instance that can watch a filesystem.
Watcher interface {
Watch(
Expand Down
166 changes: 139 additions & 27 deletions extensions/e2b/sandbox_test.go
Original file line number Diff line number Diff line change
@@ -1,65 +1,177 @@
package e2b_test
package e2b

import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"

"github.com/conneroisu/groq-go/extensions/e2b"
"github.com/conneroisu/groq-go/pkg/test"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
)

var upgrader = websocket.Upgrader{}

func echo(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
defer c.Close()
for {
mt, message, err := c.ReadMessage()
func echo(a *assert.Assertions) func(w http.ResponseWriter, r *http.Request) {
mu := sync.Mutex{}
return func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
defer mu.Unlock()
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
break
return
}
err = c.WriteMessage(mt, message)
if err != nil {
break
defer c.Close()
for {
mt, message, err := c.ReadMessage()
a.NoError(err)
defaultLogger.Debug("server read message", "msg", message)
req := decode(message)
switch req.Method {
case filesystemList:
err = c.WriteMessage(mt, encode(Response[[]LsResult, string]{
ID: req.ID,
Error: "",
Result: []LsResult{
{
Name: "hello.txt",
IsDir: false,
},
},
}))
a.NoError(err)
case filesystemRead:
err = c.WriteMessage(mt, encode(Response[string, string]{
ID: req.ID,
Error: "",
Result: "hello",
}))
a.NoError(err)
case filesystemWrite:
err = c.WriteMessage(mt, encode(Response[string, string]{
ID: req.ID,
Error: "",
Result: "hello",
}))
a.NoError(err)
case processStart:
err = c.WriteMessage(mt, encode(Response[string, APIError]{
ID: req.ID,
Error: APIError{},
Result: req.Params[0].(string),
}))
a.NoError(err)
case processSubscribe:
err = c.WriteMessage(mt, encode(Response[string, APIError]{
ID: req.ID,
Error: APIError{},
Result: "test-proc-id",
}))
a.NoError(err)
err = c.WriteMessage(mt, encode(Response[
EventParams, APIError,
]{
ID: req.ID,
Error: APIError{},
Result: EventParams{
Subscription: "test-proc-id",
Result: EventResult{
Type: "Stdout",
Line: "hello",
Timestamp: 0,
IsDirectory: false,
Error: "",
},
},
}))
a.NoError(err)
case filesystemMakeDir:
err = c.WriteMessage(mt, encode(Response[string, APIError]{
ID: req.ID,
Error: APIError{},
Result: "",
}))
default:
err = c.WriteMessage(mt, message)
a.NoError(err)
}
}
}
}

func encode(v any) []byte {
res, err := json.Marshal(v)
if err != nil {
panic(err)
}
return res
}
func decode(bod []byte) Request {
var req Request
err := json.Unmarshal(bod, &req)
if err != nil {
panic(err)
}
return req
}

func TestNewSandbox(t *testing.T) {
a := assert.New(t)
ctx := context.Background()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
srv := test.NewTestServer()
ts := srv.E2bTestServer()
wsts := httptest.NewServer(http.HandlerFunc(echo))
srv.RegisterHandler("/sandboxes", func(w http.ResponseWriter, r *http.Request) {
srv.Logger.Debug("received sandboxes request")
wsts := httptest.NewServer(http.HandlerFunc(echo(a)))
id := "test-sandbox-id"
srv.RegisterHandler("/sandboxes", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(`{"sandboxID": "test-sandbox"}`))
if err != nil {
http.Error(w, "failed to write response", http.StatusInternalServerError)
return
}
_, err := w.Write(encode(&Sandbox{ID: id}))
a.NoError(err)
})
ts.Start()
u := "ws" + strings.TrimPrefix(wsts.URL, "http")
sb, err := e2b.NewSandbox(
// Create a new sandbox.
sb, err := NewSandbox(
ctx,
test.GetTestToken(),
e2b.WithLogger(defaultLogger),
e2b.WithBaseURL(ts.URL),
e2b.WithWsURL(func(s *e2b.Sandbox) string {
WithLogger(defaultLogger),
WithBaseURL(ts.URL),
WithWsURL(func(_ *Sandbox) string {
return u + "/ws"
}),
)
a.NoError(err, "NewSandbox error")
a.NotNil(sb, "NewSandbox returned nil")
a.Equal(sb.ID, id)
// Call ls on the sandbox.
lsRes, err := sb.Ls(ctx, ".")
a.NoError(err)
a.NotEmpty(lsRes)
// Call mkdir on the sandbox.
err = sb.Mkdir(ctx, "hello")
a.NoError(err)
// Call write on the sandbox.
err = sb.Write(ctx, "hello.txt", []byte("hello"))
a.NoError(err)
// Call read on the sandbox.
readRes, err := sb.Read(ctx, "hello.txt")
a.NoError(err)
a.Equal("hello", readRes)
// create a process
proc, err := sb.NewProcess("sleep 5 && echo 'hello world!'", Process{})
a.NoError(err)
events := make(chan Event, 10)
err = proc.Start(ctx)
a.NoError(err)
err = proc.Subscribe(ctx, OnStdout, events)
a.NoError(err)
event := <-events
jsnBytes, err := json.MarshalIndent(&event, "", " ")
a.NoError(err)
t.Logf("test got event: %s", string(jsnBytes))
}
2 changes: 0 additions & 2 deletions pkg/test/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package test

import (
"log"
"log/slog"
"net/http"
"net/http/httptest"
"regexp"
Expand All @@ -21,7 +20,6 @@ func GetTestToken() string {
// ServerTest is a test server for the groq api.
type ServerTest struct {
handlers map[string]handler
Logger *slog.Logger
}

// handler is a function that handles a request.
Expand Down

0 comments on commit 658095e

Please sign in to comment.