| 1 | package docs_test |
| 2 | |
| 3 | import ( |
| 4 | "encoding/json" |
| 5 | "github.com/carlosframework/docs/internal/docs" |
| 6 | "net" |
| 7 | "net/http" |
| 8 | "net/http/httptest" |
| 9 | "os" |
| 10 | "path/filepath" |
| 11 | "strings" |
| 12 | "testing" |
| 13 | "time" |
| 14 | ) |
| 15 | |
| 16 | // The kill script uses the actual authenticated application, update log and |
| 17 | // snapshot handlers on loopback. No substitute server accepts the updates. |
| 18 | func TestNativeKillServer(t *testing.T) { |
| 19 | dir := os.Getenv("DOCS_KILL_DIR") |
| 20 | if dir == "" { |
| 21 | t.Skip("Run native/scripts/test-offline-kill.py") |
| 22 | } |
| 23 | listener, err := net.Listen("tcp", "127.0.0.1:0") |
| 24 | if err != nil { |
| 25 | t.Fatal(err) |
| 26 | } |
| 27 | defer listener.Close() |
| 28 | app, err := docs.NewForTestWith(t, docs.Config{AllowedAddresses: "kill@example.com", Origin: "http://" + listener.Addr().String()}) |
| 29 | if err != nil { |
| 30 | t.Fatal(err) |
| 31 | } |
| 32 | cookies := signInCookies(t, app, "kill@example.com") |
| 33 | create := httptest.NewRequest("POST", "/documents", strings.NewReader("title=Kill+recovery")) |
| 34 | create.Header.Set("Origin", "http://"+listener.Addr().String()) |
| 35 | create.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| 36 | for _, cookie := range cookies { |
| 37 | create.AddCookie(cookie) |
| 38 | } |
| 39 | created := httptest.NewRecorder() |
| 40 | app.Handler().ServeHTTP(created, create) |
| 41 | if created.Code != http.StatusSeeOther { |
| 42 | t.Fatalf("create: status %d", created.Code) |
| 43 | } |
| 44 | sqid := strings.TrimPrefix(created.Header().Get("Location"), "/d/") |
| 45 | server := httptest.NewUnstartedServer(app.Handler()) |
| 46 | server.Listener.Close() |
| 47 | server.Listener = listener |
| 48 | server.Start() |
| 49 | defer server.Close() |
| 50 | // Match NativeHTTP: Origin, without a browser's Sec-Fetch-Site shortcut. |
| 51 | request, err := http.NewRequest("POST", server.URL+"/d/"+sqid+"/title", strings.NewReader("title=Kill+recovery")) |
| 52 | if err != nil { |
| 53 | t.Fatal(err) |
| 54 | } |
| 55 | request.Header.Set("Origin", server.URL) |
| 56 | request.Header.Set("Accept", "application/json") |
| 57 | request.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| 58 | for _, cookie := range cookies { |
| 59 | request.AddCookie(cookie) |
| 60 | } |
| 61 | response, err := http.DefaultClient.Do(request) |
| 62 | if err != nil { |
| 63 | t.Fatal(err) |
| 64 | } |
| 65 | response.Body.Close() |
| 66 | if response.StatusCode < 200 || response.StatusCode >= 300 { |
| 67 | t.Fatalf("native-origin initial save: status %d, want 200", response.StatusCode) |
| 68 | } |
| 69 | config := map[string]string{"origin": server.URL, "sqid": sqid, "name": cookies[0].Name, "value": cookies[0].Value} |
| 70 | data, err := json.Marshal(config) |
| 71 | if err != nil { |
| 72 | t.Fatal(err) |
| 73 | } |
| 74 | if err := os.WriteFile(filepath.Join(dir, "connection.json"), data, 0600); err != nil { |
| 75 | t.Fatal(err) |
| 76 | } |
| 77 | deadline := time.Now().Add(6 * time.Minute) |
| 78 | for time.Now().Before(deadline) { |
| 79 | if _, err := os.Stat(filepath.Join(dir, "done")); err == nil { |
| 80 | return |
| 81 | } |
| 82 | time.Sleep(20 * time.Millisecond) |
| 83 | } |
| 84 | t.Fatal("kill script never completed") |
| 85 | } |
| 86 | |