Skip to content

Integration Testing

yuzhe edited this page Mar 12, 2026 · 1 revision

Integration Testing

RedScript ships with an integration test suite that compiles scripts and runs them against a real Paper 1.21.4 Minecraft server, then asserts behavior via HTTP API.

Architecture

RedScript Test Suite (Jest + TypeScript)
         ↕  HTTP  (port 25561)
   redscript-testharness  (Paper plugin)
         ↕  Bukkit API
      Paper 1.21.4 Server
      (void superflat world)

Setup

1. Install Java 21

# macOS
brew install openjdk@21
export JAVA_HOME=/opt/homebrew/opt/openjdk@21

2. Download Paper

mkdir ~/mc-test-server && cd ~/mc-test-server
curl -L "https://api.papermc.io/v2/projects/paper/versions/1.21.4/builds/232/downloads/paper-1.21.4-232.jar" -o paper.jar
echo "eula=true" > eula.txt

3. Configure for testing

server.properties:

online-mode=false
gamemode=creative
spawn-protection=0
difficulty=peaceful
spawn-monsters=false
level-type=flat
generator-settings={"biome":"minecraft:the_void","layers":[{"block":"minecraft:air","height":1}],"structures":{"structures":{}}}

4. Install TestHarness plugin

git clone https://github.com/bkmashiro/redscript-testharness
cd redscript-testharness
JAVA_HOME=/opt/homebrew/opt/openjdk@21 gradle jar
cp build/libs/redscript-testharness-1.0.0.jar ~/mc-test-server/plugins/

5. Start server

cd ~/mc-test-server
java -Xmx1G -jar paper.jar --nogui
# Wait for: [RedScriptTestHarness] TestHarness HTTP API started on port 25561

6. Run tests

MC_SERVER_DIR=~/mc-test-server npx jest mc-integration --testTimeout=120000

Test Results (v1.0)

  MC Integration Tests
    ✓ server is online and healthy
    ✓ counter.rs: tick function increments scoreboard over time
    ✓ world_manager.rs: setblock places correct block
    ✓ world_manager.rs: fill creates smooth_stone floor
    ✓ scoreboard arithmetic works via commands
    ✓ scoreboard proxy test
    ✓ inline rs: if/else logic executes correctly
    ✓ entity query: armor_stands survive peaceful mode
    ✓ @tick: tick_test increments counter every tick
    ✓ fullReset clears previously placed blocks

  E2E Scenario Tests
    ✓ A: game_loop timer countdown sets ended=1 after N ticks
    ✓ B: calc_sum + calc_product — no temp var collision
    ✓ C: 3-deep call chain preserves intermediate state (10→15→30)
    ✓ D: fill optimizer — 4 adjacent setblocks all placed correctly

Tests: 14 passed, 14 total

MCTestClient API

import { MCTestClient } from './src/mc-test/client'

const mc = new MCTestClient('localhost', 25561)

await mc.isOnline()                          // → boolean
await mc.status()                            // → { tps, players, version }
await mc.command('/function ns:load')        // run MC command
await mc.ticks(20)                           // wait N real server ticks
await mc.reload()                            // safe Bukkit.reloadData()

// Assertions (throw on failure)
await mc.assertScore('player', 'obj', 42)
await mc.assertBlock(4, 65, 4, 'minecraft:gold_block')
await mc.assertChatContains('Game started!')

// Queries
await mc.scoreboard('player', 'obj')         // → number
await mc.block(x, y, z)                      // → { type, blockData }
await mc.entities('@e[type=armor_stand]')    // → Entity[]
await mc.chat(since?)                        // → ChatEntry[]

// Reset
await mc.fullReset()                         // clear area + kill entities + reset scoreboards
await mc.fullReset({ x1: 0, y1: 60, z1: 0, x2: 20, y2: 80, z2: 20 })

Why Void Superflat?

  • No terrain → setblock/fill results fully predictable
  • No random mob spawns → entity queries deterministic
  • Fast world generation
  • fullReset() can restore pristine state between tests

Notes

  • Bukkit.reloadData() is used instead of /reload confirm — the latter can crash Paper with plugins loaded
  • All chunks in the test area are force-loaded on each reset (void worlds don't load chunks without players)
  • Multiple namespaces share one datapack directory; tick.json/load.json are merged, not overwritten

Clone this wiki locally