Tested the client against 1.21.11 on .net 10. Created a integration testing skill (end-to-end).

This commit is contained in:
Anon 2026-03-21 21:02:13 +01:00
parent fb896c45a7
commit 5b9cc4aa0e
13 changed files with 553 additions and 1 deletions

View file

@ -0,0 +1,56 @@
---
name: mcc-integration-testing
description: Repeatable local offline integration testing for Minecraft Console Client against a local Minecraft Java server. Use this whenever the user wants to validate MCC end-to-end against a local server, switch the server to persistent offline mode, run chat or server commands through FileInputBot, exercise inventory/entity handling, or perform deeper smoke testing with mobs, particles, sounds, TNT, and operator actions.
---
# MCC Integration Testing
Use this skill for local MCC validation against the user's `mc-*` and `mcc-*` shell helpers.
## Workflow
1. Source `~/.zshrc` in command invocations.
2. Do not read `~/.zshrc` directly.
3. Ensure the target server is configured for persistent offline testing:
- `online-mode=false`
- `enforce-secure-profile=false`
- `enable-rcon=true`
- `rcon.password=test123`
4. Build with `mcc-build`.
5. Run the scripted scenario with `scripts/run_full_spectrum_test.sh`.
6. Summarize the evidence with `scripts/summarize_test_run.sh`.
## Required Preconditions
- Server jar exists under `~/Minecraft/Servers/<version>/server.jar`
- `eula.txt` contains `eula=true`
- Repo root `MinecraftClient.ini` is the offline MCC test profile
- The MCC config round-trip issue must be fixed before relying on repeated launches
## Scripts
- `scripts/ensure_offline_server.sh`
- Generates `server.properties` if missing
- Applies persistent offline and RCON settings
- `scripts/run_full_spectrum_test.sh`
- Builds MCC
- Starts server and MCC
- Runs the full-spectrum scenario
- Verifies key MCC and server log assertions
- `scripts/summarize_test_run.sh`
- Prints the most relevant evidence from the latest run directory
## Scenario Coverage
The scripted run should cover:
- offline join
- MCC-originated chat
- MCC-originated slash command
- internal MCC commands such as `health`, `list`, `inventory`, and `entity`
- OP + creative mode
- passive and hostile mob spawns
- representative sound and particle events
- TNT / explosion handling
If a command syntax needs to be checked, use `references/command-matrix.md`.

View file

@ -0,0 +1,39 @@
{
"skill_name": "mcc-integration-testing",
"evals": [
{
"id": 1,
"prompt": "Configure the local 1.21.11 MCC test server for persistent offline mode, then run a deep MCC integration test covering chat, operator actions, creative inventory, entity tracking, sounds, particles, and TNT.",
"expected_output": "The server is left in offline mode with RCON enabled, MCC joins successfully, and the run reports clear pass or fail evidence from both MCC and server logs.",
"files": [],
"expectations": [
"The workflow sources ~/.zshrc without reading it directly.",
"The server is configured with online-mode=false and enable-rcon=true.",
"The run uses both mc-rcon and mcc-cmd.",
"The result includes evidence from MCC output and server logs."
]
},
{
"id": 2,
"prompt": "Validate that MCC still works after a runtime or framework change by performing a repeatable offline smoke test against the local vanilla server and exercising entity and inventory handling.",
"expected_output": "The response runs the repeatable local workflow, checks for a successful join, verifies inventory and entity commands, and calls out any configuration or protocol regression.",
"files": [],
"expectations": [
"The workflow builds MCC before running the server scenario.",
"The workflow checks inventory and entity handling explicitly.",
"The response flags config reload failures as regressions."
]
},
{
"id": 3,
"prompt": "Use the local MCC testing workflow to run a full-spectrum client/server exercise and summarize the important evidence only.",
"expected_output": "The response runs the scripted scenario and returns a concise summary with pass/fail status, affected commands, and log evidence.",
"files": [],
"expectations": [
"The workflow uses the scripted test runner.",
"The summary includes join status, MCC command coverage, and server-side effects.",
"The summary points to the saved log locations."
]
}
]
}

