Keep only the simple AI-powered approach: - set_resource_url tool lets AI save discovered URLs - Users ask AI directly: 'Find URLs for my containers' - AI uses its intelligence to discover and set URLs Removed: - URLDiscoveryService (rigid port scanning) - Bulk discovery API endpoints - Frontend discovery button The AI itself is smart enough to iterate through resources and discover URLs when asked.
155 lines
3.8 KiB
Go
155 lines
3.8 KiB
Go
package knowledge
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestFormatAllForContext_LimitsOutput(t *testing.T) {
|
|
// Create a temp directory for the test
|
|
tmpDir, err := os.MkdirTemp("", "knowledge-test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
// Create store without encryption for simplicity
|
|
store, err := NewStore(tmpDir)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create store: %v", err)
|
|
}
|
|
|
|
// Add more than maxGuests (10) guests with notes
|
|
for i := 0; i < 15; i++ {
|
|
guestID := filepath.Base(tmpDir) + "-guest-" + string(rune('A'+i))
|
|
err := store.SaveNote(guestID, "Guest-"+string(rune('A'+i)), "vm", "service", "Web Server", "http://example.com:8080")
|
|
if err != nil {
|
|
t.Fatalf("Failed to save note: %v", err)
|
|
}
|
|
// Small delay to ensure different UpdatedAt times
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
|
|
// Get the formatted context
|
|
result := store.FormatAllForContext()
|
|
|
|
if result == "" {
|
|
t.Fatal("Expected non-empty result")
|
|
}
|
|
|
|
// Should mention it's truncated (15 guests but only 10 included)
|
|
if !contains(result, "/") {
|
|
t.Error("Expected truncation indicator (e.g., '10/15 notes') in output")
|
|
}
|
|
|
|
// Count how many guest sections we have (look for "### Guest-")
|
|
guestCount := countOccurrences(result, "### Guest-")
|
|
if guestCount > 10 {
|
|
t.Errorf("Expected at most 10 guests, got %d", guestCount)
|
|
}
|
|
|
|
// Should be under 8KB
|
|
if len(result) > 8500 {
|
|
t.Errorf("Result too large: %d bytes (expected < 8500)", len(result))
|
|
}
|
|
}
|
|
|
|
func TestFormatAllForContext_PrioritizesRecent(t *testing.T) {
|
|
tmpDir, err := os.MkdirTemp("", "knowledge-test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
store, err := NewStore(tmpDir)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create store: %v", err)
|
|
}
|
|
|
|
// Create an old guest first
|
|
err = store.SaveNote("old-guest", "OldGuest", "vm", "config", "Setting", "old value")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
// Create a newer guest
|
|
err = store.SaveNote("new-guest", "NewGuest", "vm", "config", "Setting", "new value")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
result := store.FormatAllForContext()
|
|
|
|
// NewGuest should appear before OldGuest (more recently updated)
|
|
newIdx := indexOf(result, "NewGuest")
|
|
oldIdx := indexOf(result, "OldGuest")
|
|
|
|
if newIdx == -1 || oldIdx == -1 {
|
|
t.Fatalf("Both guests should be in result. newIdx=%d, oldIdx=%d", newIdx, oldIdx)
|
|
}
|
|
|
|
if newIdx > oldIdx {
|
|
t.Error("Expected NewGuest to appear before OldGuest (more recent)")
|
|
}
|
|
}
|
|
|
|
func TestFormatAllForContext_ByteLimit(t *testing.T) {
|
|
tmpDir, err := os.MkdirTemp("", "knowledge-test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
store, err := NewStore(tmpDir)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create store: %v", err)
|
|
}
|
|
|
|
// Create notes with very large content to trigger byte limit
|
|
largeContent := make([]byte, 2000)
|
|
for i := range largeContent {
|
|
largeContent[i] = 'x'
|
|
}
|
|
|
|
for i := 0; i < 10; i++ {
|
|
guestID := "large-guest-" + string(rune('A'+i))
|
|
err := store.SaveNote(guestID, "LargeGuest-"+string(rune('A'+i)), "vm", "learning", "Big Note", string(largeContent))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
result := store.FormatAllForContext()
|
|
|
|
// Should be capped at ~8KB (with some tolerance for headers)
|
|
if len(result) > 9000 {
|
|
t.Errorf("Result should be capped at ~8KB, got %d bytes", len(result))
|
|
}
|
|
}
|
|
|
|
// Helper functions
|
|
func contains(s, substr string) bool {
|
|
return indexOf(s, substr) != -1
|
|
}
|
|
|
|
func indexOf(s, substr string) int {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func countOccurrences(s, substr string) int {
|
|
count := 0
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|