Update tests for BufferLogAppender and its utilities.

AI-Assisted: yes
AI-Tool: OpenAI Codex / gpt-5.5 xhigh
This commit is contained in:
2026-05-07 07:29:36 -05:00
parent 1546973475
commit bf96303bf9
2 changed files with 258 additions and 76 deletions
+199 -76
View File
@@ -1,104 +1,227 @@
import { describe, test, expect } from "bun:test"; import { describe, expect, test } from 'bun:test'
import { BufferLogAppender, LogLevel, LogMessage } from "../src"; import {
BufferLogAppender,
LogLevel,
type LogMessage,
} from '../src'
function makeMsg(overrides: Partial<LogMessage> = {}): LogMessage { function makeMsg(overrides: Partial<LogMessage> = {}): LogMessage {
return { return {
scope: "test-scope", scope: 'test-scope',
level: LogLevel.INFO, level: LogLevel.INFO,
msg: "test message", msg: 'test message',
stacktrace: "", stacktrace: '',
ts: new Date(), ts: new Date(),
...overrides, ...overrides,
}; }
} }
describe("BufferLogAppender", () => { describe('BufferLogAppender', () => {
test("defaults to empty buffer and ALL threshold", () => { test('defaults to empty buffer, ALL threshold, and bounded size', () => {
const appender = new BufferLogAppender(); const appender = new BufferLogAppender()
expect(appender.buffer).toEqual([]); expect(appender.buffer).toEqual([])
expect(appender.threshold).toBe(LogLevel.ALL); expect(appender.threshold).toBe(LogLevel.ALL)
}); expect(appender.size).toEqual({ target: 1000, max: 1200 })
})
test("accepts initial buffer", () => { test('accepts threshold and size constructor options', () => {
const existing: LogMessage[] = [makeMsg({ msg: "pre-existing" })]; const appender = new BufferLogAppender(LogLevel.WARN, {
const appender = new BufferLogAppender(existing); target: 20,
max: 30,
})
expect(appender.buffer.length).toBe(1); expect(appender.threshold).toBe(LogLevel.WARN)
expect(appender.buffer[0].msg).toBe("pre-existing"); expect(appender.size).toEqual({ target: 20, max: 30 })
}); })
test("appends messages to buffer", () => { test('appends messages to buffer', () => {
const appender = new BufferLogAppender(); const appender = new BufferLogAppender()
appender.appendMessage(makeMsg({ msg: "first" })); appender.appendMessage(makeMsg({ msg: 'first' }))
appender.appendMessage(makeMsg({ msg: "second" })); appender.appendMessage(makeMsg({ msg: 'second' }))
expect(appender.buffer.length).toBe(2); expect(appender.buffer.length).toBe(2)
expect(appender.buffer[0].msg).toBe("first"); expect(appender.buffer[0].msg).toBe('first')
expect(appender.buffer[1].msg).toBe("second"); expect(appender.buffer[1].msg).toBe('second')
}); })
test("defaults to an unbounded buffer", () => { test('respects threshold', () => {
const appender = new BufferLogAppender(); const appender = new BufferLogAppender(LogLevel.WARN)
appender.appendMessage(makeMsg({ msg: "first" })); appender.appendMessage(makeMsg({ level: LogLevel.INFO, msg: 'dropped' }))
appender.appendMessage(makeMsg({ msg: "second" })); appender.appendMessage(makeMsg({ level: LogLevel.WARN, msg: 'kept' }))
appender.appendMessage(makeMsg({ msg: "third" })); appender.appendMessage(makeMsg({ level: LogLevel.ERROR, msg: 'also kept' }))
expect(appender.buffer.length).toBe(3); expect(appender.buffer.length).toBe(2)
expect(appender.buffer.map((message) => message.msg)).toEqual([ expect(appender.buffer[0].msg).toBe('kept')
"first", expect(appender.buffer[1].msg).toBe('also kept')
"second", })
"third",
]);
});
test("trims oldest messages when bufferMax is exceeded", () => { test('threshold of ALL does not drop messages', () => {
const appender = new BufferLogAppender(); const appender = new BufferLogAppender(LogLevel.ALL)
appender.bufferMax = 2;
appender.appendMessage(makeMsg({ msg: "first" })); appender.appendMessage(makeMsg({ level: LogLevel.ALL, msg: 'level all' }))
appender.appendMessage(makeMsg({ msg: "second" })); appender.appendMessage(makeMsg({ level: LogLevel.INFO, msg: 'level info' }))
appender.appendMessage(makeMsg({ msg: "third" }));
expect(appender.buffer.length).toBe(2); expect(appender.buffer.length).toBe(2)
expect(appender.buffer.map((message) => message.msg)).toEqual([ expect(appender.buffer[0].msg).toBe('level all')
"second", expect(appender.buffer[1].msg).toBe('level info')
"third", })
]);
});
test("respects threshold", () => { test('clearBuffer empties the buffer and reassigns the array', () => {
const appender = new BufferLogAppender(undefined, LogLevel.WARN); const appender = new BufferLogAppender()
appender.appendMessage(makeMsg({ msg: 'to clear' }))
const existing = appender.buffer
appender.appendMessage(makeMsg({ level: LogLevel.INFO, msg: "dropped" })); appender.clearBuffer()
appender.appendMessage(makeMsg({ level: LogLevel.WARN, msg: "kept" }));
appender.appendMessage(makeMsg({ level: LogLevel.ERROR, msg: "also kept" }));
expect(appender.buffer.length).toBe(2); expect(appender.buffer).toEqual([])
expect(appender.buffer[0].msg).toBe("kept"); expect(appender.buffer).not.toBe(existing)
expect(appender.buffer[1].msg).toBe("also kept"); })
});
test("threshold of ALL (0) does not cause falsy check to drop messages", () => { describe('size bounds', () => {
const appender = new BufferLogAppender(undefined, LogLevel.ALL); test('derives max as 120 percent of target by default', () => {
const appender = new BufferLogAppender(LogLevel.ALL, { target: 20 })
appender.appendMessage(makeMsg({ level: LogLevel.ALL, msg: "level all" })); expect(appender.size).toEqual({ target: 20, max: 24 })
appender.appendMessage(makeMsg({ level: LogLevel.INFO, msg: "level info" })); })
// Both should be kept — ALL (0) should not be treated as falsy test('keeps max at least one greater than target', () => {
expect(appender.buffer.length).toBe(2); const appender = new BufferLogAppender(LogLevel.ALL, {
expect(appender.buffer[0].msg).toBe("level all"); target: 20,
expect(appender.buffer[1].msg).toBe("level info"); max: 10,
}); })
test("clearBuffer empties the buffer", () => { expect(appender.size).toEqual({ target: 20, max: 21 })
const appender = new BufferLogAppender(); })
appender.appendMessage(makeMsg({ msg: "to clear" }));
expect(appender.buffer.length).toBe(1);
appender.clearBuffer(); test('normalizes fractional and too-small target values', () => {
expect(appender.buffer.length).toBe(0); const appender = new BufferLogAppender(LogLevel.ALL, { target: 0.5 })
});
}); expect(appender.size).toEqual({ target: 1, max: 2 })
})
test('normalizes fractional max values', () => {
const appender = new BufferLogAppender(LogLevel.ALL, {
target: 3,
max: 4.9,
})
expect(appender.size).toEqual({ target: 3, max: 4 })
})
test('can be disabled for unbounded buffering', () => {
const appender = new BufferLogAppender(LogLevel.ALL, {
target: 2,
max: 3,
})
appender.size = undefined
for (let i = 1; i <= 5; i++) {
appender.appendMessage(makeMsg({ msg: `message ${i}` }))
}
expect(appender.size).toBeUndefined()
expect(appender.buffer.map((message) => message.msg)).toEqual([
'message 1',
'message 2',
'message 3',
'message 4',
'message 5',
])
})
test('returns size as a defensive copy', () => {
const appender = new BufferLogAppender(LogLevel.ALL, {
target: 3,
max: 4,
})
const size = appender.size
if (size !== undefined) {
size.max = 1
}
expect(appender.size).toEqual({ target: 3, max: 4 })
})
})
describe('buffer trimming', () => {
test('allows buffer to grow until max is exceeded', () => {
const appender = new BufferLogAppender(LogLevel.ALL, {
target: 3,
max: 5,
})
for (let i = 1; i <= 5; i++) {
appender.appendMessage(makeMsg({ msg: `message ${i}` }))
}
expect(appender.buffer.map((message) => message.msg)).toEqual([
'message 1',
'message 2',
'message 3',
'message 4',
'message 5',
])
})
test('batch trims to target when max is exceeded', () => {
const appender = new BufferLogAppender(LogLevel.ALL, {
target: 3,
max: 5,
})
for (let i = 1; i <= 6; i++) {
appender.appendMessage(makeMsg({ msg: `message ${i}` }))
}
expect(appender.buffer.map((message) => message.msg)).toEqual([
'message 4',
'message 5',
'message 6',
])
})
test('reassigns buffer array when trimming', () => {
const appender = new BufferLogAppender(LogLevel.ALL, {
target: 2,
max: 3,
})
appender.appendMessage(makeMsg({ msg: 'first' }))
appender.appendMessage(makeMsg({ msg: 'second' }))
appender.appendMessage(makeMsg({ msg: 'third' }))
const existing = appender.buffer
appender.appendMessage(makeMsg({ msg: 'fourth' }))
expect(appender.buffer).not.toBe(existing)
expect(appender.buffer.map((message) => message.msg)).toEqual([
'third',
'fourth',
])
})
test('repairs invalid internal bounds before trimming', () => {
const appender = new BufferLogAppender(LogLevel.ALL, {
target: 3,
max: 4,
})
const unsafeAppender = appender as unknown as {
_size: { target: number, max: number }
}
unsafeAppender._size = { target: 3, max: 2 }
appender.appendMessage(makeMsg({ msg: 'first' }))
expect(appender.size).toEqual({ target: 3, max: 4 })
expect(appender.buffer.length).toBe(2)
expect(appender.buffer[1].level).toBe(LogLevel.ERROR)
expect(appender.buffer[1].msg).toMatchObject({
oldSize: { target: 3, max: 2 },
newSize: { target: 3, max: 4 },
})
})
})
})
+59
View File
@@ -0,0 +1,59 @@
import { describe, expect, test } from 'bun:test'
import { clamp } from '../src/util'
describe('utils', () => {
describe('clamp', () => {
test("doesn't alter integers within the given range", () => {
expect(clamp(5, { min: 0, max: 10 })).toBe(5)
expect(clamp(-8, { min: -100, max: 100 })).toBe(-8)
})
test('keeps values at the range boundaries', () => {
expect(clamp(0, { min: 0, max: 10 })).toBe(0)
expect(clamp(10, { min: 0, max: 10 })).toBe(10)
expect(clamp(-100, { min: -100, max: 100 })).toBe(-100)
expect(clamp(100, { min: -100, max: 100 })).toBe(100)
})
test('raises values below the minimum', () => {
expect(clamp(-1, { min: 0, max: 10 })).toBe(0)
expect(clamp(-101, { min: -100, max: 100 })).toBe(-100)
})
test('lowers values above the maximum', () => {
expect(clamp(11, { min: 0, max: 10 })).toBe(10)
expect(clamp(101, { min: -100, max: 100 })).toBe(100)
})
test('floors floats by default', () => {
expect(clamp(1.5, { min: 0, max: 2 })).toBe(1)
expect(clamp(1.999, { min: 0, max: 2 })).toBe(1)
expect(clamp(-0.3, { min: -2, max: 2 })).toBe(-1)
expect(clamp(-1.1, { min: -2, max: 2 })).toBe(-2)
})
test('floors before applying bounds', () => {
expect(clamp(10.5, { min: 0, max: 10 })).toBe(10)
expect(clamp(-10.5, { min: -10, max: 10 })).toBe(-10)
expect(clamp(1.2, { min: 1.5 })).toBe(1.5)
})
test('preserves floats when allowed', () => {
expect(clamp(1.5, { min: 0, max: 2 }, true)).toBe(1.5)
expect(clamp(2.5, { min: 0, max: 2 }, true)).toBe(2)
expect(clamp(-0.5, { min: 0, max: 2 }, true)).toBe(0)
})
test('supports one-sided bounds', () => {
expect(clamp(-1, { min: 0 })).toBe(0)
expect(clamp(11, { min: 0 })).toBe(11)
expect(clamp(-1, { max: 10 })).toBe(-1)
expect(clamp(11, { max: 10 })).toBe(10)
})
test('clamps infinite values to the range boundaries', () => {
expect(clamp(Infinity, { min: 0, max: 10 })).toBe(10)
expect(clamp(-Infinity, { min: 0, max: 10 })).toBe(0)
})
})
})