Its own binary for Windows, macOS and Linux, carrying no server code. It reads the files there, cuts them, has the gateway embed them with the person's key and sends text and vectors for what changed only: a SQLite ledger on the machine remembers each document's file, size, time, digest and what happened, so a rerun reads nothing unchanged, a file skipped stays skipped until --retry, and --delete removes only what this machine sent under the paths given. --dry-run says what would leave the machine. Commands: login, collections, sync, status, history, formats. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
69 lines
2.3 KiB
Go
69 lines
2.3 KiB
Go
package router
|
||
|
||
import (
|
||
"context"
|
||
"net/http/httptest"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
|
||
"gitea.mynodes.xyz/ilker/llmgw/internal/kb/kbsync"
|
||
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
|
||
)
|
||
|
||
// The whole way: llmgw-kb's sync against this gateway, embeddings served by
|
||
// an engine through the ordinary path, chunks in PostgreSQL, found again by
|
||
// a search.
|
||
func TestASyncFromAMachineIsSearchable(t *testing.T) {
|
||
rt := kbRouter(t)
|
||
ctx := t.Context()
|
||
s := rt.store
|
||
sfx := strings.ToLower(store.NewID(""))
|
||
org, _ := s.CreateOrg(ctx, "e2e-"+sfx)
|
||
team, _ := s.CreateTeam(ctx, org, "t")
|
||
proj, _ := s.CreateProject(ctx, team, "p")
|
||
coll, err := s.CreateCollection(ctx, store.KBCollection{OwnerScope: store.ScopeProject, OwnerID: proj,
|
||
Name: "belgeler", EmbedModel: "qwen2.5-14b-instruct", Dims: 3})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
srv := httptest.NewServer(rt.Handler())
|
||
t.Cleanup(srv.Close)
|
||
h, _ := kbsync.NewHTTP("")
|
||
c := &kbsync.Client{Base: srv.URL, Key: kbKey(t, s, proj, ""), HTTP: h}
|
||
|
||
dir := t.TempDir()
|
||
for name, body := range map[string]string{
|
||
"izin.md": "# Yıllık izin\n\nÇalışanlar yılda yirmi gün ücretli izin kullanır.\n",
|
||
"masraf.md": "# Masraflar\n\nMasraflar finans ekibine iletilir.\n",
|
||
} {
|
||
if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
l, err := kbsync.OpenLedger(ctx, filepath.Join(t.TempDir(), "ledger.db"))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
defer l.Close()
|
||
rep, err := kbsync.Sync(ctx, c, l, kbsync.Options{Collection: "belgeler", Roots: []string{dir}}, nil)
|
||
if err != nil || rep.Sent != 2 {
|
||
t.Fatalf("sync: %+v %v", rep, err)
|
||
}
|
||
docs, _ := s.Documents(ctx, coll.ID)
|
||
if len(docs) != 2 {
|
||
t.Fatalf("documents %+v", docs)
|
||
}
|
||
hits, err := s.SearchChunks(context.Background(), store.KBSearch{OrgID: org, Collections: []store.KBCollection{coll},
|
||
Text: "kaç gün izin", K: 1})
|
||
if err != nil || len(hits) != 1 || hits[0].Document != "izin.md" || !strings.Contains(hits[0].Body, "Yıllık izin\n\n") {
|
||
t.Errorf("search: %+v %v", hits, err)
|
||
}
|
||
// Again: nothing sent.
|
||
rep, err = kbsync.Sync(ctx, c, l, kbsync.Options{Collection: "belgeler", Roots: []string{dir}}, nil)
|
||
if err != nil || rep.Sent != 0 || rep.Unchanged != 2 {
|
||
t.Errorf("second sync: %+v %v", rep, err)
|
||
}
|
||
}
|