init
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
package api_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"wh/api"
|
||||
"wh/config"
|
||||
"wh/store"
|
||||
"wh/web"
|
||||
)
|
||||
|
||||
// newSite starts the API with the web front end mounted, as cmd/server does.
|
||||
func newSite(t *testing.T) (*httptest.Server, config.Config) {
|
||||
t.Helper()
|
||||
cfg := config.Default()
|
||||
st, err := store.Open("sqlite:" + filepath.Join(t.TempDir(), "t.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { st.Close() })
|
||||
if err := st.Migrate(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mux := api.New(st, cfg)
|
||||
mux.Handle("/", web.Handler())
|
||||
srv := httptest.NewServer(mux)
|
||||
t.Cleanup(srv.Close)
|
||||
return srv, cfg
|
||||
}
|
||||
|
||||
func get(t *testing.T, url string) (*http.Response, string) {
|
||||
t.Helper()
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
return resp, string(b)
|
||||
}
|
||||
|
||||
func TestInfoAndAssemble(t *testing.T) {
|
||||
srv, cfg := newSite(t)
|
||||
|
||||
_, body := get(t, srv.URL+"/info")
|
||||
var info struct {
|
||||
ProgramBytes int `json:"program_bytes"`
|
||||
CommBytes int `json:"comm_bytes"`
|
||||
Ores []string `json:"ores"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(body), &info); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if info.ProgramBytes != cfg.ProgramBytes || info.CommBytes != cfg.CommBytes || len(info.Ores) != 4 {
|
||||
t.Fatalf("info = %+v", info)
|
||||
}
|
||||
|
||||
post := func(src string) (int, map[string]any) {
|
||||
resp, err := http.Post(srv.URL+"/assemble", "text/plain", strings.NewReader(src))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var m map[string]any
|
||||
json.NewDecoder(resp.Body).Decode(&m)
|
||||
return resp.StatusCode, m
|
||||
}
|
||||
code, m := post("ldi r1, 5\nhalt")
|
||||
if code != 200 || m["size"].(float64) != 8 || m["fits"] != true {
|
||||
t.Fatalf("assemble ok case: %d %v", code, m)
|
||||
}
|
||||
if raw, _ := base64.StdEncoding.DecodeString(m["program"].(string)); len(raw) != 8 {
|
||||
t.Fatalf("program bytes = %d", len(raw))
|
||||
}
|
||||
code, m = post("ldi r1, 5\nbogus r1")
|
||||
if code != 400 || !strings.Contains(m["error"].(string), "line 2") {
|
||||
t.Fatalf("assemble error case: %d %v", code, m)
|
||||
}
|
||||
// Too big for the belt: assembles, but reports that it does not fit.
|
||||
code, m = post(strings.Repeat("nop\n", cfg.ProgramBytes/4+1))
|
||||
if code != 200 || m["fits"] != false {
|
||||
t.Fatalf("oversize case: %d %v", code, m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebAssets(t *testing.T) {
|
||||
srv, _ := newSite(t)
|
||||
for path, want := range map[string]string{
|
||||
"/": "<title>",
|
||||
"/manual.txt": "PROGRAMMER'S REFERENCE MANUAL",
|
||||
"/examples/echo.s": "ECHO",
|
||||
"/examples/index.json": `"pursue"`,
|
||||
} {
|
||||
resp, body := get(t, srv.URL+path)
|
||||
if resp.StatusCode != 200 || !strings.Contains(body, want) {
|
||||
t.Errorf("%s: status %d, missing %q", path, resp.StatusCode, want)
|
||||
}
|
||||
}
|
||||
resp, _ := get(t, srv.URL+"/")
|
||||
if !strings.Contains(resp.Header.Get("Content-Security-Policy"), "default-src 'self'") {
|
||||
t.Error("missing CSP header")
|
||||
}
|
||||
// The API still wins over the catch-all.
|
||||
if resp, _ := get(t, srv.URL+"/market"); !strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") {
|
||||
t.Errorf("/market served as %q", resp.Header.Get("Content-Type"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user