View file

@ -0,0 +1,53 @@
# Command Matrix
This skill uses a fixed set of stable commands for local offline integration testing.
## MCC-side commands via `mcc-cmd`
- `health`
- `list`
- `inventory player list`
- `/gamemode creative`
- `inventory creativegive 36 Diamond 16`
- `entity`
- `/time query daytime`
- `smoke_test_from_mcc_full_spectrum`
Notes:
- Lines starting with `/` are sent to the server as chat/commands.
- Non-slash lines are treated as MCC internal commands first, then fall back to chat.
## Server-side commands via `mc-rcon`
- `op CursorBot`
- `gamerule sendCommandFeedback true`
- `gamerule logAdminCommands true`
- `time set day`
- `weather clear`
## Representative entity coverage
- `execute as CursorBot at @s run summon minecraft:cow ~2 ~ ~`
- `execute as CursorBot at @s run summon minecraft:zombie ~4 ~ ~`
- `execute as CursorBot at @s run summon minecraft:creeper ~6 ~ ~`
- `execute as CursorBot at @s run summon minecraft:skeleton ~8 ~ ~`
- `execute as CursorBot at @s run summon minecraft:villager ~-2 ~ ~`
- `execute as CursorBot at @s run summon minecraft:allay ~-4 ~ ~`
- `execute as CursorBot at @s run summon minecraft:armor_stand ~ ~ ~2`
## Representative particle coverage
- `execute as CursorBot at @s run particle minecraft:happy_villager ~ ~1 ~ 0.5 0.5 0.5 0 12 force`
- `execute as CursorBot at @s run particle minecraft:end_rod ~ ~1 ~ 0.5 0.5 0.5 0.01 20 force`
- `execute as CursorBot at @s run particle minecraft:explosion ~ ~1 ~ 0 0 0 0 1 force`
- `execute as CursorBot at @s run particle minecraft:totem_of_undying ~ ~1 ~ 0.5 0.5 0.5 0.1 20 force`
## Representative sound coverage
- `execute as CursorBot at @s run playsound minecraft:entity.lightning_bolt.thunder master CursorBot ~ ~ ~ 1 1 0`
- `execute as CursorBot at @s run playsound minecraft:block.note_block.bell master CursorBot ~ ~ ~ 1 1 0`
## Explosion coverage
- `execute as CursorBot at @s run summon minecraft:tnt ~3 ~ ~`
- `execute as CursorBot at @s run summon minecraft:tnt ~6 ~ ~`

View file

@ -0,0 +1,84 @@
#!/usr/bin/env zsh
set -euo pipefail
set +eu
source ~/.zshrc
set -eu
VERSION="${1:-1.21.11-Vanilla}"
SERVER_DIR="${MCC_SERVERS:?}/$VERSION"
PROPS_FILE="$SERVER_DIR/server.properties"
SESSION_NAME="mc-${VERSION//./_}"
if [[ ! -d "$SERVER_DIR" ]]; then
echo "Server directory not found: $SERVER_DIR" >&2
exit 1
fi
if [[ ! -f "$SERVER_DIR/eula.txt" ]] || ! grep -Eq '^eula=true$' "$SERVER_DIR/eula.txt"; then
echo "Missing accepted EULA in $SERVER_DIR/eula.txt" >&2
exit 1
fi
server_running() {
mc-list | grep -Fq "$SESSION_NAME"
}
wait_for_server_ready() {
local timeout="${1:-60}"
local elapsed=0
while (( elapsed < timeout )); do
if mc-log "$VERSION" 200 2>/dev/null | grep -Fq "Done ("; then
return 0
fi
sleep 1
((elapsed += 1))
done
echo "Timed out waiting for $VERSION to become ready" >&2
return 1
}
wait_for_server_stop() {
local timeout="${1:-60}"
local elapsed=0
while (( elapsed < timeout )); do
if ! server_running; then
return 0
fi
sleep 1
((elapsed += 1))
done
echo "Timed out waiting for $VERSION to stop" >&2
return 1
}
upsert_property() {
local key="$1"
local value="$2"
if grep -Eq "^${key}=" "$PROPS_FILE"; then
sed -i "s#^${key}=.*#${key}=${value}#" "$PROPS_FILE"
else
printf '%s=%s\n' "$key" "$value" >> "$PROPS_FILE"
fi
}
if [[ ! -f "$PROPS_FILE" ]]; then
mc-start "$VERSION"
wait_for_server_ready
mc-stop "$VERSION"
wait_for_server_stop
fi
if server_running; then
mc-stop "$VERSION"
wait_for_server_stop
fi
upsert_property "online-mode" "false"
upsert_property "enforce-secure-profile" "false"
upsert_property "enable-rcon" "true"
upsert_property "rcon.port" "25575"
upsert_property "rcon.password" "test123"
echo "Configured $VERSION for persistent offline testing"

