Skip to content

Add tests/idle-close.test.ts and tests/reply-token.test.ts #8

Description

@harleyjj

Labels: tests, good first issue

Summary

The initial test suite covers webhook signatures, push tools, the session registry, the extension hooks contract, and manifest validity. Two paths still need direct tests:

  1. Idle thread close — the idle-close job runs every 15 minutes and closes any open LINE thread whose lastActivityAt is older than idleCloseMinutes. Tested transitively in extensions.test.ts (close path) but not directly.
  2. Reply-token cache — when an inbound message arrives, the plugin caches the reply token against the resulting Paperclip comment. Agents call line.ack_with_reply_token with a commentId and the plugin uses the cached token. Cache entries expire after replyTokenMaxAgeSeconds and are GC'd by the reply-token-gc job.

What to implement

tests/idle-close.test.ts

test("idle close: closes threads older than idleCloseMinutes", async () => {
  const { ctx, harness } = createHarness({
    config: { idleCloseMinutes: 30 },
  });
  // Seed a thread with lastActivityAt = 35 minutes ago
  await harness.seed.threadSession({
    issueId: "issue-1",
    lastActivityAt: minutesAgo(35),
    status: "open",
  });
  await harness.runJob("idle-close");
  const thread = await harness.getState.threadSession("issue-1");
  expect(thread.status).toBe("closed");
  expect(thread.closeReason).toBe("idle");
  expect(harness.metrics).toContainEqual({ name: "line.idle_close.closed", value: 1 });
});

test("idle close: leaves fresh threads alone", async () => {
  // Seed thread with lastActivityAt = 10 minutes ago, idleCloseMinutes = 30
  // Run idle-close. Expect thread still open.
});

test("idle close: invokes onCloseThread extension hook", async () => {
  const onCloseThread = vi.fn();
  setExtensions({ onCloseThread });
  // Seed an idle thread, run idle-close, assert onCloseThread called with reason="idle"
});

tests/reply-token.test.ts

test("reply token: cached on inbound message, used by line.ack_with_reply_token", async () => {
  // POST a signed webhook with an inbound text message and a reply token.
  // Assert: reply-token cache populated, indexed by the resulting commentId.
  // Call line.ack_with_reply_token with that commentId.
  // Assert: LINE reply API called with the cached token.
});

test("reply token: expired tokens return token_expired", async () => {
  // Seed a cached reply token with capturedAt = 90 seconds ago, replyTokenMaxAgeSeconds = 60.
  // Call line.ack_with_reply_token. Assert error result with reason "reply_token_expired".
});

test("reply-token-gc: removes expired cache entries", async () => {
  // Seed two reply tokens: one fresh, one expired.
  // Run reply-token-gc job.
  // Assert: only the fresh one remains.
});

The existing tests use a createHarness helper from @paperclipai/plugin-sdk/testing. Use the same.

Acceptance criteria

  • Two new test files
  • At least 6 tests total across them
  • All tests pass alongside the existing suite
  • No new production code needed — these test existing behavior

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions