backend: serve single-agent task templates with their status and results, rename the kinds in storage
This commit is contained in:
1 parent
e77880d372
commit
3a9ea46d6f
45 files changed
+763
-216
No files matched your search
@@ -48,8 +48,8 @@ public sealed class StorageTests : IDisposable
|
||||
public async Task Raw_AppendReadAndDedupe()
|
||||
{
|
||||
var raw = new SqliteRawStore(_db);
|
||||
RawChunkAppend a = Chunk(s_agent, "udp_echo", 1_000, "2026-01-01");
|
||||
RawChunkAppend b = Chunk(s_agent, "udp_echo", 11_000, "2026-01-01");
|
||||
RawChunkAppend a = Chunk(s_agent, "quic_ping", 1_000, "2026-01-01");
|
||||
RawChunkAppend b = Chunk(s_agent, "quic_ping", 11_000, "2026-01-01");
|
||||
|
||||
Assert.True(await raw.AppendAsync(a, default));
|
||||
Assert.True(await raw.AppendAsync(b, default));
|
||||
@@ -57,7 +57,7 @@ public sealed class StorageTests : IDisposable
|
||||
|
||||
IReadOnlyList<RawChunkMeta> metas = await raw.FindAsync(new RawQuery(s_agent), default);
|
||||
Assert.Equal(2, metas.Count);
|
||||
Assert.Equal("2026-01-01/agt_storage/udp_echo.seg", metas[0].SegmentPath);
|
||||
Assert.Equal("2026-01-01/agt_storage/quic_ping.seg", metas[0].SegmentPath);
|
||||
Assert.Equal(0, metas[0].Offset);
|
||||
Assert.Equal(metas[0].Length, metas[1].Offset); // 同一个段文件里依次追加
|
||||
Assert.Equal(a.Bytes.ToArray(), await raw.ReadAsync(metas[0], default));
|
||||
@@ -72,7 +72,7 @@ public sealed class StorageTests : IDisposable
|
||||
var raw = new SqliteRawStore(_db);
|
||||
var evil = new NodeId("../../../outside");
|
||||
Assert.True(await raw.AppendAsync(Chunk(evil, "../etc", 1, "2026-01-01"), default));
|
||||
Assert.True(await raw.AppendAsync(Chunk(s_agent, "udp_echo", 2, "2026-01-01", test: new TestId("..\\..\\x"), phase: 0), default));
|
||||
Assert.True(await raw.AppendAsync(Chunk(s_agent, "quic_ping", 2, "2026-01-01", test: new TestId("..\\..\\x"), phase: 0), default));
|
||||
|
||||
foreach (string file in Directory.EnumerateFiles(_dir, "*.seg", SearchOption.AllDirectories))
|
||||
{
|
||||
@@ -80,7 +80,7 @@ public sealed class StorageTests : IDisposable
|
||||
}
|
||||
|
||||
Assert.False(File.Exists(Path.Combine(_dir, "outside")));
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await raw.AppendAsync(Chunk(s_agent, "udp_echo", 3, "../2026"), default));
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await raw.AppendAsync(Chunk(s_agent, "quic_ping", 3, "../2026"), default));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -90,19 +90,19 @@ public sealed class StorageTests : IDisposable
|
||||
var testId = new TestId("tst_one");
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
await raw.AppendAsync(Chunk(s_agent, "udp_echo", i * 10_000L, "2026-01-01"), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "quic_ping", i * 10_000L, "2026-01-01"), default);
|
||||
}
|
||||
|
||||
await raw.AppendAsync(Chunk(s_agent, "tcp_echo", 5_000, "2026-01-01"), default);
|
||||
await raw.AppendAsync(Chunk(new NodeId("agt_other"), "udp_echo", 5_000, "2026-01-01"), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "udp_train", 0, "2026-01-01", test: testId, phase: 1), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "tcp_ping", 5_000, "2026-01-01"), default);
|
||||
await raw.AppendAsync(Chunk(new NodeId("agt_other"), "quic_ping", 5_000, "2026-01-01"), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "quic_stream", 0, "2026-01-01", test: testId, phase: 1), default);
|
||||
|
||||
Assert.Equal(8, (await raw.FindAsync(new RawQuery(s_agent), default)).Count);
|
||||
Assert.Equal(6, (await raw.FindAsync(new RawQuery(s_agent, Kind: "udp_echo"), default)).Count);
|
||||
Assert.Equal(6, (await raw.FindAsync(new RawQuery(s_agent, Kind: "quic_ping"), default)).Count);
|
||||
Assert.Single(await raw.FindAsync(new RawQuery(TestId: testId), default));
|
||||
Assert.Equal(3, (await raw.FindAsync(new RawQuery(s_agent, Kind: "udp_echo", FromMs: 10_000, ToMs: 40_000), default)).Count);
|
||||
Assert.Equal(2, (await raw.FindAsync(new RawQuery(s_agent, Kind: "udp_echo", Limit: 2), default)).Count);
|
||||
Assert.Equal(4, (await raw.FindAsync(new RawQuery(s_agent, Kind: "udp_echo", AfterStartTs: 20_000), default)).Count);
|
||||
Assert.Equal(3, (await raw.FindAsync(new RawQuery(s_agent, Kind: "quic_ping", FromMs: 10_000, ToMs: 40_000), default)).Count);
|
||||
Assert.Equal(2, (await raw.FindAsync(new RawQuery(s_agent, Kind: "quic_ping", Limit: 2), default)).Count);
|
||||
Assert.Equal(4, (await raw.FindAsync(new RawQuery(s_agent, Kind: "quic_ping", AfterStartTs: 20_000), default)).Count);
|
||||
|
||||
IReadOnlyList<(NodeId AgentId, string Series)> series = await raw.ListSeriesAsync(0, 100_000, default);
|
||||
Assert.Equal(2, series.Count); // 测试的块不算持续序列
|
||||
@@ -112,9 +112,9 @@ public sealed class StorageTests : IDisposable
|
||||
public async Task Raw_RecoverTruncatesAnUnindexedTail_AndLaterAppendsStayConsistent()
|
||||
{
|
||||
var raw = new SqliteRawStore(_db);
|
||||
RawChunkAppend first = Chunk(s_agent, "udp_echo", 1, "2026-01-01");
|
||||
RawChunkAppend first = Chunk(s_agent, "quic_ping", 1, "2026-01-01");
|
||||
await raw.AppendAsync(first, default);
|
||||
string segment = Directory.EnumerateFiles(_dir, "udp_echo.seg", SearchOption.AllDirectories).Single();
|
||||
string segment = Directory.EnumerateFiles(_dir, "quic_ping.seg", SearchOption.AllDirectories).Single();
|
||||
long indexedEnd = new FileInfo(segment).Length;
|
||||
|
||||
// 追加之后、写索引之前崩溃:段文件尾部多了没有索引记录的字节
|
||||
@@ -124,7 +124,7 @@ public sealed class StorageTests : IDisposable
|
||||
await raw.RecoverAsync(default);
|
||||
Assert.Equal(indexedEnd, new FileInfo(segment).Length);
|
||||
|
||||
RawChunkAppend second = Chunk(s_agent, "udp_echo", 2, "2026-01-01");
|
||||
RawChunkAppend second = Chunk(s_agent, "quic_ping", 2, "2026-01-01");
|
||||
await raw.AppendAsync(second, default);
|
||||
IReadOnlyList<RawChunkMeta> metas = await raw.FindAsync(new RawQuery(s_agent), default);
|
||||
Assert.Equal(first.Bytes.ToArray(), await raw.ReadAsync(metas[0], default));
|
||||
@@ -137,10 +137,10 @@ public sealed class StorageTests : IDisposable
|
||||
{
|
||||
var raw = new SqliteRawStore(_db);
|
||||
var testId = new TestId("tst_old");
|
||||
await raw.AppendAsync(Chunk(s_agent, "udp_echo", 1, "2026-01-01"), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "udp_echo", 2, "2026-03-01"), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "udp_train", 3, "2026-01-01", test: testId, phase: 0), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "udp_train", 4, "2026-02-01", test: new TestId("tst_mid"), phase: 0), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "quic_ping", 1, "2026-01-01"), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "quic_ping", 2, "2026-03-01"), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "quic_stream", 3, "2026-01-01", test: testId, phase: 0), default);
|
||||
await raw.AppendAsync(Chunk(s_agent, "quic_stream", 4, "2026-02-01", test: new TestId("tst_mid"), phase: 0), default);
|
||||
|
||||
// 持续任务保留到 2026-02-01 之后,测试保留到 2026-01-15 之后
|
||||
int removed = await raw.DeleteExpiredAsync(new DateOnly(2026, 2, 1), new DateOnly(2026, 1, 15), default);
|
||||
@@ -365,6 +365,44 @@ public sealed class StorageTests : IDisposable
|
||||
Assert.Equal(200, (await settings.GetAllAsync(default)).Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Schema_UpgradesVersion1_ByRenamingTheKinds()
|
||||
{
|
||||
string dir = Path.Combine(Path.GetTempPath(), "moshiro-schema-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(dir);
|
||||
string path = Path.Combine(dir, "moshiro.db");
|
||||
try
|
||||
{
|
||||
using (var db = new SqliteDatabase(dir))
|
||||
{
|
||||
}
|
||||
|
||||
SqliteConnection.ClearAllPools();
|
||||
using (var connection = new SqliteConnection($"Data Source={path}"))
|
||||
{
|
||||
connection.Open();
|
||||
connection.Exec("INSERT INTO tasks (id, scope, kind, schedule, updated_at) VALUES ('tsk_a', 'line', 'udp_echo', 'continuous', 1), ('tsk_b', 'line', 'connect', 'continuous', 1)");
|
||||
connection.Exec("PRAGMA user_version = 1");
|
||||
}
|
||||
|
||||
SqliteConnection.ClearAllPools();
|
||||
using (var db = new SqliteDatabase(dir))
|
||||
{
|
||||
}
|
||||
|
||||
SqliteConnection.ClearAllPools();
|
||||
using var check = new SqliteConnection($"Data Source={path}");
|
||||
check.Open();
|
||||
Assert.Equal(2, check.Scalar<int>("PRAGMA user_version"));
|
||||
Assert.Equal("quic_ping,tcp_connect", string.Join(',', new[] { "tsk_a", "tsk_b" }.Select(id => check.Scalar<string>($"SELECT kind FROM tasks WHERE id = '{id}'"))));
|
||||
}
|
||||
finally
|
||||
{
|
||||
SqliteConnection.ClearAllPools();
|
||||
Directory.Delete(dir, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Schema_RefusesADatabaseFromANewerBuild()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user