Skip to content

Commit 8968659

Browse files
plainprinceSimeon Kummercursoragent
authored
Bump @types/node to 25.2.1 + fix flaky time & nether tests (#3828)
* Bump @types/node from 24.0.6 to 25.2.1 and fix flaky tests Bump @types/node to ^25.2.1 (from #3821) and fix two flaky tests that were causing spurious CI failures: - time: disable daylight cycle during /time set assertions to prevent drift (e.g. expected 18000, got 21695) - nether: add stabilization wait after chunk loading on slow CI Co-authored-by: Cursor <cursoragent@cursor.com> * Increase runExample timeout from 30s to 60s The 30s timeout for example tests covers child process spawn, server join, 5s stabilization wait, chunk loading, and the actual test. On slow CI runners this was too tight, causing spurious timeouts (e.g. exampleBee on 1.19.3). Co-authored-by: Cursor <cursoragent@cursor.com> * Replace timeout-based test fixes with robust alternatives nether: Replace blind 1s wait with polling loop that checks for a solid block below before placing. Mocha's 90s test timeout is the backstop. runExample: Remove withTimeout wrapper (mocha handles overall timeout), remove 5s setTimeout before saying "loaded" (server command ordering guarantees the TP is applied before the chat message reaches the child), remove timeout from child join detection. This eliminates the premature "Promise timed out" failures on slow CI. testCommon: Increase base onceWithCleanup timeout from 5s to 20s for helper functions (gamemode change, teleport, clear inventory, etc.) to prevent spurious failures on slow CI while still providing useful error messages. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix runExample: wait for child entity + physics settling The child bot's physics engine needs time to initialize at the teleported position (ground detection, etc.) before tests that require jumping (exampleDigger build) can work. Replace the removed blind 5s setTimeout with: 1. Condition-based poll for child entity at the TP target position 2. waitForTicks(60) for physics engine to settle (~3s game time) This is more robust than the original setTimeout because the entity check adapts to server speed and waitForTicks is tied to game ticks. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Simeon Kummer <simeon@hitthecode.de> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 0d7cac0 commit 8968659

4 files changed

Lines changed: 58 additions & 23 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"vec3": "^0.1.7"
4242
},
4343
"devDependencies": {
44-
"@types/node": "^24.0.6",
44+
"@types/node": "^25.2.1",
4545
"doctoc": "^2.0.1",
4646
"minecraft-wrap": "^1.3.0",
4747
"mineflayer": "file:",

test/externalTests/nether.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const assert = require('assert')
22
const Vec3 = require('vec3')
3-
const { once } = require('../../lib/promise_utils')
3+
const { once, sleep } = require('../../lib/promise_utils')
44

55
module.exports = () => async (bot) => {
66
// Test spawn event on death
@@ -46,7 +46,14 @@ module.exports = () => async (bot) => {
4646
await once(bot, 'forcedMove')
4747
await bot.waitForChunksToLoad()
4848

49-
const lowerBlock = bot.blockAt(bot.entity.position.offset(0, -1, 0))
49+
// Poll until the block below is loaded and non-air before placing.
50+
// On slow CI, chunks may report as loaded before block data is ready.
51+
let lowerBlock = bot.blockAt(bot.entity.position.offset(0, -1, 0))
52+
while (!lowerBlock || lowerBlock.name === 'air') {
53+
await sleep(100)
54+
lowerBlock = bot.blockAt(bot.entity.position.offset(0, -1, 0))
55+
}
56+
5057
await bot.lookAt(lowerBlock.position, true)
5158
await bot.test.setInventorySlot(36, new Item(signItem.id, 1, 0))
5259
await bot.placeBlock(lowerBlock, new Vec3(0, 1, 0))

test/externalTests/plugins/testCommon.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ const { spawn } = require('child_process')
44
const { once } = require('../../../lib/promise_utils')
55
const process = require('process')
66
const assert = require('assert')
7-
const { sleep, onceWithCleanup, withTimeout } = require('../../../lib/promise_utils')
7+
const { sleep, onceWithCleanup } = require('../../../lib/promise_utils')
88

9-
const timeout = 5000
9+
const timeout = 20000
1010
module.exports = inject
1111

1212
function inject (bot) {
@@ -188,14 +188,21 @@ function inject (bot) {
188188

189189
const detectChildJoin = async () => {
190190
const [message] = await onceWithCleanup(bot, 'message', {
191-
timeout,
192191
checkCondition: message => message.json.translate === 'multiplayer.player.joined'
193192
})
194193
childBotName = message.json.with[0].insertion
195194
bot.chat(`/tp ${childBotName} 50 ${bot.test.groundY} 0`)
196-
setTimeout(() => {
197-
bot.chat('loaded')
198-
}, 5000)
195+
// Wait for the child entity to arrive at the teleport target,
196+
// confirming the server has processed the TP
197+
const targetPos = new Vec3(50, bot.test.groundY, 0)
198+
while (!bot.players[childBotName]?.entity ||
199+
bot.players[childBotName].entity.position.distanceTo(targetPos) > 5) {
200+
await sleep(100)
201+
}
202+
// Let the child's physics engine initialize at the new position
203+
// (ground detection, chunk processing) before starting the test
204+
await bot.waitForTicks(60)
205+
bot.chat('loaded')
199206
}
200207

201208
const runExampleOnReady = async () => {
@@ -216,7 +223,7 @@ function inject (bot) {
216223

217224
try {
218225
process.kill(child.pid, 'SIGTERM')
219-
const [code] = await onceWithCleanup(child, 'close', { timeout: 1000 })
226+
const [code] = await onceWithCleanup(child, 'close', { timeout: 5000 })
220227
console.log('close requested', code)
221228
} catch (e) {
222229
console.log(e)
@@ -228,8 +235,11 @@ function inject (bot) {
228235
}
229236
}
230237

238+
// Let mocha's test-level timeout (90s) be the backstop instead of
239+
// an inner withTimeout, which was causing premature failures on
240+
// slow CI runners.
231241
try {
232-
await withTimeout(Promise.all([detectChildJoin(), runExampleOnReady()]), 30000)
242+
await Promise.all([detectChildJoin(), runExampleOnReady()])
233243
} catch (err) {
234244
console.log(err)
235245
return closeExample(err)

test/externalTests/time.js

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,21 @@ module.exports = () => async (bot) => {
4343
}
4444
}
4545

46+
// Helper to set gamerule using the correct name for the version
47+
const setDaylightCycle = (value) => {
48+
if (bot.supportFeature('gameRuleUsesResourceLocation')) {
49+
bot.test.sayEverywhere(`/gamerule minecraft:advance_time ${value}`)
50+
} else {
51+
bot.test.sayEverywhere(`/gamerule doDaylightCycle ${value}`)
52+
}
53+
}
54+
55+
// Disable daylight cycle before time transition tests to prevent
56+
// time from drifting between /time set and the assertion
57+
const originalDaylightCycle = bot.time.doDaylightCycle
58+
setDaylightCycle(false)
59+
await waitForTime()
60+
4661
// Test time transitions
4762
const timeTests = [
4863
{ time: 18000, name: 'midnight', isDay: false },
@@ -58,6 +73,10 @@ module.exports = () => async (bot) => {
5873
assert.strictEqual(bot.time.isDay, test.isDay, `${test.name} should be ${test.isDay ? 'day' : 'night'}`)
5974
}
6075

76+
// Re-enable daylight cycle for progression test
77+
setDaylightCycle(true)
78+
await waitForTime()
79+
6180
// Test day and moon phase progression
6281
const currentDay = bot.time.day
6382
const currentPhase = bot.time.moonPhase
@@ -66,24 +85,19 @@ module.exports = () => async (bot) => {
6685
assert(bot.time.day >= currentDay + 1, `Expected day to be at least ${currentDay + 1}, got ${bot.time.day}`)
6786
assert.notStrictEqual(bot.time.moonPhase, currentPhase, 'Moon phase should change after a full day')
6887

69-
// Test daylight cycle
70-
const originalDaylightCycle = bot.time.doDaylightCycle
71-
if (bot.supportFeature('gameRuleUsesResourceLocation')) {
72-
bot.test.sayEverywhere('/gamerule minecraft:advance_time false')
73-
} else {
74-
bot.test.sayEverywhere('/gamerule doDaylightCycle false')
75-
}
88+
// Test daylight cycle toggle
89+
setDaylightCycle(false)
7690
await waitForTime()
7791
assert.strictEqual(bot.time.doDaylightCycle, false)
7892

79-
if (bot.supportFeature('gameRuleUsesResourceLocation')) {
80-
bot.test.sayEverywhere(`/gamerule minecraft:advance_time ${originalDaylightCycle}`)
81-
} else {
82-
bot.test.sayEverywhere(`/gamerule doDaylightCycle ${originalDaylightCycle}`)
83-
}
93+
setDaylightCycle(originalDaylightCycle)
8494
await waitForTime()
8595
assert.strictEqual(bot.time.doDaylightCycle, originalDaylightCycle)
8696

97+
// Disable daylight cycle again for day/night range tests
98+
setDaylightCycle(false)
99+
await waitForTime()
100+
87101
// Test day/night transitions
88102
const dayNightTests = [
89103
{ command: 'day', range: [0, 12000], isDay: true },
@@ -96,4 +110,8 @@ module.exports = () => async (bot) => {
96110
assert(isTimeInRange(bot.time.timeOfDay, test.range[0], test.range[1]), `Time should be in ${test.command} range`)
97111
assert.strictEqual(bot.time.isDay, test.isDay, `${test.command} should be ${test.isDay ? 'day' : 'night'}`)
98112
}
113+
114+
// Restore original daylight cycle setting
115+
setDaylightCycle(originalDaylightCycle)
116+
await waitForTime()
99117
}

0 commit comments

Comments
 (0)