Skip to content

[Feature]: Add comprehensive test suite for core engine, rules, evaluator, and store #4

@yash194

Description

@yash194

Problem

OpenAlerts currently has zero test coverage. The core engine, all 8 alert rules, the evaluator, the JSONL store, the event bus, the formatter, and the bounded map are all untested. This makes it risky to accept contributions or refactor internals there's no safety net to catch regressions.

The publish CI workflow also has no test step, so broken code can ship to npm undetected.

Proposal

Add a comprehensive test suite using Vitest (fast, TypeScript-native, zero-config for ESM projects). The suite should cover the framework-agnostic src/core/ layer the part that every adapter depends on.

Proposed test files

File Covers
tests/rules.test.ts All 8 alert rules in isolation
tests/evaluator.test.ts processEvent(), warmFromHistory(), stats tracking, hourly cap
tests/store.test.ts JSONL append, read, prune (age + size), atomic write
tests/engine.test.ts OpenAlertsEngine lifecycle — start, stop, ingest → alert flow
tests/formatter.test.ts /health and /alerts output formatting
tests/bounded-map.test.ts LRU eviction, TTL expiry, hit/miss stats
tests/event-bus.test.ts Pub/sub, listener error isolation

Key test cases for rules (rules.test.ts)

llm-errors

  • Fires after ≥1 error-outcome llm.call event in a 1-minute window
  • Does NOT fire for llm.call with outcome: "success" or undefined
  • Fires for llm.error and agent.error event types (always treated as errors)
  • Respects configurable threshold override

gateway-down

  • Only evaluates on watchdog.tick events (ignores all other types)
  • Returns null when no heartbeat has ever been received (lastHeartbeatTs === 0)
  • Fires when silence exceeds threshold (default 30s)
  • Severity is critical

high-error-rate

  • Does NOT fire until at least 20 messages have been tracked
  • Calculates rate over the last 20 messages only (sliding window)
  • Fires at ≥50% error rate by default
  • Counts timeout outcomes as errors

queue-depth

  • Updates lastHeartbeatTs on infra.heartbeat events regardless of whether the rule itself is enabled (gateway-down depends on this)
  • Fires when queueDepth ≥ threshold

Cross-cutting (evaluator)

  • Cooldown prevents the same fingerprint from firing twice within the cooldown window
  • Hourly cap (default 5) blocks the 6th alert in the same hour
  • Hourly cap resets after the hour expires
  • Cooldown map is pruned when it exceeds maxCooldownEntries (50)
  • 24h stats reset after 24 hours
  • warmFromHistory() restores cooldowns from persisted alert events
  • warmFromHistory() does NOT replay diagnostic events through rules

Setup

npm install -D vitest

Add to package.json:

{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest"
  }
}

Add to tsconfig.json (or a tsconfig.test.json):

{
  "exclude": ["tests"]  // don't emit test files to dist/
}

I'd like to work on this , happy to submit a PR if this fits in. Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions