1package plugin
2
3import (
4 "io"
5 "net/http"
6 "net/http/httptest"
7 "strings"
8 "testing"
9
10 lua "github.com/yuin/gopher-lua"
11)
12
13// newTestManager creates a Manager with a fresh Lua VM for testing.
14func newTestManager() *Manager {
15 return NewManager()
16}
17
18func TestHTTPGet(t *testing.T) {
19 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
20 if r.Method != http.MethodGet {
21 t.Errorf("expected GET, got %s", r.Method)
22 }
23 w.Header().Set("X-Test", "hello")
24 w.WriteHeader(http.StatusOK)
25 w.Write([]byte("ok"))
26 }))
27 defer srv.Close()
28
29 m := newTestManager()
30 defer m.Close()
31
32 err := m.state.DoString(`
33 local matcha = require("matcha")
34 res, err = matcha.http({ url = "` + srv.URL + `" })
35 `)
36 if err != nil {
37 t.Fatal(err)
38 }
39
40 errVal := m.state.GetGlobal("err")
41 if errVal != lua.LNil {
42 t.Fatalf("expected nil error, got %v", errVal)
43 }
44
45 res := m.state.GetGlobal("res")
46 tbl, ok := res.(*lua.LTable)
47 if !ok {
48 t.Fatalf("expected table, got %T", res)
49 }
50
51 if status := tbl.RawGetString("status"); status.(lua.LNumber) != 200 {
52 t.Errorf("expected status 200, got %v", status)
53 }
54 if body := tbl.RawGetString("body"); body.String() != "ok" {
55 t.Errorf("expected body 'ok', got %q", body.String())
56 }
57
58 headers := tbl.RawGetString("headers").(*lua.LTable)
59 if v := headers.RawGetString("x-test"); v.String() != "hello" {
60 t.Errorf("expected header x-test='hello', got %q", v.String())
61 }
62}
63
64func TestHTTPPostWithBodyAndHeaders(t *testing.T) {
65 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
66 if r.Method != http.MethodPost {
67 t.Errorf("expected POST, got %s", r.Method)
68 }
69 if ct := r.Header.Get("Content-Type"); ct != "application/json" {
70 t.Errorf("expected Content-Type application/json, got %q", ct)
71 }
72 body, _ := io.ReadAll(r.Body)
73 w.Write(body)
74 }))
75 defer srv.Close()
76
77 m := newTestManager()
78 defer m.Close()
79
80 err := m.state.DoString(`
81 local matcha = require("matcha")
82 res, err = matcha.http({
83 url = "` + srv.URL + `",
84 method = "post",
85 headers = { ["Content-Type"] = "application/json" },
86 body = '{"key":"value"}',
87 })
88 `)
89 if err != nil {
90 t.Fatal(err)
91 }
92
93 errVal := m.state.GetGlobal("err")
94 if errVal != lua.LNil {
95 t.Fatalf("expected nil error, got %v", errVal)
96 }
97
98 res := m.state.GetGlobal("res")
99 tbl := res.(*lua.LTable)
100 if body := tbl.RawGetString("body"); body.String() != `{"key":"value"}` {
101 t.Errorf("expected echoed body, got %q", body.String())
102 }
103}
104
105func TestHTTPMissingURL(t *testing.T) {
106 m := newTestManager()
107 defer m.Close()
108
109 err := m.state.DoString(`
110 local matcha = require("matcha")
111 res, err = matcha.http({})
112 `)
113 if err != nil {
114 t.Fatal(err)
115 }
116
117 resVal := m.state.GetGlobal("res")
118 if resVal != lua.LNil {
119 t.Errorf("expected nil result, got %v", resVal)
120 }
121
122 errVal := m.state.GetGlobal("err")
123 if errVal == lua.LNil {
124 t.Fatal("expected error, got nil")
125 }
126 if !strings.Contains(errVal.String(), "url") {
127 t.Errorf("expected error about url, got %q", errVal.String())
128 }
129}
130
131func TestHTTPInvalidScheme(t *testing.T) {
132 m := newTestManager()
133 defer m.Close()
134
135 err := m.state.DoString(`
136 local matcha = require("matcha")
137 res, err = matcha.http({ url = "file:///etc/passwd" })
138 `)
139 if err != nil {
140 t.Fatal(err)
141 }
142
143 resVal := m.state.GetGlobal("res")
144 if resVal != lua.LNil {
145 t.Errorf("expected nil result, got %v", resVal)
146 }
147
148 errVal := m.state.GetGlobal("err")
149 if errVal == lua.LNil {
150 t.Fatal("expected error, got nil")
151 }
152 if !strings.Contains(errVal.String(), "scheme") {
153 t.Errorf("expected error about scheme, got %q", errVal.String())
154 }
155}
156
157func TestHTTPBodyTruncation(t *testing.T) {
158 // Server returns more than 1 MB.
159 bigBody := strings.Repeat("x", httpMaxBodySize+1024)
160 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
161 w.Write([]byte(bigBody))
162 }))
163 defer srv.Close()
164
165 m := newTestManager()
166 defer m.Close()
167
168 err := m.state.DoString(`
169 local matcha = require("matcha")
170 res, err = matcha.http({ url = "` + srv.URL + `" })
171 body_len = #res.body
172 `)
173 if err != nil {
174 t.Fatal(err)
175 }
176
177 bodyLen := m.state.GetGlobal("body_len")
178 n, ok := bodyLen.(lua.LNumber)
179 if !ok {
180 t.Fatalf("expected number, got %T", bodyLen)
181 }
182 if int(n) > httpMaxBodySize {
183 t.Errorf("expected body to be capped at %d, got %d", httpMaxBodySize, int(n))
184 }
185}