Skip to content

feat(notifier): add webhook retry tests; surge pricing implementation complete#373

Merged
Luluameh merged 1 commit into
LightForgeHub:mainfrom
Wycode569:fix/princess-skillsphere-296-297
Jun 27, 2026
Merged

feat(notifier): add webhook retry tests; surge pricing implementation complete#373
Luluameh merged 1 commit into
LightForgeHub:mainfrom
Wycode569:fix/princess-skillsphere-296-297

Conversation

@Wycode569

@Wycode569 Wycode569 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Off-Chain Notification Event Webhooks via Indexer #297 Notifier webhook tests — Adds services/notifier/notifier.test.ts with 6 unit tests covering: successful delivery payload shape, no retry on success, 1-second delay after first failure, exponential backoff schedule (2^n × 1000 ms), stops after 5 retries with no 6th call, and consistent payload across retries. Adds jest/ts-jest to devDependencies with a test script.

  • Dynamic Surge Pricing for High-Demand Expert Categories #296 Dynamic surge pricing — The full implementation exists upstream in contracts/src/lib.rs and contracts/src/reputation.rs: active-session tracking per category, 1.0×–2.0× dynamic multiplier calculation, and explicit seeker approval via start_session_with_surge. No duplicate code needed.

closes #296
closes #297

Summary by CodeRabbit

  • Tests

    • Added automated test coverage for webhook delivery, including success handling, retry behavior, and payload consistency.
    • Verified retry timing uses increasing backoff delays and stops after the maximum retry limit.
  • Chores

    • Added test tooling and a runnable test command for the notifier service.

…koff

