Writing Stories Guide

This guide covers the YAML story format used by Pilaf for defining test scenarios.

Story Structure

A Pilaf story consists of several sections:

name: "Story Name"           # Required: Descriptive name
description: "Description"   # Optional: What this test validates

setup:                       # Optional: Setup actions
  - action: "..."

steps:                       # Required: Main test actions
  - action: "..."

cleanup:                     # Optional: Cleanup actions
  - action: "..."

Name and Description

The name field is required and should be unique and descriptive:

name: "Test Lightning Ability"
description: "Validates that the lightning command spawns lightning and damages entities"

Setup Section

The setup section contains actions that prepare the test environment:

setup:
  - action: "execute_rcon_command"
    command: "op test_player"
    assertions:
      - type: "assert_success"

  - action: "execute_rcon_command"
    command: "whitelist add test_player"

Steps Section

The steps section contains the main test actions:

steps:
  - action: "send_chat"
    player: "test_player"
    message: "/lightning"

  - action: "wait"
    seconds: 2

  - action: "check_log"
    pattern: "Lightning struck"
    assertions:
      - type: "assert_match"

Cleanup Section

The cleanup section restores the environment after testing:

cleanup:
  - action: "execute_rcon_command"
    command: "deop test_player"

  - action: "execute_rcon_command"
    command: "whitelist remove test_player"

Action Reference

Server Actions

Server actions interact with the Minecraft server through RCON.

execute_rcon_command

Execute any RCON command on the server:

- action: "execute_rcon_command"
  command: "op test_player"
  assertions:
    - type: "assert_success"
Parameters:
  • command (required): The RCON command to execute

  • assertions (optional): List of assertions to validate the result

check_log

Wait for and check server log patterns:

- action: "check_log"
  pattern: "Player spawned"
  timeout: 10
  assertions:
    - type: "assert_match"
Parameters:
  • pattern (required): Regex pattern to match

  • timeout (optional): Maximum seconds to wait (default: 30)

  • assertions (optional): List of assertions

Client Actions

Client actions simulate player interactions through connected clients.

send_chat

Send a chat message as a player:

- action: "send_chat"
  player: "test_player"
  message: "/give @p diamond_sword"
  assertions:
    - type: "assert_success"

move_player

Move a player to specific coordinates:

- action: "move_player"
  player: "test_player"
  x: 100
  y: 64
  z: 100
  assertions:
    - type: "assert_success"

give_item

Give items to a player:

- action: "give_item"
  player: "test_player"
  item: "diamond_sword"
  count: 1
  assertions:
    - type: "assert_success"

Workflow Actions

Workflow actions control test flow and validation.

wait

Pause execution for a specified duration:

- action: "wait"
  seconds: 5

capture_state

Capture server or player state for later comparison:

- action: "capture_state"
  variable: "inventory_before"
  target: "inventory"
  player: "test_player"

assert_state

Compare captured states:

- action: "assert_state"
  expected: "inventory_before"
  actual: "inventory_after"
  assertion:
    type: "assert_not_equal"

Assertions

Assertions validate the results of actions.

Common Assertion Types

Type Description

assert_success

Action completed without errors

assert_match

String matches a pattern

assert_not_match

String does not match a pattern

assert_contains

Collection contains an item

assert_not_contains

Collection does not contain an item

assert_equal

Values are equal

assert_not_equal

Values are not equal

Assertion Examples

- action: "check_log"
  pattern: "Lightning struck"
  assertions:
    - type: "assert_match"
      invert: false  # Normal assertion

    - type: "assert_not_match"
      pattern: "Error"
      invert: false  # No errors expected

Variables and State

Pilaf supports storing and comparing states across test steps.

Capturing State

steps:
  # Capture initial inventory
  - action: "capture_state"
    variable: "initial_inventory"
    target: "inventory"
    player: "test_player"

  # Perform action that changes inventory
  - action: "give_item"
    player: "test_player"
    item: "bow"
    count: 1

  # Capture new inventory
  - action: "capture_state"
    variable: "new_inventory"
    target: "inventory"
    player: "test_player"

  # Compare states
  - action: "assert_state"
    expected: "initial_inventory"
    actual: "new_inventory"
    assertion:
      type: "assert_not_equal"

Advanced Patterns

Multiple Players

Test interactions between multiple players:

steps:
  - action: "send_chat"
    player: "player1"
    message: "/home set"

  - action: "send_chat"
    player: "player2"
    message: "/home set"

  - action: "send_chat"
    player: "player1"
    message: "/home teleport player2"

  - action: "check_log"
    pattern: "Teleported"
    assertions:
      - type: "assert_match"

Conditional Assertions

Use invert for negative assertions:

- action: "check_log"
  pattern: "Error occurred"
  assertions:
    - type: "assert_match"
      invert: true  # Assert that pattern is NOT found

Optional Actions

Mark actions as optional for cleanup:

cleanup:
  - action: "execute_rcon_command"
    command: "deop test_player"
    optional: true  # Ignore if player doesn't exist

Best Practices

  1. Use descriptive names: Test Lightning Ability instead of Test1

  2. Include assertions: Always validate the expected outcomes

  3. Proper cleanup: Ensure tests don’t leave the server in a dirty state

  4. Use appropriate wait times: Don’t over-wait, but allow for server processing

  5. Test isolation: Each story should be independent


Back to top

Copyright © 2025 Pilaf Contributors. Open source under the MIT license.

This site uses Just the Docs, a documentation theme for Jekyll.