mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 20:08:18 +08:00
test(goal): exercise managed JSON through real supervisor dispatch
This commit is contained in:
parent
6c0a60ce14
commit
716c765c3a
@ -32,13 +32,13 @@ import static org.mockito.Mockito.*;
|
|||||||
@TestPropertySource(properties = {
|
@TestPropertySource(properties = {
|
||||||
"spring.datasource.url=jdbc:h2:mem:json_http_${random.uuid};MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1",
|
"spring.datasource.url=jdbc:h2:mem:json_http_${random.uuid};MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1",
|
||||||
"spring.ai.dashscope.api-key=offline-fixture-no-provider",
|
"spring.ai.dashscope.api-key=offline-fixture-no-provider",
|
||||||
"mateclaw.goal.enabled=true", "mateclaw.plugin.enabled=false", "mateclaw.skill.workspace.auto-init=false",
|
"mateclaw.goal.enabled=true", "mateclaw.goal.supervisor-poll-ms=3600000", "mateclaw.plugin.enabled=false", "mateclaw.skill.workspace.auto-init=false",
|
||||||
"mateclaw.skill.workspace.root=${java.io.tmpdir}/mateclaw-json-http-skills-${random.uuid}"
|
"mateclaw.skill.workspace.root=${java.io.tmpdir}/mateclaw-json-http-skills-${random.uuid}"
|
||||||
})
|
})
|
||||||
class GoalJsonHttpRuntimeIntegrationTest {
|
class GoalJsonHttpRuntimeIntegrationTest {
|
||||||
@MockBean private MemoryManager memory;
|
@MockBean private MemoryManager memory;
|
||||||
@MockBean private GoalEvaluationService evaluator;
|
@MockBean private GoalEvaluationService evaluator;
|
||||||
@MockBean private GoalContinuationSupervisor supervisor;
|
@Autowired private GoalContinuationSupervisor supervisor;
|
||||||
@MockBean private ProviderChatModelFactory modelFactory;
|
@MockBean private ProviderChatModelFactory modelFactory;
|
||||||
@Autowired private JdbcTemplate jdbc;
|
@Autowired private JdbcTemplate jdbc;
|
||||||
@Autowired private vip.mate.config.LoginRateLimitFilter loginLimiter;
|
@Autowired private vip.mate.config.LoginRateLimitFilter loginLimiter;
|
||||||
@ -61,19 +61,27 @@ class GoalJsonHttpRuntimeIntegrationTest {
|
|||||||
org.springframework.test.util.ReflectionTestUtils.getField(loginLimiter, "attempts");
|
org.springframework.test.util.ReflectionTestUtils.getField(loginLimiter, "attempts");
|
||||||
assertNotNull(attempts);
|
assertNotNull(attempts);
|
||||||
attempts.invalidateAll();
|
attempts.invalidateAll();
|
||||||
|
// Independent journeys share a context; old retryable fixtures must not be redispatched.
|
||||||
|
jdbc.update("UPDATE mate_agent_goal SET auto_followup_enabled=FALSE");
|
||||||
|
var backoff = (java.util.concurrent.atomic.AtomicReference<?>)
|
||||||
|
org.springframework.test.util.ReflectionTestUtils.getField(supervisor, "providerBackoffUntil");
|
||||||
|
assertNotNull(backoff); backoff.set(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.junit.jupiter.params.ParameterizedTest
|
@org.junit.jupiter.params.ParameterizedTest
|
||||||
@org.junit.jupiter.params.provider.CsvSource({"false,sync,true", "true,sync,true", "false,stream,true", "true,stream,true",
|
@org.junit.jupiter.params.provider.CsvSource({"false,sync,true", "true,sync,true", "false,stream,true", "true,stream,true",
|
||||||
"false,scheduled,true", "true,scheduled,true", "false,recovered,true", "true,recovered,true",
|
"false,scheduled,true", "true,scheduled,true", "false,recovered,true", "true,recovered,true",
|
||||||
"false,scheduled,false", "true,scheduled,false", "false,recovered,false", "true,recovered,false",
|
"false,scheduled,false", "true,scheduled,false", "false,recovered,false", "true,recovered,false",
|
||||||
"false,queued,true", "false,reuse,true", "true,reuse,true", "false,recheck,true", "true,recheck,true"})
|
"false,queued,true", "false,reuse,true", "true,reuse,true", "false,recheck,true", "true,recheck,true",
|
||||||
|
"false,supervised,true", "true,supervised,true", "false,supervised-recovered,true", "true,supervised-recovered,true",
|
||||||
|
"false,supervised,false", "true,supervised,false", "false,supervised-recovered,false", "true,supervised-recovered,false"})
|
||||||
void authenticatedGoalCompletesThroughHttpOrScheduledProductionRuntime(boolean plan, String entry, boolean accepted) throws Exception {
|
void authenticatedGoalCompletesThroughHttpOrScheduledProductionRuntime(boolean plan, String entry, boolean accepted) throws Exception {
|
||||||
boolean scheduled = entry.equals("scheduled") || entry.equals("recovered");
|
boolean supervised = entry.startsWith("supervised");
|
||||||
|
boolean scheduled = entry.equals("scheduled") || entry.equals("recovered") || supervised;
|
||||||
boolean reuse = entry.equals("reuse");
|
boolean reuse = entry.equals("reuse");
|
||||||
boolean recheck = entry.equals("recheck");
|
boolean recheck = entry.equals("recheck");
|
||||||
boolean queued = entry.equals("queued");
|
boolean queued = entry.equals("queued");
|
||||||
boolean recovered = entry.equals("recovered");
|
boolean recovered = entry.equals("recovered") || entry.equals("supervised-recovered");
|
||||||
String username = "http-json-" + UUID.randomUUID();
|
String username = "http-json-" + UUID.randomUUID();
|
||||||
String conversation = UUID.randomUUID().toString();
|
String conversation = UUID.randomUUID().toString();
|
||||||
long userId = IdWorker.getId(), agentId = IdWorker.getId();
|
long userId = IdWorker.getId(), agentId = IdWorker.getId();
|
||||||
@ -112,8 +120,10 @@ class GoalJsonHttpRuntimeIntegrationTest {
|
|||||||
GoalRunCoordinator.ClaimedRun run = null;
|
GoalRunCoordinator.ClaimedRun run = null;
|
||||||
if (scheduled) {
|
if (scheduled) {
|
||||||
jdbc.update("UPDATE mate_agent_goal SET auto_followup_enabled=TRUE WHERE id=?", goal.getId());
|
jdbc.update("UPDATE mate_agent_goal SET auto_followup_enabled=TRUE WHERE id=?", goal.getId());
|
||||||
continuations.discover(java.time.LocalDateTime.now());
|
if (!supervised || recovered) {
|
||||||
run = claim(goal);
|
continuations.discover(java.time.LocalDateTime.now());
|
||||||
|
run = claim(goal);
|
||||||
|
}
|
||||||
if (recovered) {
|
if (recovered) {
|
||||||
var old = run;
|
var old = run;
|
||||||
var staleOrigin = attemptOrigin(goal, old);
|
var staleOrigin = attemptOrigin(goal, old);
|
||||||
@ -123,11 +133,13 @@ class GoalJsonHttpRuntimeIntegrationTest {
|
|||||||
long expired = java.time.Instant.now().minusSeconds(1).getEpochSecond();
|
long expired = java.time.Instant.now().minusSeconds(1).getEpochSecond();
|
||||||
jdbc.update("UPDATE mate_goal_attempt SET lease_until_epoch_second=? WHERE attempt_id=?", expired, old.attempt().id());
|
jdbc.update("UPDATE mate_goal_attempt SET lease_until_epoch_second=? WHERE attempt_id=?", expired, old.attempt().id());
|
||||||
jdbc.update("UPDATE mate_goal_continuation SET lease_until_epoch_second=? WHERE goal_id=?", expired, goal.getId());
|
jdbc.update("UPDATE mate_goal_continuation SET lease_until_epoch_second=? WHERE goal_id=?", expired, goal.getId());
|
||||||
assertEquals(1, recovery.recoverExpired(java.time.Instant.now()));
|
if (!supervised) {
|
||||||
assertEquals("retry", continuations.get(goal.getId()).state());
|
assertEquals(1, recovery.recoverExpired(java.time.Instant.now()));
|
||||||
run = claim(goal);
|
assertEquals("retry", continuations.get(goal.getId()).state());
|
||||||
assertEquals(old.attempt().id(), run.attempt().parentAttemptId());
|
run = claim(goal);
|
||||||
assertNotEquals(old.attempt().leaseToken(), run.attempt().leaseToken());
|
assertEquals(old.attempt().id(), run.attempt().parentAttemptId());
|
||||||
|
assertNotEquals(old.attempt().leaseToken(), run.attempt().leaseToken());
|
||||||
|
}
|
||||||
assertFalse(coordinator.renew(old, java.time.LocalDateTime.now()));
|
assertFalse(coordinator.renew(old, java.time.LocalDateTime.now()));
|
||||||
assertThrows(vip.mate.exception.MateClawException.class, () -> artifacts.publishForRuntime(staleOrigin, "report",
|
assertThrows(vip.mate.exception.MateClawException.class, () -> artifacts.publishForRuntime(staleOrigin, "report",
|
||||||
new ManagedGoalJsonService.PublishRequest(1L, "{\"summary\":\"stale writer\"}")));
|
new ManagedGoalJsonService.PublishRequest(1L, "{\"summary\":\"stale writer\"}")));
|
||||||
@ -264,6 +276,37 @@ class GoalJsonHttpRuntimeIntegrationTest {
|
|||||||
initialResponse.tryEmitEmpty();
|
initialResponse.tryEmitEmpty();
|
||||||
response.cancel(true);
|
response.cancel(true);
|
||||||
}
|
}
|
||||||
|
} else if (supervised) {
|
||||||
|
GoalAttempt finished = null;
|
||||||
|
var active = (Map<?, ?>) org.springframework.test.util.ReflectionTestUtils.getField(supervisor, "active");
|
||||||
|
assertNotNull(active);
|
||||||
|
long deadline = System.nanoTime() + java.util.concurrent.TimeUnit.SECONDS.toNanos(30);
|
||||||
|
try {
|
||||||
|
while (System.nanoTime() < deadline) {
|
||||||
|
supervisor.tick();
|
||||||
|
finished = attempts.listRecent(goal.getId(), 2).stream()
|
||||||
|
.filter(attempt -> attempt.assistantMessageId() != null
|
||||||
|
&& (accepted ? "succeeded" : "retryable").equals(attempt.state()))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
var projection = continuations.get(goal.getId());
|
||||||
|
if (finished != null && active.isEmpty() && projection != null
|
||||||
|
&& (accepted ? "completed" : "retry").equals(projection.state())) break;
|
||||||
|
Thread.sleep(25);
|
||||||
|
}
|
||||||
|
assertNotNull(finished, "Actual supervisor must dispatch and settle a persisted segment");
|
||||||
|
assertTrue(active.isEmpty(), "Supervisor must release the completed worker");
|
||||||
|
assertEquals(accepted ? "completed" : "retry", continuations.get(goal.getId()).state());
|
||||||
|
assertEquals("message_saved", finished.checkpointType());
|
||||||
|
assertTrue(jdbc.queryForObject("SELECT content FROM mate_message WHERE id=?", String.class,
|
||||||
|
finished.assistantMessageId()).contains(accepted ? "Managed JSON fixture completed." : "PASS from offline fixture."));
|
||||||
|
if (recovered) {
|
||||||
|
assertEquals(run.attempt().id(), finished.parentAttemptId());
|
||||||
|
assertNotEquals(run.attempt().leaseToken(), finished.leaseToken());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
jdbc.update("UPDATE mate_agent_goal SET auto_followup_enabled=FALSE WHERE id=?", goal.getId());
|
||||||
|
runner.cancel(goal.getId());
|
||||||
|
}
|
||||||
} else if (scheduled) {
|
} else if (scheduled) {
|
||||||
SegmentOutcome outcome = runner.run(run, message, recovered);
|
SegmentOutcome outcome = runner.run(run, message, recovered);
|
||||||
assertEquals(accepted ? GoalStatus.COMPLETED : GoalStatus.ACTIVE, goals.getById(goal.getId()).getStatus(), outcome.toString());
|
assertEquals(accepted ? GoalStatus.COMPLETED : GoalStatus.ACTIVE, goals.getById(goal.getId()).getStatus(), outcome.toString());
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user