Skip to content

Commit b2244f3

Browse files
committed
tests: Add e2e tests for http and grpc
Signed-off-by: joshjms <joshjms1607@gmail.com>
1 parent 9c2c03c commit b2244f3

2 files changed

Lines changed: 291 additions & 0 deletions

File tree

Makefile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,16 @@ build:
3131
.PHONY: dev
3232
dev:
3333
sudo env "PATH=$$PATH:/usr/local/go/bin" go run main.go server
34+
35+
.PHONY: e2e
36+
e2e: prepare-dirs make-rootfs
37+
@echo "Running end-to-end tests..."
38+
@echo "Building castletown..."
39+
@sudo env "PATH=$$PATH:/usr/local/go/bin" go build -o tmp/castletown main.go
40+
@echo "Starting castletown server..."
41+
@sudo tmp/castletown server &
42+
@sleep 2
43+
sudo env "PATH=$$PATH:/usr/local/go/bin" go test -v ./tests/e2e -timeout 2m
44+
@sudo pkill castletown
45+
@sudo rm -rf tmp
46+

tests/e2e/e2e_test.go

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
package e2e
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"io"
8+
"net/http"
9+
"testing"
10+
"time"
11+
12+
pb "github.com/joshjms/castletown/proto"
13+
"google.golang.org/grpc"
14+
"google.golang.org/grpc/credentials/insecure"
15+
)
16+
17+
const (
18+
httpURL = "http://localhost:8000"
19+
grpcAddr = "localhost:8001"
20+
defaultImage = "gcc:15-bookworm"
21+
)
22+
23+
type HTTPExecRequest struct {
24+
ID string `json:"id"`
25+
Files []HTTPFile `json:"files"`
26+
Steps []HTTPProcess `json:"steps"`
27+
}
28+
29+
type HTTPFile struct {
30+
Name string `json:"name"`
31+
Content string `json:"content"`
32+
}
33+
34+
type HTTPProcess struct {
35+
Image string `json:"image"`
36+
Cmd []string `json:"cmd"`
37+
Stdin string `json:"stdin"`
38+
MemoryLimitMB int64 `json:"memoryLimitMB"`
39+
TimeLimitMs uint64 `json:"timeLimitMs"`
40+
ProcLimit int64 `json:"procLimit"`
41+
Files []string `json:"files"`
42+
Persist []string `json:"persist"`
43+
}
44+
45+
type HTTPExecResponse struct {
46+
ID string `json:"id"`
47+
Reports []HTTPReport `json:"reports"`
48+
}
49+
50+
type HTTPReport struct {
51+
Status string `json:"Status"`
52+
ExitCode int `json:"ExitCode"`
53+
Signal int `json:"Signal"`
54+
Stdout string `json:"Stdout"`
55+
Stderr string `json:"Stderr"`
56+
CPUTime uint64 `json:"CPUTime"`
57+
Memory uint64 `json:"Memory"`
58+
WallTime int64 `json:"WallTime"`
59+
}
60+
61+
type HTTPDoneRequest struct {
62+
ID string `json:"id"`
63+
}
64+
65+
func TestHTTPExec(t *testing.T) {
66+
// Create a simple exec request
67+
req := HTTPExecRequest{
68+
ID: "test-http-exec",
69+
Files: []HTTPFile{
70+
{
71+
Name: "test.txt",
72+
Content: "Hello from HTTP test",
73+
},
74+
},
75+
Steps: []HTTPProcess{
76+
{
77+
Image: defaultImage,
78+
Cmd: []string{"/bin/cat", "test.txt"},
79+
Files: []string{"test.txt"},
80+
},
81+
},
82+
}
83+
84+
reqBody, err := json.Marshal(req)
85+
if err != nil {
86+
t.Fatalf("Failed to marshal request: %v", err)
87+
}
88+
89+
resp, err := http.Post(httpURL+"/exec", "application/json", bytes.NewReader(reqBody))
90+
if err != nil {
91+
t.Fatalf("Failed to send HTTP request: %v", err)
92+
}
93+
defer resp.Body.Close()
94+
95+
if resp.StatusCode != http.StatusOK {
96+
body, _ := io.ReadAll(resp.Body)
97+
t.Fatalf("Expected status 200, got %d: %s", resp.StatusCode, string(body))
98+
}
99+
100+
var execResp HTTPExecResponse
101+
if err := json.NewDecoder(resp.Body).Decode(&execResp); err != nil {
102+
t.Fatalf("Failed to decode response: %v", err)
103+
}
104+
105+
if execResp.ID != "test-http-exec" {
106+
t.Errorf("Expected ID 'test-http-exec', got '%s'", execResp.ID)
107+
}
108+
109+
if len(execResp.Reports) == 0 {
110+
t.Fatal("Expected at least one report")
111+
}
112+
113+
report := execResp.Reports[0]
114+
if report.Status != "OK" {
115+
t.Errorf("Expected status OK, got %s", report.Status)
116+
}
117+
118+
if report.ExitCode != 0 {
119+
t.Errorf("Expected exit code 0, got %d", report.ExitCode)
120+
}
121+
122+
t.Logf("HTTP Exec test passed. Stdout: %s", report.Stdout)
123+
}
124+
125+
func TestHTTPDone(t *testing.T) {
126+
req := HTTPDoneRequest{
127+
ID: "test-http-exec",
128+
}
129+
130+
reqBody, err := json.Marshal(req)
131+
if err != nil {
132+
t.Fatalf("Failed to marshal request: %v", err)
133+
}
134+
135+
resp, err := http.Post(httpURL+"/done", "application/json", bytes.NewReader(reqBody))
136+
if err != nil {
137+
t.Fatalf("Failed to send HTTP request: %v", err)
138+
}
139+
defer resp.Body.Close()
140+
141+
if resp.StatusCode != http.StatusOK {
142+
body, _ := io.ReadAll(resp.Body)
143+
t.Fatalf("Expected status 200, got %d: %s", resp.StatusCode, string(body))
144+
}
145+
146+
t.Log("HTTP Done test passed")
147+
}
148+
149+
func TestGRPCExec(t *testing.T) {
150+
conn, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
151+
if err != nil {
152+
t.Fatalf("Failed to connect to gRPC server: %v", err)
153+
}
154+
defer conn.Close()
155+
156+
client := pb.NewExecServiceClient(conn)
157+
158+
req := &pb.ExecRequest{
159+
Id: "test-grpc-exec",
160+
Files: []*pb.File{
161+
{
162+
Name: "test.txt",
163+
Content: "Hello from gRPC test",
164+
},
165+
},
166+
Procs: []*pb.Process{
167+
{
168+
Image: defaultImage,
169+
Cmd: []string{"/bin/cat", "test.txt"},
170+
Files: []string{"test.txt"},
171+
},
172+
},
173+
}
174+
175+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
176+
defer cancel()
177+
178+
resp, err := client.Execute(ctx, req)
179+
if err != nil {
180+
t.Fatalf("Failed to execute gRPC request: %v", err)
181+
}
182+
183+
if resp.Id != "test-grpc-exec" {
184+
t.Errorf("Expected ID 'test-grpc-exec', got '%s'", resp.Id)
185+
}
186+
187+
if len(resp.Reports) == 0 {
188+
t.Fatal("Expected at least one report")
189+
}
190+
191+
report := resp.Reports[0]
192+
if report.Status != pb.Status_STATUS_OK {
193+
t.Errorf("Expected status OK, got %v", report.Status)
194+
}
195+
196+
if report.ExitCode != 0 {
197+
t.Errorf("Expected exit code 0, got %d", report.ExitCode)
198+
}
199+
200+
t.Logf("gRPC Exec test passed. Stdout: %s", report.Stdout)
201+
}
202+
203+
func TestGRPCDone(t *testing.T) {
204+
conn, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
205+
if err != nil {
206+
t.Fatalf("Failed to connect to gRPC server: %v", err)
207+
}
208+
defer conn.Close()
209+
210+
client := pb.NewDoneServiceClient(conn)
211+
212+
req := &pb.DoneRequest{
213+
Id: "test-grpc-exec",
214+
}
215+
216+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
217+
defer cancel()
218+
219+
resp, err := client.Done(ctx, req)
220+
if err != nil {
221+
t.Fatalf("Failed to execute gRPC done request: %v", err)
222+
}
223+
224+
if resp == nil {
225+
t.Error("Expected non-nil response")
226+
}
227+
228+
t.Log("gRPC Done test passed")
229+
}
230+
231+
func TestHTTPAndGRPCIntegration(t *testing.T) {
232+
httpReq := HTTPExecRequest{
233+
ID: "test-integration",
234+
Files: []HTTPFile{
235+
{
236+
Name: "input.txt",
237+
Content: "Hello, world!",
238+
},
239+
},
240+
Steps: []HTTPProcess{
241+
{
242+
Image: defaultImage,
243+
Cmd: []string{"/bin/echo", "Hello, world!"},
244+
Files: []string{},
245+
},
246+
},
247+
}
248+
249+
reqBody, _ := json.Marshal(httpReq)
250+
httpResp, err := http.Post(httpURL+"/exec", "application/json", bytes.NewReader(reqBody))
251+
if err != nil {
252+
t.Fatalf("Failed to send HTTP request: %v", err)
253+
}
254+
defer httpResp.Body.Close()
255+
256+
if httpResp.StatusCode != http.StatusOK {
257+
body, _ := io.ReadAll(httpResp.Body)
258+
t.Fatalf("HTTP request failed with status %d: %s", httpResp.StatusCode, string(body))
259+
}
260+
261+
conn, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
262+
if err != nil {
263+
t.Fatalf("Failed to connect to gRPC server: %v", err)
264+
}
265+
defer conn.Close()
266+
267+
doneClient := pb.NewDoneServiceClient(conn)
268+
269+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
270+
defer cancel()
271+
272+
_, err = doneClient.Done(ctx, &pb.DoneRequest{Id: "test-integration"})
273+
if err != nil {
274+
t.Fatalf("Failed to mark job done via gRPC: %v", err)
275+
}
276+
277+
t.Log("Integration test passed: HTTP exec + gRPC done")
278+
}

0 commit comments

Comments
 (0)