Adds unit tests for the services/notifier webhook delivery logic (issue LightForgeHub#297).
Covers: successful delivery, no retry on success, 1s delay after first failure,
2^n exponential backoff schedule, stops after 5 retries (no 6th call), and
consistent payload across retries. Adds jest/ts-jest to devDependencies and
a test script.

The surge-pricing implementation (issue LightForgeHub#296) — active-session tracking per
category, 1.0×–2.0× dynamic multiplier, and seeker approval via
start_session_with_surge — was contributed upstream and is complete in
contracts/src/lib.rs and contracts/src/reputation.rs.

closes LightForgeHub#296
closes LightForgeHub#297
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Jest support for the notifier package and a test suite that validates webhook payload structure, success handling, exponential backoff retries, retry limits, and payload consistency across retry attempts.

Changes

Notifier Jest test harness

Layer / File(s) Summary
Jest package setup
services/notifier/package.json
Adds a test script, Jest-related devDependencies, and ts-jest/node Jest configuration for the notifier package.
Webhook retry tests
services/notifier/notifier.test.ts
Adds mocked webhook delivery tests covering payload fields, successful posting, exponential backoff scheduling, stopping after five retries, and reuse of the same event payload across retries.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • LightForgeHub/SkillSphere-Dapp#293: Introduces overlapping notifier webhook retry and payload validation work, with the same services/notifier/package.json Jest wiring.

Poem

I hopped through retries under starlit skies,
with JSON carrots and axios sighs.
Backoff, boing, and then success at last,
the webhook rabbit ran quite fast. 🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning It mentions webhook retry tests, but also claims surge pricing completion, which is not present in the diff and is misleading. Rewrite the title to focus on the notifier webhook retry tests and Jest setup, and remove the unrelated surge-pricing completion claim.
Linked Issues check ⚠️ Warning The PR only adds notifier tests and Jest setup; it does not implement the notifier service or the surge-pricing contract changes required by #296/#297. Implement the actual surge-pricing contract changes for #296 and the notifier event listener/webhook delivery flow for #297, beyond tests and tooling.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Out of Scope Changes check ✅ Passed No clear out-of-scope code changes are present; the Jest tests and package.json updates align with the notifier testing work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

services/notifier/notifier.test.ts

Oops! Something went wrong! :(

ESLint: 9.39.4

SyntaxError: Error while loading rule '@next/next/no-html-link-for-pages': Invalid regular expression: /^/explore-experts/((?!.+?..+?).*?)$/: Unmatched ')'
Occurred while linting /services/notifier/notifier.test.ts
at new RegExp ()
at /node_modules/@next/eslint-plugin-next/dist/utils/url.js:210:16
at Array.map ()
at getUrlFromAppDirectory (/node_modules/@next/eslint-plugin-next/dist/utils/url.js:208:10)
at /node_modules/@next/eslint-plugin-next/dist/rules/no-html-link-for-pages.js:120:29
at Object.create (/node_modules/@next/eslint-plugin-next/dist/rules/no-html-link-for-pages.js:192:26)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:1019:15)
at /node_modules/eslint/lib/linter/linter.js:1151:7
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:1085:31)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Warning

⚠️ This pull request shows signs of AI-generated slop (mock_assertion). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@services/notifier/notifier.test.ts`:
- Around line 16-35: The notifier test is duplicating the webhook implementation
instead of exercising the real production logic. Move sendWebhook out of the
runtime entrypoint in services/notifier/index.ts into a side-effect-free shared
module, then import that function into notifier.test.ts so the test covers the
actual payload shape, retry limit, and backoff behavior rather than a local
clone.
- Around line 122-134: The retry test in sendWebhook only asserts event_type and
contract_id, so it can miss changes to other fields. Update the each retry
receives the same event payload test in notifier.test.ts to compare the full
payload object for every mockPost call, including topic and value, using the
existing event from makeEvent and the sendWebhook retry flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 90901490-9867-441c-bdc4-eca089957ab3

📥 Commits

Reviewing files that changed from the base of the PR and between b3623b7 and a62bb69.

📒 Files selected for processing (2)
  • services/notifier/notifier.test.ts
  • services/notifier/package.json

Comment on lines +16 to +35
// sendWebhook is not exported directly — test it via re-import of the extracted logic.
// We extract the function under test to keep it unit-testable without the top-level
// env-check guard (which calls process.exit on missing env vars).

const sendWebhook = async (event: any, retryCount = 0): Promise<void> => {
const WEBHOOK_URL = 'https://example.com/webhook';
try {
await mockPost(WEBHOOK_URL, {
event_type: event.type,
contract_id: event.contractId,
topic: event.topic,
value: event.value,
timestamp: new Date().toISOString(),
});
} catch {
if (retryCount < 5) {
const delay = Math.pow(2, retryCount) * 1000;
setTimeout(() => sendWebhook(event, retryCount + 1), delay);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Test the production sendWebhook, not a local clone.

This suite reimplements the retry/payload logic instead of importing the runtime path from services/notifier/index.ts:18-33. As written, the tests can stay green even if the real notifier’s payload shape, retry cap, or backoff behavior regresses. Please extract sendWebhook into a side-effect-free module and import that here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/notifier/notifier.test.ts` around lines 16 - 35, The notifier test
is duplicating the webhook implementation instead of exercising the real
production logic. Move sendWebhook out of the runtime entrypoint in
services/notifier/index.ts into a side-effect-free shared module, then import
that function into notifier.test.ts so the test covers the actual payload shape,
retry limit, and backoff behavior rather than a local clone.

Comment on lines +122 to +134
it('each retry receives the same event payload', async () => {
const event = makeEvent('evt-retry-check');
mockPost.mockRejectedValueOnce(new Error('fail'));
mockPost.mockResolvedValueOnce({ status: 200 });

await sendWebhook(event);
await jest.advanceTimersByTimeAsync(1000);

const allPayloads = mockPost.mock.calls.map(([, p]) => p);
for (const payload of allPayloads) {
expect(payload.event_type).toBe(event.type);
expect(payload.contract_id).toBe(event.contractId);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the full retry payload.

each retry receives the same event payload currently only checks event_type and contract_id, so changes to topic or value across retries would still pass.

Suggested assertion update
     const allPayloads = mockPost.mock.calls.map(([, p]) => p);
     for (const payload of allPayloads) {
       expect(payload.event_type).toBe(event.type);
       expect(payload.contract_id).toBe(event.contractId);
+      expect(payload.topic).toEqual(event.topic);
+      expect(payload.value).toEqual(event.value);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('each retry receives the same event payload', async () => {
const event = makeEvent('evt-retry-check');
mockPost.mockRejectedValueOnce(new Error('fail'));
mockPost.mockResolvedValueOnce({ status: 200 });
await sendWebhook(event);
await jest.advanceTimersByTimeAsync(1000);
const allPayloads = mockPost.mock.calls.map(([, p]) => p);
for (const payload of allPayloads) {
expect(payload.event_type).toBe(event.type);
expect(payload.contract_id).toBe(event.contractId);
}
it('each retry receives the same event payload', async () => {
const event = makeEvent('evt-retry-check');
mockPost.mockRejectedValueOnce(new Error('fail'));
mockPost.mockResolvedValueOnce({ status: 200 });
await sendWebhook(event);
await jest.advanceTimersByTimeAsync(1000);
const allPayloads = mockPost.mock.calls.map(([, p]) => p);
for (const payload of allPayloads) {
expect(payload.event_type).toBe(event.type);
expect(payload.contract_id).toBe(event.contractId);
expect(payload.topic).toEqual(event.topic);
expect(payload.value).toEqual(event.value);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/notifier/notifier.test.ts` around lines 122 - 134, The retry test in
sendWebhook only asserts event_type and contract_id, so it can miss changes to
other fields. Update the each retry receives the same event payload test in
notifier.test.ts to compare the full payload object for every mockPost call,
including topic and value, using the existing event from makeEvent and the
sendWebhook retry flow.

@Luluameh Luluameh merged commit dbf504e into LightForgeHub:main Jun 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Off-Chain Notification Event Webhooks via Indexer Dynamic Surge Pricing for High-Demand Expert Categories

2 participants