View file

@ -0,0 +1,219 @@
#!/usr/bin/env zsh
set -euo pipefail
set +eu
source ~/.zshrc
set -eu
SCRIPT_DIR="${0:A:h}"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
VERSION="${1:-1.21.11-Vanilla}"
RUN_ROOT="${TMPDIR:-/tmp}/mcc-integration-testing"
RUN_ID="$(date +%Y%m%d-%H%M%S)"
RUN_DIR="$RUN_ROOT/$RUN_ID"
SERVER_LOG_FILE="$MCC_SERVERS/$VERSION/logs/latest.log"
MCC_LOG="$RUN_DIR/mcc.log"
BUILD_LOG="$RUN_DIR/build.log"
SERVER_TMUX_LOG="$RUN_DIR/server-tmux.log"
SERVER_FILE_LOG="$RUN_DIR/server-latest.log"
INPUT_FILE="$REPO_ROOT/mcc_input.txt"
MCC_PID=""
mkdir -p "$RUN_DIR"
cleanup() {
if [[ -n "${MCC_PID:-}" ]] && kill -0 "$MCC_PID" 2>/dev/null; then
mcc-cmd "quit" >/dev/null 2>&1 || true
sleep 2
kill "$MCC_PID" 2>/dev/null || true
wait "$MCC_PID" 2>/dev/null || true
fi
mc-stop "$VERSION" >/dev/null 2>&1 || true
}
trap cleanup EXIT
wait_for_file_pattern() {
local file="$1"
local pattern="$2"
local description="$3"
local timeout="${4:-60}"
local elapsed=0
while (( elapsed < timeout )); do
if [[ -f "$file" ]] && grep -Fq "$pattern" "$file"; then
return 0
fi
sleep 1
((elapsed += 1))
done
echo "Timed out waiting for: $description" >&2
return 1
}
wait_for_server_ready() {
local timeout="${1:-60}"
local elapsed=0
while (( elapsed < timeout )); do
if mc-log "$VERSION" 250 2>/dev/null | grep -Fq "Done ("; then
return 0
fi
sleep 1
((elapsed += 1))
done
echo "Timed out waiting for server readiness" >&2
return 1
}
wait_for_server_log_pattern() {
local pattern="$1"
local description="$2"
local timeout="${3:-60}"
local elapsed=0
while (( elapsed < timeout )); do
if [[ -f "$SERVER_LOG_FILE" ]] && grep -Fq "$pattern" "$SERVER_LOG_FILE"; then
return 0
fi
sleep 1
((elapsed += 1))
done
echo "Timed out waiting for server log: $description" >&2
return 1
}
capture_server_logs() {
mc-log "$VERSION" 400 > "$SERVER_TMUX_LOG" 2>/dev/null || true
if [[ -f "$SERVER_LOG_FILE" ]]; then
cp "$SERVER_LOG_FILE" "$SERVER_FILE_LOG"
fi
}
fail() {
capture_server_logs
echo "FAIL: $1" >&2
echo "Run directory: $RUN_DIR" >&2
exit 1
}
assert_contains() {
local file="$1"
local pattern="$2"
local description="$3"
grep -Fq "$pattern" "$file" || fail "$description"
}
assert_not_contains() {
local file="$1"
local pattern="$2"
local description="$3"
if grep -Fq "$pattern" "$file"; then
fail "$description"
fi
}
run_server_command() {
local cmd="$1"
echo "SERVER> $cmd"
mc-rcon "$cmd" >/dev/null || fail "Server command failed: $cmd"
}
run_mcc_command() {
local cmd="$1"
echo "MCC> $cmd"
mcc-cmd "$cmd"
sleep 2
}
"$SCRIPT_DIR/ensure_offline_server.sh" "$VERSION"
: > "$INPUT_FILE"
echo "Building MCC..."
mcc-build > "$BUILD_LOG" 2>&1 || fail "mcc-build failed"
echo "Starting server..."
mc-start "$VERSION" >/dev/null
wait_for_server_ready || fail "Server did not become ready"
echo "Starting MCC..."
mcc-run 25565 > "$MCC_LOG" 2>&1 &
MCC_PID=$!
wait_for_file_pattern "$MCC_LOG" "Server was successfully joined." "MCC join success" 90 || fail "MCC failed to join"
wait_for_server_log_pattern "CursorBot joined the game" "server join entry" 30 || fail "Server never logged the join"
run_server_command "op CursorBot"
run_server_command "gamerule sendCommandFeedback true"
run_server_command "gamerule logAdminCommands true"
run_server_command "time set day"
run_server_command "weather clear"
sleep 2
run_mcc_command "health"
run_mcc_command "list"
run_mcc_command "inventory player list"
run_mcc_command "/gamemode creative"
run_mcc_command "inventory creativegive 36 Diamond 16"
run_mcc_command "inventory player list"
run_mcc_command "entity"
run_mcc_command "/time query daytime"
run_mcc_command "smoke_test_from_mcc_full_spectrum"
run_server_command "execute as CursorBot at @s run summon minecraft:cow ~2 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:zombie ~4 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:creeper ~6 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:skeleton ~8 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:villager ~-2 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:allay ~-4 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:armor_stand ~ ~ ~2"
sleep 2
run_mcc_command "entity"
run_server_command "execute as CursorBot at @s run particle minecraft:happy_villager ~ ~1 ~ 0.5 0.5 0.5 0 12 force"
run_server_command "execute as CursorBot at @s run particle minecraft:end_rod ~ ~1 ~ 0.5 0.5 0.5 0.01 20 force"
run_server_command "execute as CursorBot at @s run particle minecraft:explosion ~ ~1 ~ 0 0 0 0 1 force"
run_server_command "execute as CursorBot at @s run particle minecraft:totem_of_undying ~ ~1 ~ 0.5 0.5 0.5 0.1 20 force"
run_server_command "execute as CursorBot at @s run playsound minecraft:entity.lightning_bolt.thunder master CursorBot ~ ~ ~ 1 1 0"
run_server_command "execute as CursorBot at @s run playsound minecraft:block.note_block.bell master CursorBot ~ ~ ~ 1 1 0"
run_server_command "execute as CursorBot at @s run summon minecraft:tnt ~3 ~ ~"
sleep 2
run_server_command "execute as CursorBot at @s run summon minecraft:tnt ~6 ~ ~"
sleep 6
capture_server_logs
assert_contains "$MCC_LOG" "Server was successfully joined." "MCC never joined the server"
assert_contains "$MCC_LOG" "[FileInput] > inventory player list" "Inventory command was not executed"
assert_contains "$MCC_LOG" "[FileInput] > entity" "Entity command was not executed"
assert_contains "$MCC_LOG" "[FileInput] > /gamemode creative" "Creative mode command was not executed from MCC"
assert_contains "$MCC_LOG" "Requested Diamond x16 in slot #36" "Creative inventory give did not succeed"
assert_contains "$MCC_LOG" "smoke_test_from_mcc_full_spectrum" "Client-originated chat was not observed"
assert_not_contains "$MCC_LOG" "Please enable InventoryHandling" "Inventory handling is still disabled"
assert_not_contains "$MCC_LOG" "Please enable EntityHandling" "Entity handling is still disabled"
assert_not_contains "$MCC_LOG" "You must be in Creative gamemode" "Creative mode was not active when creativegive ran"
assert_not_contains "$MCC_LOG" "Failed to load settings" "MCC failed to reload its config"
assert_contains "$SERVER_FILE_LOG" "CursorBot joined the game" "Server never saw CursorBot join"
assert_contains "$SERVER_FILE_LOG" "smoke_test_from_mcc_full_spectrum" "Server never received the client chat message"
assert_contains "$SERVER_FILE_LOG" "Displaying particle minecraft:happy_villager" "Particle events were not recorded on the server"
assert_contains "$SERVER_FILE_LOG" "Played sound minecraft:block.note_block.bell to CursorBot" "Sound events were not recorded on the server"
assert_contains "$SERVER_FILE_LOG" "Summoned new Primed TNT" "TNT summon did not occur on the server"
assert_not_contains "$SERVER_FILE_LOG" "Sending unknown packet 'clientbound/minecraft:disconnect'" "Server hit the disconnect packet regression during the test"
cat <<EOF
PASS
Run directory: $RUN_DIR
MCC log: $MCC_LOG
Server log: $SERVER_FILE_LOG
Build log: $BUILD_LOG
EOF

