package main import ( "context" "crypto/sha256" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "os" "path/filepath" "strconv" "strings" "testing" ) func TestSearchItemsContinuesWhenServerCapsLimit(t *testing.T) { var offsets []int server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/v1/sec/item/search" { t.Fatalf("unexpected path: %s", r.URL.Path) } var req struct { Offset int `json:"offset"` Limit int `json:"limit"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { t.Fatal(err) } offsets = append(offsets, req.Offset) count := 200 if req.Offset >= 400 { count = 0 } items := make([]SearchItem, count) for i := range items { items[i] = SearchItem{ID: strconv.Itoa(req.Offset + i)} } _ = json.NewEncoder(w).Encode(map[string]any{ "groups": []map[string]any{{"items": items}}, "limit": 200, "offset": req.Offset, }) })) defer server.Close() client := NewDocspellClient(server.URL, "") items, err := client.SearchItems(context.Background(), "*", 1000) if err != nil { t.Fatal(err) } if len(items) != 400 { t.Fatalf("expected 400 items, got %d", len(items)) } wantOffsets := []int{0, 200, 400} if len(offsets) != len(wantOffsets) { t.Fatalf("expected offsets %v, got %v", wantOffsets, offsets) } for i := range wantOffsets { if offsets[i] != wantOffsets[i] { t.Fatalf("expected offsets %v, got %v", wantOffsets, offsets) } } } func TestFileExistsUsesAuthenticatedChecksumEndpoint(t *testing.T) { file := filepath.Join(t.TempDir(), "doc.txt") content := []byte("hello docspell") if err := os.WriteFile(file, content, 0644); err != nil { t.Fatal(err) } wantHash := fmt.Sprintf("%x", sha256.Sum256(content)) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { t.Fatalf("expected GET, got %s", r.Method) } if r.URL.Path != "/api/v1/sec/checkfile/"+wantHash { t.Fatalf("unexpected path: %s", r.URL.Path) } if r.Header.Get(defaultDocspellAuthHeader) != "token-1" { t.Fatalf("missing auth header: %q", r.Header.Get(defaultDocspellAuthHeader)) } _ = json.NewEncoder(w).Encode(FileExistsResult{ Exists: true, Items: []DocspellItem{{ID: "item-1", Name: "Invoice", State: "confirmed", ItemDate: 1720396800000}}, }) })) defer server.Close() client := NewDocspellClient(server.URL, "") client.Token = "token-1" got, err := client.FileExists(context.Background(), file) if err != nil { t.Fatal(err) } if !got.Exists || got.Items[0].ID != "item-1" { t.Fatalf("unexpected result: %#v", got) } if got.File == "" { t.Fatal("expected local file path to be populated") } } func TestUploadFileUsesAuthenticatedMultipartEndpoint(t *testing.T) { file := filepath.Join(t.TempDir(), "upload.txt") if err := os.WriteFile(file, []byte("upload body"), 0644); err != nil { t.Fatal(err) } server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { t.Fatalf("expected POST, got %s", r.Method) } if r.URL.Path != "/api/v1/sec/upload/item" { t.Fatalf("unexpected path: %s", r.URL.Path) } if r.Header.Get(defaultDocspellAuthHeader) != "token-1" { t.Fatalf("missing auth header: %q", r.Header.Get(defaultDocspellAuthHeader)) } if !strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/form-data") { t.Fatalf("expected multipart content type, got %q", r.Header.Get("Content-Type")) } if err := r.ParseMultipartForm(10 << 20); err != nil { t.Fatal(err) } var meta UploadMeta if err := json.Unmarshal([]byte(r.FormValue("meta")), &meta); err != nil { t.Fatal(err) } if !meta.Multiple { t.Fatalf("expected multiple upload meta, got %#v", meta) } parts := r.MultipartForm.File["file"] if len(parts) != 1 || parts[0].Filename != "upload.txt" { t.Fatalf("unexpected file parts: %#v", parts) } opened, err := parts[0].Open() if err != nil { t.Fatal(err) } defer opened.Close() data, err := io.ReadAll(opened) if err != nil { t.Fatal(err) } if string(data) != "upload body" { t.Fatalf("unexpected upload body %q", string(data)) } _ = json.NewEncoder(w).Encode(BasicResult{Success: true, Message: "ok"}) })) defer server.Close() client := NewDocspellClient(server.URL, "") client.Token = "token-1" got, err := client.UploadFile(context.Background(), file, UploadMeta{Multiple: true}) if err != nil { t.Fatal(err) } if !got.Success { t.Fatalf("expected success, got %#v", got) } }