Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ public abstract class MonolithMeshTestBase : Fixture.TestBase
/// (e.g., "TestData/mynode") and they'll have proper mesh node hubs.
/// Registered as a Markdown node so the hub gets AddMeshDataSource + WithNodeOperationHandlers.
/// </summary>
public const string TestPartition = "TestData";
/// <summary>Per-class overridable so concurrently-running classes do not contend on one
/// partition hub. Default is unchanged ("TestData"): only a project that opts in — by
/// overriding in its own base — gets isolation, so no existing test moves. See #1040.</summary>
public virtual string TestPartition => "TestData";

/// <summary>
/// Quiescing budget for test-mesh hubs.
Expand Down
6 changes: 6 additions & 0 deletions test/MeshWeaver.AI.Test/AITestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ namespace MeshWeaver.AI.Test;
/// </summary>
public abstract class AITestBase(ITestOutputHelper output) : MonolithMeshTestBase(output)
{

/// <summary>Per-class partition, so concurrently-running AI.Test classes do not contend on one
/// partition hub — the root cause behind the parallel failures in #1040 (a partition has ONE
/// owning hub that serialises every write routed through it). Overriding here covers all
/// AI.Test classes in one place; other projects keep the "TestData" default and are unaffected.</summary>
Comment on lines +14 to +17
public override string TestPartition => $"TestData{GetType().Name}";
/// <inheritdoc />
protected override MeshBuilder ConfigureMesh(MeshBuilder builder)
=> base.ConfigureMesh(builder).AddAI();
Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/AutoRouterDispatchTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,12 @@ await NodeFactory.CreateNode(new MeshNode(id, ProviderPath)
private async Task<string> SeedThread()
{
var threadId = Guid.NewGuid().AsString();
var threadPath = $"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
var threadPath = $"{TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
await NodeFactory.CreateNode(MeshNode.FromPath(threadPath) with
{
Name = $"Auto Dispatch Thread {threadId}",
NodeType = ThreadNodeType.NodeType,
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread { CreatedBy = TestUser }
}).Should().Within(30.Seconds()).Emit();
return threadPath;
Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/CancellationAckTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ public async Task Stop_ImmediatelyStampsCancellationAck_BeforeRoundTearsDown()
{
var ct = TestContext.Current.CancellationToken;
var threadId = Guid.NewGuid().AsString();
var threadPath = $"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
var threadPath = $"{TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
await NodeFactory.CreateNode(MeshNode.FromPath(threadPath) with
{
Name = $"Cancellation Ack Test Thread {threadId}",
NodeType = ThreadNodeType.NodeType,
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread { CreatedBy = "rbuergi@systemorph.com" }
}).Should().Emit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ await MeshService.CreateNode(new MeshNode(defaultModelId, providerPath)
// Seed the user's MASTER composer pinned to a GONE model (never created in the catalog) — the
// exact stuck-selection the maintainer reported.
var user = Mesh.ServiceProvider.GetRequiredService<AccessService>().Context?.ObjectId
?? MonolithMeshTestBase.TestPartition;
?? TestPartition;
var masterPath = ThreadComposerNodeType.PathFor(user);
var goneModel = $"gone-model-{suffix}";
await MeshService.CreateNode(MeshNode.FromPath(masterPath) with
Expand Down
6 changes: 3 additions & 3 deletions test/MeshWeaver.AI.Test/ChatCommandSubmissionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public async Task StartThread_WhitespaceOnly_CreatesThread_ButRunsNoRound()
var client = GetClient();

client.StartThread(
MonolithMeshTestBase.TestPartition,
TestPartition,
" ",
agentName: "Assistant",
createdBy: "rbuergi@systemorph.com",
Expand Down Expand Up @@ -180,12 +180,12 @@ await Mesh.GetWorkspace().GetMeshNodeStream(threadPath)
private async Task<string> SeedEmptyThread()
{
var threadId = Guid.NewGuid().AsString();
var threadPath = $"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
var threadPath = $"{TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
await NodeFactory.CreateNode(MeshNode.FromPath(threadPath) with
{
Name = $"Test Thread {threadId}",
NodeType = ThreadNodeType.NodeType,
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread { CreatedBy = "rbuergi@systemorph.com" }
}).Should().Emit();
return threadPath;
Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/CrossHubPatchAtomicityTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public async Task ConcurrentCrossHubPatches_DoNotDropAQueuedMessage()
for (var round = 0; round < rounds; round++)
{
var nodeId = Guid.NewGuid().AsString();
var path = $"{MonolithMeshTestBase.TestPartition}/CrossHubAtomicity/{nodeId}";
var path = $"{TestPartition}/CrossHubAtomicity/{nodeId}";

// Fresh node per round, empty merge-safe dict. NodeType is a plain "Markdown"
// node (NOT Thread) so NO submission watcher runs — this isolates the OWNER
Expand All @@ -93,7 +93,7 @@ await NodeFactory.CreateNode(MeshNode.FromPath(path) with
{
Name = $"Atomicity Node {nodeId}",
NodeType = "Markdown",
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread { CreatedBy = "rbuergi@systemorph.com" }
}).FirstAsync().ToTask(ct);

Expand Down
2 changes: 1 addition & 1 deletion test/MeshWeaver.AI.Test/DocumentNodeGetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace MeshWeaver.AI.Test;
public class DocumentNodeGetTest(ITestOutputHelper output) : MonolithMeshTestBase(output)
{
/// <summary>The collection segment is deliberately the UCR keyword <c>content</c>.</summary>
private static readonly string Collection = TestPartition + "/content";
private string Collection => TestPartition + "/content";

/// <summary>Registers the Document NodeType + sink on the test mesh.</summary>
protected override MeshBuilder ConfigureMesh(MeshBuilder builder)
Expand Down
18 changes: 9 additions & 9 deletions test/MeshWeaver.AI.Test/HarnessInstallGateTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public async Task RequiresInstall_WithoutInstalledNode_ResolvesNull()
// The absent-node case emits only after the InstallProbeTimeout elapses — give the
// assertion strictly more than that so it never races the probe.
var resolved = await HarnessNodeType
.ResolveInstalledHarness(hub, $"{MonolithMeshTestBase.TestPartition}/Harness/{GatedId}")
.ResolveInstalledHarness(hub, $"{TestPartition}/Harness/{GatedId}")
.Should().Within(HarnessNodeType.InstallProbeTimeout + TimeSpan.FromSeconds(10)).Emit();
resolved.Should().BeNull(
"a RequiresInstall harness without its installed node must fall back — this is what " +
Expand All @@ -138,7 +138,7 @@ public async Task RequiresInstall_WithInstalledNode_ResolvesHarness()
var meshService = Mesh.ServiceProvider.GetRequiredService<IMeshService>();

// The node the Store plugin's install localizes into the viewer's partition.
var installed = new MeshNode(GatedId, $"{MonolithMeshTestBase.TestPartition}/Harness")
var installed = new MeshNode(GatedId, $"{TestPartition}/Harness")
{
NodeType = HarnessNodeType.NodeType,
Name = "Gated CLI",
Expand Down Expand Up @@ -182,9 +182,9 @@ public async Task Round_WithInstalledNode_RunsTheGatedHarness()
{
var client = GetClient();
var meshService = Mesh.ServiceProvider.GetRequiredService<IMeshService>();
var harnessPath = $"{MonolithMeshTestBase.TestPartition}/Harness/{GatedId}";
var harnessPath = $"{TestPartition}/Harness/{GatedId}";

await meshService.CreateNode(new MeshNode(GatedId, $"{MonolithMeshTestBase.TestPartition}/Harness")
await meshService.CreateNode(new MeshNode(GatedId, $"{TestPartition}/Harness")
{
NodeType = HarnessNodeType.NodeType,
Name = "Gated CLI",
Expand All @@ -194,12 +194,12 @@ await meshService.CreateNode(new MeshNode(GatedId, $"{MonolithMeshTestBase.TestP

var threadCreated = new System.Reactive.Subjects.AsyncSubject<MeshNode>();
client.StartThread(
namespacePath: MonolithMeshTestBase.TestPartition,
namespacePath: TestPartition,
userText: "run on the gated harness",
agentName: "Agent/Assistant",
modelName: "_Provider/Fake/fake-model",
harness: harnessPath,
contextPath: MonolithMeshTestBase.TestPartition,
contextPath: TestPartition,
createdBy: "rbuergi@systemorph.com",
onCreated: node => { threadCreated.OnNext(node); threadCreated.OnCompleted(); });

Expand All @@ -222,16 +222,16 @@ public async Task Round_WithoutInstalledNode_FallsBackToAgentPath()
{
var client = GetClient();
// A picked path whose node deliberately does not exist (uninstalled / never installed).
var harnessPath = $"{MonolithMeshTestBase.TestPartition}/Harness/never-installed-{GatedId}";
var harnessPath = $"{TestPartition}/Harness/never-installed-{GatedId}";

var threadCreated = new System.Reactive.Subjects.AsyncSubject<MeshNode>();
client.StartThread(
namespacePath: MonolithMeshTestBase.TestPartition,
namespacePath: TestPartition,
userText: "run without an install",
agentName: "Agent/Assistant",
modelName: "_Provider/Fake/fake-model",
harness: harnessPath,
contextPath: MonolithMeshTestBase.TestPartition,
contextPath: TestPartition,
createdBy: "rbuergi@systemorph.com",
onCreated: node => { threadCreated.OnNext(node); threadCreated.OnCompleted(); });

Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/InboxToolIntegrationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -697,15 +697,15 @@ public void ExtractPendingTexts_NullThread_ReturnsEmpty()
private async Task<string> SeedEmptyThreadAsync(CancellationToken ct)
{
var threadId = Guid.NewGuid().AsString();
var threadPath = $"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
var threadPath = $"{TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
// FromPath splits the namespace ("TestData/_Thread") from the id — `new MeshNode(path)`
// would bake the slashes into the Id with an EMPTY namespace, which the
// PartitionWriteGuard (correctly) rejects as a malformed top-level node.
await NodeFactory.CreateNode(MeshNode.FromPath(threadPath) with
{
Name = $"Inbox Test Thread {threadId}",
NodeType = ThreadNodeType.NodeType,
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread { CreatedBy = "rbuergi@systemorph.com" }
});
return threadPath;
Expand Down
2 changes: 1 addition & 1 deletion test/MeshWeaver.AI.Test/McpCompileInFlightTruthTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public override async ValueTask DisposeAsync()
}

private const string TypeName = "InFlightTruthType";
private static readonly string RunningActivityPath =
private string RunningActivityPath =>
TestPartition + "/" + TypeName + "/_Activity/compile-inflight-under-test";

[Fact(Timeout = 120000)]
Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/McpFailureSurfacingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ private Task<string> UpdateAsync(string json) =>
private Task<string> PatchAsync(string path, string fields) =>
new MeshOperations(Mesh).Patch(path, fields).FirstAsync().Timeout(TimeSpan.FromSeconds(45)).ToTask(Ct);

private MeshNode ValidMarkdown(string id, string ns = TestPartition) =>
new(id, ns) { Name = id, NodeType = "Markdown", Content = new MarkdownContent { Content = $"# {id}" } };
private MeshNode ValidMarkdown(string id, string? ns = null) =>
new(id, ns ?? TestPartition) { Name = id, NodeType = "Markdown", Content = new MarkdownContent { Content = $"# {id}" } };

[Fact(Timeout = 60000)]
public async Task Create_InvalidJson_ReturnsError()
Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/McpNegativeOperationsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public class McpNegativeOperationsTest(ITestOutputHelper output) : MonolithMeshT
private Task<string> Run(IObservable<string> op) =>
op.FirstAsync().Timeout(TimeSpan.FromSeconds(45)).ToTask(Ct);

private MeshNode ValidMarkdown(string id, string ns = TestPartition) =>
new(id, ns) { Name = id, NodeType = "Markdown", Content = new MarkdownContent { Content = $"# {id}" } };
private MeshNode ValidMarkdown(string id, string? ns = null) =>
new(id, ns ?? TestPartition) { Name = id, NodeType = "Markdown", Content = new MarkdownContent { Content = $"# {id}" } };

private static string Unique(string prefix) => prefix + Guid.NewGuid().ToString("N")[..8];

Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/ModelSubstitutionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,12 @@ await NodeFactory.CreateNode(new MeshNode(id, providerPath)
private async Task<string> SeedThread()
{
var threadId = Guid.NewGuid().AsString();
var threadPath = $"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
var threadPath = $"{TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
await NodeFactory.CreateNode(MeshNode.FromPath(threadPath) with
{
Name = $"Model Substitution Thread {threadId}",
NodeType = ThreadNodeType.NodeType,
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread { CreatedBy = TestUser }
}).Should().Within(30.Seconds()).Emit();
return threadPath;
Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/ProviderRefusalRoundTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,12 @@ await NodeFactory.CreateNode(new MeshNode(id, providerPath)
private async Task<string> SeedThread()
{
var threadId = Guid.NewGuid().AsString();
var threadPath = $"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
var threadPath = $"{TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
await NodeFactory.CreateNode(MeshNode.FromPath(threadPath) with
{
Name = $"Provider Refusal Thread {threadId}",
NodeType = ThreadNodeType.NodeType,
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread { CreatedBy = TestUser }
}).Should().Within(TimeSpan.FromMilliseconds(SeedMs)).Emit();
return threadPath;
Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/RoundFaultTerminalStateTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ protected override MessageHubConfiguration ConfigureClient(MessageHubConfigurati
public async Task StreamThrows_AfterExecutingFlip_ThreadReachesTerminalState_NoWatchdog()
{
var threadId = Guid.NewGuid().AsString();
var threadPath = $"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
var threadPath = $"{TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
await NodeFactory.CreateNode(MeshNode.FromPath(threadPath) with
{
Name = $"Fault Test Thread {threadId}",
NodeType = ThreadNodeType.NodeType,
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread { CreatedBy = "rbuergi@systemorph.com" }
}).Should().Emit();

Expand Down
4 changes: 2 additions & 2 deletions test/MeshWeaver.AI.Test/StreamingTimeoutTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ protected override MessageHubConfiguration ConfigureClient(MessageHubConfigurati
public async Task StreamHangsForever_RoundEndsWithTimeoutError_NotSilentWedge()
{
var threadId = Guid.NewGuid().AsString();
var threadPath = $"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
var threadPath = $"{TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
await NodeFactory.CreateNode(MeshNode.FromPath(threadPath) with
{
Name = $"Streaming Timeout Test Thread {threadId}",
NodeType = ThreadNodeType.NodeType,
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread { CreatedBy = "rbuergi@systemorph.com" }
}).Should().Emit();

Expand Down
16 changes: 8 additions & 8 deletions test/MeshWeaver.AI.Test/ThreadComposerFlowTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ public async Task StartThread_WithComposer_CreatesUnderContext_AndEmbedsEmptiedC
Harness = $"{HarnessNodeType.RootNamespace}/{Harnesses.MeshWeaver}",
AgentName = "Agent/Assistant",
ModelName = "_Provider/Fake/fake-model",
ContextPath = MonolithMeshTestBase.TestPartition,
Attachments = ImmutableList.Create("TestData/SomeDoc"),
ContextPath = TestPartition,
Attachments = ImmutableList.Create($"{TestPartition}/SomeDoc"),
OpenThreadPath = "stale/_Thread/previous" // must never carry onto the thread
};

client.StartThread(
namespacePath: MonolithMeshTestBase.TestPartition,
namespacePath: TestPartition,
userText: composer.MessageContent!,
agentName: composer.AgentName,
modelName: composer.ModelName,
Expand All @@ -92,7 +92,7 @@ public async Task StartThread_WithComposer_CreatesUnderContext_AndEmbedsEmptiedC

var created = await threadCreated.Should().Emit();
created!.Path.Should().StartWith(
$"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/",
$"{TestPartition}/{ThreadNodeType.ThreadPartition}/",
"the thread must live under {context}/_Thread/{speakingId}");

var thread = (created.Content as MeshThread)!;
Expand Down Expand Up @@ -123,7 +123,7 @@ public async Task SubmitComposer_DrainsDraft_CarriesSelection_AndEmptiesComposer
Harness = $"{HarnessNodeType.RootNamespace}/{Harnesses.MeshWeaver}",
AgentName = "Agent/Assistant",
ModelName = "_Provider/Fake/fake-model",
Attachments = ImmutableList.Create("TestData/Chip")
Attachments = ImmutableList.Create($"{TestPartition}/Chip")
});
var client = GetClient();

Expand Down Expand Up @@ -151,7 +151,7 @@ public async Task SubmitComposer_DrainsDraft_CarriesSelection_AndEmptiesComposer
userMessage.Text.Should().Be("typed into the composer");
userMessage.AgentName.Should().Be("Agent/Assistant");
userMessage.Harness.Should().Be($"{HarnessNodeType.RootNamespace}/{Harnesses.MeshWeaver}");
userMessage.Attachments.Should().Equal("TestData/Chip");
userMessage.Attachments.Should().Equal($"{TestPartition}/Chip");
}

[Fact]
Expand Down Expand Up @@ -238,12 +238,12 @@ public void ThreadComposer_Equality_ComparesAttachmentsBySequence()
private async Task<string> SeedThreadWithComposer(ThreadComposer composer)
{
var threadId = Guid.NewGuid().AsString();
var threadPath = $"{MonolithMeshTestBase.TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
var threadPath = $"{TestPartition}/{ThreadNodeType.ThreadPartition}/{threadId}";
await NodeFactory.CreateNode(MeshNode.FromPath(threadPath) with
{
Name = $"Composer Test Thread {threadId}",
NodeType = ThreadNodeType.NodeType,
MainNode = MonolithMeshTestBase.TestPartition,
MainNode = TestPartition,
Content = new MeshThread
{
CreatedBy = "rbuergi@systemorph.com",
Expand Down
Loading
Loading