using System;using System.IO;using System.Linq;using System.Threading.Tasks;using Agent.Core.Memory;using Agent.Core.Ollama;using Xunit;namespace Agent.Core.DesignTests;public class MemorySystemTests{ private class MockOllamaClient : OllamaClient { public Func<string, float[]>? OnGetEmbedding { get; set; } public MockOllamaClient() : base(new Logging.Logger(_ => { })) { } public override Task<float[]> GetEmbeddingAsync(string prompt, string? modelOverride = null) { if (OnGetEmbedding != null) { return Task.FromResult(OnGetEmbedding(prompt)); } return Task.FromResult(new float[] { 0.1f, 0.2f, 0.3f }); } } [Fact] public void CosineSimilarity_calculates_correct_value() { float[] vecA = { 1.0f, 0.0f, 0.0f }; float[] vecB = { 1.0f, 0.0f, 0.0f }; float[] vecC = { 0.0f, 1.0f, 0.0f }; Assert.Equal(1.0f, MemoryManager.CosineSimilarity(vecA, vecB), 5); Assert.Equal(0.0f, MemoryManager.CosineSimilarity(vecA, vecC), 5); } [Fact] public async Task MemoryManager_stores_and_loads_hierarchical_json() { string tempFile = Path.Combine(Path.GetTempPath(), "memory_test_" + Path.GetRandomFileName() + ".json"); var client = new MockOllamaClient(); // Setup deterministic mock embeddings based on text content to verify similarity client.OnGetEmbedding = text => { if (text.Contains("Alice")) return new float[] { 1.0f, 0.0f }; if (text.Contains("Bob")) return new float[] { 0.0f, 1.0f }; return new float[] { 0.5f, 0.5f }; }; try { var manager = new MemoryManager(tempFile, client); // 1. Store dynamic branches and leaves await manager.StoreAsync("user/name", "Alice"); await manager.StoreAsync("user/settings/theme", "dark"); await manager.StoreAsync("system/version", "6.0"); // Verify file was written Assert.True(File.Exists(tempFile)); string jsonContent = File.ReadAllText(tempFile); // Should contain hierarchy Assert.Contains("\"user\"", jsonContent); Assert.Contains("\"settings\"", jsonContent); Assert.Contains("\"theme\"", jsonContent); Assert.Contains("\"value\": \"Alice\"", jsonContent); // 2. Load it back into a new manager var manager2 = new MemoryManager(tempFile, client); Assert.Equal(3, manager2.Leaves.Count); // Verify paths and values are preserved Assert.Equal("Alice", manager2.QueryPath("user/name")); Assert.Equal("dark", manager2.QueryPath("user/settings/theme")); Assert.Equal("6.0", manager2.QueryPath("system/version")); // 3. Test retrieve semantic search // Query closest to Alice var matchesAlice = await manager2.RetrieveAsync("Who is Alice?", topN: 1); Assert.Single(matchesAlice); Assert.Equal("user/name", matchesAlice[0].Path); Assert.Equal("Alice", matchesAlice[0].Value); } finally { if (File.Exists(tempFile)) File.Delete(tempFile); } }}
Documentation
Version 3 design tests
Automated checks for chat, history, and Kokoro TTS (sanitizer + optional live speech).
Quick run
python src/Version3/DesignTests/run-design-tests.py
python src/Version3/DesignTests/run-design-tests.py --live
.\src\Version3\DesignTests\run-design-tests.ps1 -Live
What runs
| Suite | Needs network | Covers | |-------|----------------|--------| | ChatHistoryStoreTests | No | Same as V1 history XML | | OllamaClientHistoryTests | No | Client save/load | | KokoroTtsSanitizerTests | No | Markdown/emoji/URL stripping for speech | | OllamaLiveTests | Yes (--live) | Ollama chat | | KokoroTtsLiveTests | Yes (--live) | Kokoro model load + SpeakAsync |
First Kokoro live run may download ~320MB of model weights.
Console video (testers)
Record Version 3 console sessions (chat + TTS + history):
python src/TestManager/test_manager.py record v2 chat-tts --build
See src/TestManager/README.md.
All versions
python src/run-all-design-tests.py
python src/run-all-design-tests.py --live