View file

@ -0,0 +1,25 @@
#!/usr/bin/env zsh
set -euo pipefail
RUN_ROOT="${TMPDIR:-/tmp}/mcc-integration-testing"
RUN_DIR="${1:-$(find "$RUN_ROOT" -mindepth 1 -maxdepth 1 -type d | sort | tail -n 1)}"
if [[ -z "${RUN_DIR:-}" ]] || [[ ! -d "$RUN_DIR" ]]; then
echo "Run directory not found" >&2
exit 1
fi
MCC_LOG="$RUN_DIR/mcc.log"
SERVER_LOG="$RUN_DIR/server-latest.log"
BUILD_LOG="$RUN_DIR/build.log"
echo "Run directory: $RUN_DIR"
echo
echo "Build result:"
grep -E "Warning\(s\)|Error\(s\)|Time Elapsed" "$BUILD_LOG" || true
echo
echo "MCC highlights:"
grep -E "Server was successfully joined|FileInput|smoke_test_from_mcc_full_spectrum|There are [0-9]+ of a max|health|Creative" "$MCC_LOG" || true
echo
echo "Server highlights:"
grep -E "joined the game|Made CursorBot a server operator|game mode|smoke_test_from_mcc_full_spectrum|summon|particle|playsound|tnt" "$SERVER_LOG" || true

