package main import ( "bytes" "encoding/json" "errors" "fmt" "io" "log" "net/http" "net/http/cookiejar" "os" "path/filepath" "time" "kra/internal/service/dto" ) const ( baseURL = "http://127.0.0.1:18080" wsURL = "ws://127.0.0.1:18080/ws" outputDir = ".tmp-live-acceptance" adminPassword = "LocalAdmin_2026!" userPassword = "LocalAccept_2026!" ) type apiClient struct { http *http.Client token string } type envelope struct { Code int `json:"code"` Data json.RawMessage `json:"data"` Msg string `json:"msg"` } type account struct { Username string `json:"username"` Password string `json:"password"` ID uint `json:"id"` } type fixtures struct { BaseURL string `json:"baseUrl"` Database string `json:"database"` CreatedAt string `json:"createdAt"` Admin account `json:"admin"` Anchor account `json:"anchor"` Viewer account `json:"viewer"` Other account `json:"other"` CategoryID uint `json:"categoryId"` RoomID uint `json:"roomId"` ReplayID uint `json:"replayRoomId"` DisabledID uint `json:"disabledRoomId"` BrowserID uint `json:"browserRoomId,omitempty"` GiftID uint `json:"giftId"` Checks []string `json:"setupChecks"` } func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) if len(os.Args) != 2 { log.Fatal("usage: go run ./tmp/live_acceptance ") } var err error switch os.Args[1] { case "setup": err = setup() case "verify": err = verify() case "publisher": err = holdPublisher() case "mock-payment": err = serveMockPayments() case "browser-room": err = setupBrowserRoom() default: err = errors.New("unknown command") } if err != nil { log.Fatal(err) } } func newClient() *apiClient { jar, _ := cookiejar.New(nil) return &apiClient{http: &http.Client{Jar: jar, Timeout: 30 * time.Second}} } func (c *apiClient) call(method, path string, body, out any) error { var reader io.Reader if body != nil { data, err := json.Marshal(body) if err != nil { return err } reader = bytes.NewReader(data) } req, err := http.NewRequest(method, baseURL+path, reader) if err != nil { return err } req.Header.Set("Content-Type", "application/json") if c.token != "" { req.Header.Set("x-token", c.token) } response, err := c.http.Do(req) if err != nil { return err } defer response.Body.Close() var result envelope if err = json.NewDecoder(response.Body).Decode(&result); err != nil { return fmt.Errorf("%s %s status=%d: %w", method, path, response.StatusCode, err) } if result.Code != 0 || response.StatusCode >= 400 { return fmt.Errorf("%s %s status=%d code=%d: %s", method, path, response.StatusCode, result.Code, result.Msg) } if out != nil { return json.Unmarshal(result.Data, out) } return nil } func login(user account, admin bool) (*apiClient, error) { c := newClient() path := "/live/client/auth/login" if admin { path = "/base/login" } var result struct { Token string `json:"token"` } if err := c.call("POST", path, map[string]any{"username": user.Username, "password": user.Password}, &result); err != nil { return nil, err } if result.Token == "" { return nil, errors.New("login returned no token") } c.token = result.Token return c, nil } func readFixtures() (*fixtures, error) { raw, err := os.ReadFile(filepath.Join(outputDir, "fixtures.json")) if err != nil { return nil, err } var f fixtures if err = json.Unmarshal(raw, &f); err != nil { return nil, err } absolute, err := filepath.Abs(filepath.Join(outputDir, "acceptance.db")) if err != nil || f.Database != absolute || f.BaseURL != baseURL { return nil, errors.New("fixture points outside the isolated acceptance environment") } return &f, nil } func saveJSON(name string, value any) error { data, err := json.MarshalIndent(value, "", " ") if err != nil { return err } return os.WriteFile(filepath.Join(outputDir, name), append(data, '\n'), 0600) } func must(err error) { if err != nil { panic(err) } } func check(condition bool, format string, args ...any) { if !condition { panic(fmt.Sprintf(format, args...)) } } func roomPath(id uint) string { return fmt.Sprintf("/live/rooms/%d", id) } func adminRoomPath(id uint) string { return fmt.Sprintf("/live/admin/rooms/%d", id) } func makeRoom(name string, categoryID, anchorID uint, replay string, enabled bool) dto.LiveRoomRequest { return dto.LiveRoomRequest{ Name: name, CategoryID: categoryID, AnchorUserID: anchorID, CoverURL: "/uploads/acceptance-cover.jpg", Intro: "Local isolated acceptance room", ReplayURL: replay, Enabled: enabled, } }