WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
package opensearch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/types"
|
||||
)
|
||||
|
||||
// spySink records audit events for assertions.
|
||||
type spySink struct {
|
||||
mu sync.Mutex
|
||||
indexCreated []indexCreatedEvent
|
||||
reindex []reindexEvent
|
||||
}
|
||||
|
||||
type indexCreatedEvent struct {
|
||||
alias string
|
||||
dim int
|
||||
}
|
||||
type reindexEvent struct {
|
||||
src, dst string
|
||||
docs int64
|
||||
}
|
||||
|
||||
func (s *spySink) EmitIndexCreated(_ context.Context, alias string, dim int) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.indexCreated = append(s.indexCreated, indexCreatedEvent{alias, dim})
|
||||
}
|
||||
|
||||
func (s *spySink) EmitReindexExecuted(_ context.Context, src, dst string, docs int64) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.reindex = append(s.reindex, reindexEvent{src, dst, docs})
|
||||
}
|
||||
|
||||
// TestBuildInternalCfg_ReadsHNSWFields verifies the HNSW IndexConfig fields
|
||||
// flow into internalCfg, falling back to defaults when unset.
|
||||
func TestBuildInternalCfg_ReadsHNSWFields(t *testing.T) {
|
||||
t.Run("reads set fields", func(t *testing.T) {
|
||||
cfg, err := buildInternalCfg(&types.IndexConfig{
|
||||
HNSWM: 24,
|
||||
HNSWEFConstruction: 200,
|
||||
HNSWEFSearch: 128,
|
||||
KNNEngine: "faiss",
|
||||
NumberOfShards: 2,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.hnswM != 24 || cfg.hnswEFConstruction != 200 || cfg.efSearch != 128 || cfg.knnEngine != "faiss" {
|
||||
t.Errorf("HNSW not wired: %+v", cfg)
|
||||
}
|
||||
if cfg.shards != 2 {
|
||||
t.Errorf("shards: want 2, got %d", cfg.shards)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("defaults when unset", func(t *testing.T) {
|
||||
cfg, err := buildInternalCfg(&types.IndexConfig{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.hnswM != 16 || cfg.hnswEFConstruction != 100 || cfg.efSearch != 100 || cfg.knnEngine != "lucene" {
|
||||
t.Errorf("defaults not applied: %+v", cfg)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nil config is all defaults", func(t *testing.T) {
|
||||
cfg, err := buildInternalCfg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.knnEngine != "lucene" || cfg.hnswM != 16 {
|
||||
t.Errorf("nil defaults wrong: %+v", cfg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestBuildIndexMapping_ReflectsHNSWConfig verifies the end-to-end path
|
||||
// IndexConfig → buildInternalCfg → buildIndexMapping carries the operator's
|
||||
// HNSW values into the cluster mapping JSON (regression guard for the wire-
|
||||
// through, since defaults coincide with common values).
|
||||
func TestBuildIndexMapping_ReflectsHNSWConfig(t *testing.T) {
|
||||
cfg, err := buildInternalCfg(&types.IndexConfig{
|
||||
HNSWM: 24,
|
||||
HNSWEFConstruction: 200,
|
||||
HNSWEFSearch: 128,
|
||||
KNNEngine: "faiss",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, err := buildIndexMapping(cfg, 768)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s := string(body)
|
||||
for _, want := range []string{`"m":24`, `"ef_construction":200`, `"engine":"faiss"`, `"knn.algo_param.ef_search":128`} {
|
||||
if !strings.Contains(s, want) {
|
||||
t.Errorf("mapping JSON missing %q\n%s", want, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestWithAuditSink_SetsAndNilSafe verifies the functional option.
|
||||
func TestWithAuditSink_SetsAndNilSafe(t *testing.T) {
|
||||
spy := &spySink{}
|
||||
r := &Repository{}
|
||||
WithAuditSink(spy)(r)
|
||||
if r.sink != spy {
|
||||
t.Fatal("WithAuditSink did not set the sink")
|
||||
}
|
||||
// nil must not clobber an already-set sink
|
||||
WithAuditSink(nil)(r)
|
||||
if r.sink != spy {
|
||||
t.Fatal("WithAuditSink(nil) clobbered the sink")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAuditSink_NopByDefault verifies a Repository with no sink does not panic
|
||||
// when the audit accessor is used (nopSink fallback).
|
||||
func TestAuditSink_NopByDefault(t *testing.T) {
|
||||
var _ AuditSink = nopSink{} // compile-time assertion
|
||||
r := &Repository{} // sink left nil
|
||||
// Must not panic.
|
||||
r.auditSink().EmitIndexCreated(context.Background(), "weknora_768", 768)
|
||||
r.auditSink().EmitReindexExecuted(context.Background(), "a", "b", 3)
|
||||
}
|
||||
|
||||
// TestAuditSink_EmitIndexCreated_OnEnsureReady verifies createIndexAndAlias
|
||||
// emits exactly one index-created event with the per-dim alias when a new
|
||||
// index is provisioned.
|
||||
func TestAuditSink_EmitIndexCreated_OnEnsureReady(t *testing.T) {
|
||||
repo, ts := newTestRepo(t, (&indexLifecycleHandler{}).ServeHTTP)
|
||||
defer ts.Close()
|
||||
spy := &spySink{}
|
||||
repo.sink = spy
|
||||
|
||||
if err := repo.ensureReady(context.Background(), 768); err != nil {
|
||||
t.Fatalf("ensureReady: %v", err)
|
||||
}
|
||||
if len(spy.indexCreated) != 1 {
|
||||
t.Fatalf("want 1 index_created event, got %d", len(spy.indexCreated))
|
||||
}
|
||||
got := spy.indexCreated[0]
|
||||
if got.alias != "weknora_test_768" || got.dim != 768 {
|
||||
t.Errorf("event mismatch: %+v", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user