X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/522d7fd117285e4e4ce3ea5ec9b5a1722521b681..refs/heads/master:/telegram_fake_helper_test.go?ds=sidebyside diff --git a/telegram_fake_helper_test.go b/telegram_fake_helper_test.go index 3056aa1..b71eee0 100644 --- a/telegram_fake_helper_test.go +++ b/telegram_fake_helper_test.go @@ -2,8 +2,10 @@ package main import ( "encoding/json" + "errors" "fmt" "io" + "mime/multipart" "net/http" "net/http/httptest" "strings" @@ -45,6 +47,7 @@ type TelegramFake struct { stall func(call TelegramFakeCall) bool added chan struct{} stopped chan struct{} + images map[string][]byte } // Add makes update available to getUpdates. @@ -86,7 +89,49 @@ func (f *TelegramFake) Sent() []string { return texts } +func (f *TelegramFake) Image(id string, data []byte) { + f.mutex.Lock() + defer f.mutex.Unlock() + + if f.images == nil { + f.images = make(map[string][]byte) + } + + f.images[id] = data +} + +// RoundTrip handles a request in memory, so synctest can control its timers. +func (f *TelegramFake) RoundTrip(request *http.Request) (*http.Response, error) { + response := httptest.NewRecorder() + f.serve(response, request) + + ctx := request.Context() + if ctx.Err() != nil { + return nil, ctx.Err() + } + + return response.Result(), nil +} + func (f *TelegramFake) serve(w http.ResponseWriter, r *http.Request) { + id, file := strings.CutPrefix(r.URL.Path, "/file/bot"+telegramTestToken+"/") + if file { + f.mutex.Lock() + + data, exists := f.images[id] + f.mutex.Unlock() + + if !exists { + http.NotFound(w, r) + + return + } + + _, _ = w.Write(data) + + return + } + method, found := strings.CutPrefix(r.URL.Path, "/bot"+telegramTestToken+"/") if !found { http.Error(w, `{"ok":false,"error_code":404,"description":"Not Found"}`, http.StatusNotFound) @@ -96,7 +141,38 @@ func (f *TelegramFake) serve(w http.ResponseWriter, r *http.Request) { var params map[string]any - err := json.NewDecoder(r.Body).Decode(¶ms) + var err error + + if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/form-data;") { + err = r.ParseMultipartForm(imageMaxBytes + (1 << 20)) + if err == nil { + params = make(map[string]any) + for key, values := range r.MultipartForm.Value { + params[key] = values[0] + } + + for key, files := range r.MultipartForm.File { + var file multipart.File + + file, err = files[0].Open() + if err != nil { + break + } + + params[key], err = io.ReadAll(file) + err = errors.Join(err, file.Close()) + params[key+"_name"] = files[0].Filename + if err != nil { + break + } + } + + err = errors.Join(err, r.MultipartForm.RemoveAll()) + } + } else { + err = json.NewDecoder(r.Body).Decode(¶ms) + } + if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) @@ -146,6 +222,15 @@ func (f *TelegramFake) serve(w http.ResponseWriter, r *http.Request) { call.ok = true f.record(call) + if method == "getFile" { + _ = json.NewEncoder(w).Encode(map[string]any{ + "ok": true, + "result": map[string]any{"file_path": params["file_id"]}, + }) + + return + } + if method != "getUpdates" { _, _ = io.WriteString(w, `{"ok":true,"result":true}`) return @@ -217,6 +302,23 @@ func newTelegramFake(t *testing.T) *TelegramFake { return f } +// newTelegramLocalFake returns the same fake API without network goroutines. +// Its RoundTrip handles requests directly for tests with fake time. +func newTelegramLocalFake(t *testing.T) *TelegramFake { + t.Helper() + + f := &TelegramFake{ + Server: &httptest.Server{URL: "https://example.com"}, + added: make(chan struct{}, 1), + stopped: make(chan struct{}), + } + t.Cleanup(func() { + close(f.stopped) + }) + + return f +} + // telegramOwnerMessage returns a text message of the owner in the private chat. func telegramOwnerMessage(text string) *TelegramMessage { return &TelegramMessage{