package main import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "testing" "time" ) func testApplication(t *testing.T) *application { t.Helper() store, err := openPlaygroundStore(filepath.Join(t.TempDir(), "test.db")) if err != nil { t.Fatal(err) } t.Cleanup(func() { store.Close() }) monaco := filepath.Join(t.TempDir(), "monaco") if err := os.MkdirAll(monaco, 0o755); err != nil { t.Fatal(err) } app, err := newApplication(store, map[string]memoryAsset{ "core.js": { contentType: "text/javascript; charset=utf-8", content: []byte("export const ready = true"), etag: `"core"`, modified: time.Unix(0, 0), }, }, monaco) if err != nil { t.Fatal(err) } return app } func TestPackageIsServedFromMemory(t *testing.T) { request := httptest.NewRequest(http.MethodGet, "/pkg/core.js", nil) response := httptest.NewRecorder() testApplication(t).routes().ServeHTTP(response, request) if response.Code != http.StatusOK { t.Fatalf("status = %d", response.Code) } if response.Body.String() != "export const ready = true" { t.Fatalf("body = %q", response.Body.String()) } if response.Header().Get("Cross-Origin-Opener-Policy") != "same-origin" { t.Fatal("cross-origin isolation header is missing") } if response.Header().Get("Access-Control-Allow-Origin") != "*" { t.Fatal("package CORS header is missing") } if response.Header().Get("Cross-Origin-Resource-Policy") != "cross-origin" { t.Fatal("package resource policy is missing") } } func TestSaveAndLoadPlaygroundRevision(t *testing.T) { app := testApplication(t) handler := app.routes() requestBody, _ := json.Marshal(map[string]string{ "title": "First scene", "code": "const scene = true;", }) request := httptest.NewRequest(http.MethodPost, "/api/playgrounds", bytes.NewReader(requestBody)) response := httptest.NewRecorder() handler.ServeHTTP(response, request) if response.Code != http.StatusCreated { t.Fatalf("save status = %d: %s", response.Code, response.Body.String()) } var saved playground if err := json.NewDecoder(response.Body).Decode(&saved); err != nil { t.Fatal(err) } if saved.Revision != 1 || !playgroundIDPattern.MatchString(saved.ID) { t.Fatalf("saved = %#v", saved) } requestBody, _ = json.Marshal(map[string]string{ "id": saved.ID, "title": "Second scene", "code": "const scene = false;", }) request = httptest.NewRequest(http.MethodPost, "/api/playgrounds", bytes.NewReader(requestBody)) response = httptest.NewRecorder() handler.ServeHTTP(response, request) if response.Code != http.StatusCreated { t.Fatalf("second save status = %d: %s", response.Code, response.Body.String()) } var second playground if err := json.NewDecoder(response.Body).Decode(&second); err != nil { t.Fatal(err) } if second.ID != saved.ID || second.Revision != 2 { t.Fatalf("second = %#v", second) } request = httptest.NewRequest(http.MethodGet, "/api/playgrounds/"+saved.ID+"/1", nil) response = httptest.NewRecorder() handler.ServeHTTP(response, request) if response.Code != http.StatusOK { t.Fatalf("load status = %d: %s", response.Code, response.Body.String()) } var loaded playground if err := json.NewDecoder(response.Body).Decode(&loaded); err != nil { t.Fatal(err) } if loaded.Code != "const scene = true;" || loaded.Title != "First scene" { t.Fatalf("loaded = %#v", loaded) } request = httptest.NewRequest(http.MethodGet, "/api/playgrounds/"+saved.ID+"/2", nil) response = httptest.NewRecorder() handler.ServeHTTP(response, request) if response.Code != http.StatusOK { t.Fatalf("second load status = %d: %s", response.Code, response.Body.String()) } if err := json.NewDecoder(response.Body).Decode(&loaded); err != nil { t.Fatal(err) } if loaded.Code != "const scene = false;" || loaded.Title != "Second scene" { t.Fatalf("second loaded = %#v", loaded) } } func TestSavedPlaygroundPageRoute(t *testing.T) { request := httptest.NewRequest(http.MethodGet, "/playground/abcdef12/1", nil) response := httptest.NewRecorder() testApplication(t).routes().ServeHTTP(response, request) if response.Code != http.StatusOK { t.Fatalf("status = %d", response.Code) } if contentType := response.Header().Get("Content-Type"); contentType != "text/html; charset=utf-8" { t.Fatalf("content type = %q", contentType) } }