chore: move business tooling out of the public library; tidy filestore naming
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go stable) (pull_request) Successful in 1m19s
Tests / Test (Go 1.25) (pull_request) Successful in 1m24s
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go stable) (pull_request) Successful in 1m19s
Tests / Test (Go 1.25) (pull_request) Successful in 1m24s
Keep the public tree project-generic. Business/one-off tools, deployment and business docs move to the private oo-workspace repo. Moved to oo-workspace: - cmd/ooscan, cmd/pdfamount, cmd/kontoblatt, cmd/kontolink - internal/xlspipe (cutover-portugal workbook) -> oow workbook build (drops the --template/--title flags from oo docs put-xlsx) - deploy/docker-compose.rclone-webdav.yml + docs/rclone-webdav.md - docs/crm-associations.md Removed GitHub-era leftovers: - .github/workflows/release-please.yml, release-please-config.json, .release-please-manifest.json (tags are created on Gitea per SemVer) Naming: the FileStore subsystem is now filestore_*.go (was file_*.go) to match files_*.go (project Documents). Docs/AGENTS/README updated.
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
//go:build integration
|
||||
|
||||
package onlyoffice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/eslider/go-onlyoffice/internal/docpipe"
|
||||
)
|
||||
|
||||
// TestIntegrationESTextIndex verifies the own full-text index end to end
|
||||
// against a live Elasticsearch: create the index with its mapping, index a
|
||||
// document, find it by content (and reject it via a folder filter and after
|
||||
// deletion), then drop the throwaway index.
|
||||
//
|
||||
// Requires ONLYOFFICE_ES_URL (a reachable ES endpoint — in the current setup a
|
||||
// tunnel to the ES inside the OnlyOffice VM, see docs/elasticsearch.md). It
|
||||
// does not need OnlyOffice credentials because no file is downloaded: the
|
||||
// TextIndexer write path is covered by unit tests with a fake extractor.
|
||||
func TestIntegrationESTextIndex(t *testing.T) {
|
||||
esURL := strings.TrimSpace(os.Getenv("ONLYOFFICE_ES_URL"))
|
||||
if esURL == "" {
|
||||
t.Skip("ONLYOFFICE_ES_URL not set — skipping Elasticsearch integration test")
|
||||
}
|
||||
stamp := time.Now().UTC().Format("20060102150405")
|
||||
idx, err := NewESTextIndex(ESTextConfig{URL: esURL, Index: "oo_docs_text_it_" + stamp})
|
||||
if err != nil {
|
||||
t.Fatalf("NewESTextIndex: %v", err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanupCtx, done := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer done()
|
||||
_, _, _ = idx.do(cleanupCtx, http.MethodDelete, "/"+idx.Index(), nil, "")
|
||||
})
|
||||
|
||||
if err := idx.Ensure(ctx); err != nil {
|
||||
t.Fatalf("Ensure: %v", err)
|
||||
}
|
||||
// Ensure is idempotent.
|
||||
if err := idx.Ensure(ctx); err != nil {
|
||||
t.Fatalf("Ensure (second): %v", err)
|
||||
}
|
||||
|
||||
token := "gotes" + stamp
|
||||
doc := TextDoc{
|
||||
ID: "3578",
|
||||
Title: "2026-07-28-S1021-acme-rechnung.pdf",
|
||||
FolderID: "634",
|
||||
Ext: "pdf",
|
||||
Content: "Begleitzettel SGB XI — Rechnung " + token,
|
||||
}
|
||||
if err := idx.Put(ctx, []TextDoc{doc}); err != nil {
|
||||
t.Fatalf("Put: %v", err)
|
||||
}
|
||||
|
||||
hits, err := idx.Search(ctx, SearchQuery{Text: token})
|
||||
if err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
if len(hits) != 1 || hits[0].ID != "3578" {
|
||||
t.Fatalf("content search hits = %+v, want doc 3578", hits)
|
||||
}
|
||||
if !strings.Contains(hits[0].Highlight, token) {
|
||||
t.Errorf("highlight = %q, want token", hits[0].Highlight)
|
||||
}
|
||||
|
||||
if hits, err := idx.Search(ctx, SearchQuery{Text: token, FolderID: "999"}); err != nil {
|
||||
t.Fatalf("Search with folder filter: %v", err)
|
||||
} else if len(hits) != 0 {
|
||||
t.Errorf("folder filter returned %d hits, want 0", len(hits))
|
||||
}
|
||||
if hits, err := idx.Search(ctx, SearchQuery{Text: token, Extensions: []string{"docx"}}); err != nil {
|
||||
t.Fatalf("Search with ext filter: %v", err)
|
||||
} else if len(hits) != 0 {
|
||||
t.Errorf("ext filter returned %d hits, want 0", len(hits))
|
||||
}
|
||||
|
||||
if err := idx.Delete(ctx, []string{"3578"}); err != nil {
|
||||
t.Fatalf("Delete: %v", err)
|
||||
}
|
||||
if hits, err := idx.Search(ctx, SearchQuery{Text: token}); err != nil {
|
||||
t.Fatalf("Search after delete: %v", err)
|
||||
} else if len(hits) != 0 {
|
||||
t.Errorf("after delete search returned %d hits, want 0", len(hits))
|
||||
}
|
||||
}
|
||||
|
||||
// TestIntegrationESTextIndexPDFAttachment indexes testdata/pdf-with-attachment.pdf
|
||||
// through the real pipeline (TextIndexer + docpipe: pdfdetach + pdftotext) and
|
||||
// verifies that text living only in the embedded attachment is searchable.
|
||||
//
|
||||
// Requires ONLYOFFICE_ES_URL plus poppler (pdfdetach/pdftotext). No OnlyOffice
|
||||
// credentials are needed: a fixture FileStore serves the PDF bytes.
|
||||
func TestIntegrationESTextIndexPDFAttachment(t *testing.T) {
|
||||
esURL := strings.TrimSpace(os.Getenv("ONLYOFFICE_ES_URL"))
|
||||
if esURL == "" {
|
||||
t.Skip("ONLYOFFICE_ES_URL not set — skipping Elasticsearch integration test")
|
||||
}
|
||||
if docpipe.LookPath().PDFDetach == "" {
|
||||
t.Skip("pdfdetach not on PATH — skipping PDF attachment integration test")
|
||||
}
|
||||
pdf, err := os.ReadFile("testdata/pdf-with-attachment.pdf")
|
||||
if err != nil {
|
||||
t.Fatalf("read fixture: %v", err)
|
||||
}
|
||||
|
||||
stamp := time.Now().UTC().Format("20060102150405")
|
||||
idx, err := NewESTextIndex(ESTextConfig{URL: esURL, Index: "oo_docs_text_it_att_" + stamp})
|
||||
if err != nil {
|
||||
t.Fatalf("NewESTextIndex: %v", err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
t.Cleanup(func() {
|
||||
cleanupCtx, done := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer done()
|
||||
_, _, _ = idx.do(cleanupCtx, http.MethodDelete, "/"+idx.Index(), nil, "")
|
||||
})
|
||||
if err := idx.Ensure(ctx); err != nil {
|
||||
t.Fatalf("Ensure: %v", err)
|
||||
}
|
||||
|
||||
store := &textFakeStore{files: map[string][]byte{"9001": pdf}}
|
||||
ix := NewTextIndexer(store, idx)
|
||||
res, err := ix.IndexEntries(ctx, []Entry{{ID: "9001", Title: "scan.pdf", ParentID: "777", Kind: File}}, IndexOptions{MinChars: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("IndexEntries: %v", err)
|
||||
}
|
||||
if res.Indexed != 1 || res.Failed != 0 {
|
||||
t.Fatalf("result = %+v, want one indexed doc", res)
|
||||
}
|
||||
|
||||
// Token appears only inside the embedded goo-note.txt attachment.
|
||||
hits, err := idx.Search(ctx, SearchQuery{Text: "gooattachmenttoken"})
|
||||
if err != nil {
|
||||
t.Fatalf("Search attachment token: %v", err)
|
||||
}
|
||||
if len(hits) != 1 || hits[0].ID != "9001" {
|
||||
t.Fatalf("attachment-token hits = %+v, want doc 9001", hits)
|
||||
}
|
||||
if !strings.Contains(hits[0].Highlight, "gooattachmenttoken") {
|
||||
t.Errorf("highlight = %q, want attachment token", hits[0].Highlight)
|
||||
}
|
||||
// Body text is indexed as before.
|
||||
if hits, err := idx.Search(ctx, SearchQuery{Text: "goobodytoken"}); err != nil {
|
||||
t.Fatalf("Search body token: %v", err)
|
||||
} else if len(hits) != 1 {
|
||||
t.Errorf("body-token hits = %d, want 1", len(hits))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user