View file

@ -64,6 +64,12 @@ namespace MinecraftClient.ChatBots
{ {
public double min, max; public double min, max;
public Range()
{
min = 0;
max = 0;
}
public Range(int value) public Range(int value)
{ {
min = max = value; min = max = value;
@ -180,4 +186,4 @@ namespace MinecraftClient.ChatBots
currentLocation.Z + random.Next(range * -1, range)); currentLocation.Z + random.Next(range * -1, range));
} }
} }
} }

View file

@ -106,6 +106,13 @@ namespace MinecraftClient.ChatBots
{ {
public double X, Y, Z; public double X, Y, Z;
public LocationConfig()
{
X = 0;
Y = 0;
Z = 0;
}
public LocationConfig(double X, double Y, double Z) public LocationConfig(double X, double Y, double Z)
{ {
this.X = X; this.X = X;

View file

@ -85,6 +85,13 @@ namespace MinecraftClient.ChatBots
{ {
public double x, y, z; public double x, y, z;
public Coordination()
{
x = 0;
y = 0;
z = 0;
}
public Coordination(double x, double y, double z) public Coordination(double x, double y, double z)
{ {
this.x = x; this.y = y; this.z = z; this.x = x; this.y = y; this.z = z;

View file

@ -103,6 +103,12 @@ namespace MinecraftClient.ChatBots
public Coordination? XYZ; public Coordination? XYZ;
public Facing? facing; public Facing? facing;
public LocationConfig()
{
XYZ = null;
facing = null;
}
public LocationConfig(double yaw, double pitch) public LocationConfig(double yaw, double pitch)
{ {
this.XYZ = null; this.XYZ = null;
@ -125,6 +131,13 @@ namespace MinecraftClient.ChatBots
{ {
public double x, y, z; public double x, y, z;
public Coordination()
{
x = 0;
y = 0;
z = 0;
}
public Coordination(double x, double y, double z) public Coordination(double x, double y, double z)
{ {
this.x = x; this.y = y; this.z = z; this.x = x; this.y = y; this.z = z;
@ -135,6 +148,12 @@ namespace MinecraftClient.ChatBots
{ {
public double yaw, pitch; public double yaw, pitch;
public Facing()
{
yaw = 0;
pitch = 0;
}
public Facing(double yaw, double pitch) public Facing(double yaw, double pitch)
{ {
this.yaw = yaw; this.pitch = pitch; this.yaw = yaw; this.pitch = pitch;

View file

@ -57,6 +57,12 @@ namespace MinecraftClient.ChatBots
{ {
public double min, max; public double min, max;
public Range()
{
min = 0;
max = 0;
}
public Range(int value) public Range(int value)
{ {
min = max = value; min = max = value;

View file

@ -116,6 +116,12 @@ namespace MinecraftClient.ChatBots
public bool Enable = false; public bool Enable = false;
public TimeSpan[] Times; public TimeSpan[] Times;
public TriggerOnTimeConfig()
{
Enable = false;
Times = Array.Empty<TimeSpan>();
}
public TriggerOnTimeConfig(bool Enable, TimeSpan[] Time) public TriggerOnTimeConfig(bool Enable, TimeSpan[] Time)
{ {
this.Enable = Enable; this.Enable = Enable;
@ -134,6 +140,13 @@ namespace MinecraftClient.ChatBots
public bool Enable = false; public bool Enable = false;
public double MinTime, MaxTime; public double MinTime, MaxTime;
public TriggerOnIntervalConfig()
{
Enable = false;
MinTime = 0;
MaxTime = 0;
}
public TriggerOnIntervalConfig(double value) public TriggerOnIntervalConfig(double value)
{ {
this.Enable = true; this.Enable = true;

View file

@ -658,6 +658,12 @@ namespace MinecraftClient
{ {
public string Login = string.Empty, Password = string.Empty; public string Login = string.Empty, Password = string.Empty;
public AccountInfoConfig()
{
Login = string.Empty;
Password = string.Empty;
}
public AccountInfoConfig(string Login) public AccountInfoConfig(string Login)
{ {
this.Login = Login; this.Login = Login;
@ -676,6 +682,12 @@ namespace MinecraftClient
public string Host = string.Empty; public string Host = string.Empty;
public ushort? Port = null; public ushort? Port = null;
public ServerInfoConfig()
{
Host = string.Empty;
Port = null;
}
public ServerInfoConfig(string Host) public ServerInfoConfig(string Host)
{ {
string[] sip = Host.Split(new[] { ":", "" }, StringSplitOptions.None); string[] sip = Host.Split(new[] { ":", "" }, StringSplitOptions.None);
@ -699,6 +711,12 @@ namespace MinecraftClient
public string Host = string.Empty; public string Host = string.Empty;
public int Port = 443; public int Port = 443;
public AuthlibServer()
{
Host = string.Empty;
Port = 443;
}
public AuthlibServer(string Host) public AuthlibServer(string Host)
{ {
string[] sip = Host.Split(new[] { ":", "" }, StringSplitOptions.None); string[] sip = Host.Split(new[] { ":", "" }, StringSplitOptions.None);