backend-infra-engineer: Release v0.3.3 snapshot

This commit is contained in:
scawful
2025-11-21 21:35:50 -05:00
parent 3d71417f62
commit 476dd1cd1c
818 changed files with 65706 additions and 35514 deletions

View File

@@ -21,17 +21,16 @@ using ::testing::Return;
class MockGuiAutomationClient : public GuiAutomationClient {
public:
MockGuiAutomationClient() : GuiAutomationClient("localhost:50052") {}
MOCK_METHOD(absl::Status, Connect, ());
MOCK_METHOD(absl::StatusOr<AutomationResult>, Ping, (const std::string&));
MOCK_METHOD(absl::StatusOr<AutomationResult>, Click,
MOCK_METHOD(absl::StatusOr<AutomationResult>, Click,
(const std::string&, ClickType));
MOCK_METHOD(absl::StatusOr<AutomationResult>, Type,
(const std::string&, const std::string&, bool));
MOCK_METHOD(absl::StatusOr<AutomationResult>, Wait,
(const std::string&, int, int));
MOCK_METHOD(absl::StatusOr<AutomationResult>, Assert,
(const std::string&));
MOCK_METHOD(absl::StatusOr<AutomationResult>, Assert, (const std::string&));
};
class AIGUIControllerTest : public ::testing::Test {
@@ -42,12 +41,12 @@ class AIGUIControllerTest : public ::testing::Test {
config.api_key = "test_key";
config.model = "gemini-2.5-flash";
gemini_service_ = std::make_unique<GeminiAIService>(config);
gui_client_ = std::make_unique<MockGuiAutomationClient>();
controller_ = std::make_unique<AIGUIController>(
gemini_service_.get(), gui_client_.get());
controller_ = std::make_unique<AIGUIController>(gemini_service_.get(),
gui_client_.get());
ControlLoopConfig loop_config;
loop_config.max_iterations = 5;
loop_config.enable_vision_verification = false; // Disable for unit tests
@@ -68,16 +67,16 @@ TEST_F(AIGUIControllerTest, ExecuteClickAction_Success) {
AIAction action(AIActionType::kClickButton);
action.parameters["target"] = "button:Test";
action.parameters["click_type"] = "left";
AutomationResult result;
result.success = true;
result.message = "Click successful";
EXPECT_CALL(*gui_client_, Click("button:Test", ClickType::kLeft))
.WillOnce(Return(result));
auto status = controller_->ExecuteSingleAction(action, false);
ASSERT_TRUE(status.ok()) << status.status().message();
EXPECT_TRUE(status->action_successful);
}
@@ -85,18 +84,18 @@ TEST_F(AIGUIControllerTest, ExecuteClickAction_Success) {
TEST_F(AIGUIControllerTest, ExecuteClickAction_Failure) {
AIAction action(AIActionType::kClickButton);
action.parameters["target"] = "button:NonExistent";
AutomationResult result;
result.success = false;
result.message = "Button not found";
EXPECT_CALL(*gui_client_, Click("button:NonExistent", ClickType::kLeft))
.WillOnce(Return(result));
auto status = controller_->ExecuteSingleAction(action, false);
EXPECT_FALSE(status.ok());
EXPECT_THAT(status.status().message(),
EXPECT_THAT(status.status().message(),
::testing::HasSubstr("Click action failed"));
}
@@ -105,20 +104,21 @@ TEST_F(AIGUIControllerTest, ExecuteClickAction_Failure) {
// ============================================================================
TEST_F(AIGUIControllerTest, ExecuteTypeAction_Success) {
AIAction action(AIActionType::kSelectTile); // Using SelectTile as a type action
AIAction action(
AIActionType::kSelectTile); // Using SelectTile as a type action
action.parameters["target"] = "input:TileID";
action.parameters["text"] = "0x42";
action.parameters["clear_first"] = "true";
AutomationResult result;
result.success = true;
result.message = "Text entered";
EXPECT_CALL(*gui_client_, Type("input:TileID", "0x42", true))
.WillOnce(Return(result));
auto status = controller_->ExecuteSingleAction(action, false);
ASSERT_TRUE(status.ok());
EXPECT_TRUE(status->action_successful);
}
@@ -131,16 +131,16 @@ TEST_F(AIGUIControllerTest, ExecuteWaitAction_Success) {
AIAction action(AIActionType::kWait);
action.parameters["condition"] = "window:OverworldEditor";
action.parameters["timeout_ms"] = "2000";
AutomationResult result;
result.success = true;
result.message = "Condition met";
EXPECT_CALL(*gui_client_, Wait("window:OverworldEditor", 2000, 100))
.WillOnce(Return(result));
auto status = controller_->ExecuteSingleAction(action, false);
ASSERT_TRUE(status.ok());
EXPECT_TRUE(status->action_successful);
}
@@ -149,16 +149,16 @@ TEST_F(AIGUIControllerTest, ExecuteWaitAction_Timeout) {
AIAction action(AIActionType::kWait);
action.parameters["condition"] = "window:NonExistentWindow";
action.parameters["timeout_ms"] = "100";
AutomationResult result;
result.success = false;
result.message = "Timeout waiting for condition";
EXPECT_CALL(*gui_client_, Wait("window:NonExistentWindow", 100, 100))
.WillOnce(Return(result));
auto status = controller_->ExecuteSingleAction(action, false);
EXPECT_FALSE(status.ok());
}
@@ -169,18 +169,17 @@ TEST_F(AIGUIControllerTest, ExecuteWaitAction_Timeout) {
TEST_F(AIGUIControllerTest, ExecuteVerifyAction_Success) {
AIAction action(AIActionType::kVerifyTile);
action.parameters["condition"] = "tile_placed";
AutomationResult result;
result.success = true;
result.message = "Assertion passed";
result.expected_value = "0x42";
result.actual_value = "0x42";
EXPECT_CALL(*gui_client_, Assert("tile_placed"))
.WillOnce(Return(result));
EXPECT_CALL(*gui_client_, Assert("tile_placed")).WillOnce(Return(result));
auto status = controller_->ExecuteSingleAction(action, false);
ASSERT_TRUE(status.ok());
EXPECT_TRUE(status->action_successful);
}
@@ -188,25 +187,23 @@ TEST_F(AIGUIControllerTest, ExecuteVerifyAction_Success) {
TEST_F(AIGUIControllerTest, ExecuteVerifyAction_Failure) {
AIAction action(AIActionType::kVerifyTile);
action.parameters["condition"] = "tile_placed";
AutomationResult result;
result.success = false;
result.message = "Assertion failed";
result.expected_value = "0x42";
result.actual_value = "0x00";
EXPECT_CALL(*gui_client_, Assert("tile_placed"))
.WillOnce(Return(result));
EXPECT_CALL(*gui_client_, Assert("tile_placed")).WillOnce(Return(result));
auto status = controller_->ExecuteSingleAction(action, false);
EXPECT_FALSE(status.ok());
EXPECT_THAT(status.status().message(),
::testing::HasSubstr("Assert action failed"));
EXPECT_THAT(status.status().message(),
::testing::HasSubstr("expected: 0x42"));
EXPECT_THAT(status.status().message(),
::testing::HasSubstr("actual: 0x00"));
EXPECT_THAT(status.status().message(), ::testing::HasSubstr("actual: 0x00"));
}
// ============================================================================
@@ -219,27 +216,27 @@ TEST_F(AIGUIControllerTest, ExecutePlaceTileAction_CompleteFlow) {
action.parameters["x"] = "10";
action.parameters["y"] = "20";
action.parameters["tile"] = "0x42";
AutomationResult result;
result.success = true;
// Expect sequence: open menu, wait for window, set map ID, click position
testing::InSequence seq;
EXPECT_CALL(*gui_client_, Click("menu:Overworld", ClickType::kLeft))
.WillOnce(Return(result));
EXPECT_CALL(*gui_client_, Wait("window:Overworld Editor", 2000, 100))
.WillOnce(Return(result));
EXPECT_CALL(*gui_client_, Type("input:Map ID", "5", true))
.WillOnce(Return(result));
EXPECT_CALL(*gui_client_, Click(::testing::_, ClickType::kLeft))
.WillOnce(Return(result));
auto status = controller_->ExecuteSingleAction(action, false);
ASSERT_TRUE(status.ok()) << status.status().message();
EXPECT_TRUE(status->action_successful);
}
@@ -250,26 +247,26 @@ TEST_F(AIGUIControllerTest, ExecutePlaceTileAction_CompleteFlow) {
TEST_F(AIGUIControllerTest, ExecuteActions_MultipleActionsSuccess) {
std::vector<AIAction> actions;
AIAction action1(AIActionType::kClickButton);
action1.parameters["target"] = "button:Overworld";
actions.push_back(action1);
AIAction action2(AIActionType::kWait);
action2.parameters["condition"] = "window:OverworldEditor";
actions.push_back(action2);
AutomationResult success_result;
success_result.success = true;
EXPECT_CALL(*gui_client_, Click("button:Overworld", ClickType::kLeft))
.WillOnce(Return(success_result));
EXPECT_CALL(*gui_client_, Wait("window:OverworldEditor", 5000, 100))
.WillOnce(Return(success_result));
auto result = controller_->ExecuteActions(actions);
ASSERT_TRUE(result.ok()) << result.status().message();
EXPECT_TRUE(result->success);
EXPECT_EQ(result->actions_executed.size(), 2);
@@ -277,28 +274,27 @@ TEST_F(AIGUIControllerTest, ExecuteActions_MultipleActionsSuccess) {
TEST_F(AIGUIControllerTest, ExecuteActions_StopsOnFirstFailure) {
std::vector<AIAction> actions;
AIAction action1(AIActionType::kClickButton);
action1.parameters["target"] = "button:Test";
actions.push_back(action1);
AIAction action2(AIActionType::kClickButton);
action2.parameters["target"] = "button:NeverReached";
actions.push_back(action2);
AutomationResult failure_result;
failure_result.success = false;
failure_result.message = "First action failed";
EXPECT_CALL(*gui_client_, Click("button:Test", ClickType::kLeft))
.WillOnce(Return(failure_result));
// Second action should never be called
EXPECT_CALL(*gui_client_, Click("button:NeverReached", _))
.Times(0);
EXPECT_CALL(*gui_client_, Click("button:NeverReached", _)).Times(0);
auto result = controller_->ExecuteActions(actions);
EXPECT_FALSE(result.ok());
EXPECT_EQ(result->actions_executed.size(), 1);
}
@@ -309,9 +305,9 @@ TEST_F(AIGUIControllerTest, ExecuteActions_StopsOnFirstFailure) {
TEST_F(AIGUIControllerTest, ExecuteAction_InvalidActionType) {
AIAction action(AIActionType::kInvalidAction);
auto status = controller_->ExecuteSingleAction(action, false);
EXPECT_FALSE(status.ok());
EXPECT_THAT(status.status().message(),
::testing::HasSubstr("Action type not implemented"));
@@ -320,9 +316,9 @@ TEST_F(AIGUIControllerTest, ExecuteAction_InvalidActionType) {
TEST_F(AIGUIControllerTest, ExecutePlaceTileAction_MissingParameters) {
AIAction action(AIActionType::kPlaceTile);
// Missing required parameters
auto status = controller_->ExecuteSingleAction(action, false);
EXPECT_FALSE(status.ok());
EXPECT_THAT(status.status().message(),
::testing::HasSubstr("requires map_id, x, y, and tile"));