Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions indexer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ An optional indexer that polls Soroban RPC and persists contract deployments, in
cd indexer
npm install

# Set the database connection string
# Set the required database connection string and Soroban RPC endpoint
export DATABASE_URL="postgresql://user:password@localhost:5432/prism"
export SOROBAN_RPC_URL="https://soroban-testnet.stellar.org"

# Generate the Prisma client and run migrations
npm run prisma:generate
Expand Down Expand Up @@ -38,7 +39,7 @@ docker compose up
| Environment Variable | Default | Description |
| -------------------- | ------------------------------------ | ------------------------------------ |
| `DATABASE_URL` | *(required)* | PostgreSQL connection string |
| `SOROBAN_RPC_URL` | `https://soroban-testnet.stellar.org` | Soroban RPC endpoint to poll |
| `SOROBAN_RPC_URL` | *(required)* | Soroban RPC endpoint to poll |
| `POLL_INTERVAL_MS` | `5000` | Milliseconds between poll cycles |
| `HEALTH_PORT` | `8080` | Port for the `GET /healthz` endpoint |
| `HEALTH_MAX_LEDGER_LAG` | `60` | Ledgers behind the network before `/healthz` returns 503 |
Expand Down
39 changes: 37 additions & 2 deletions indexer/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { describe, expect, it } from "vitest";
import { backoffDelay, commitPage, healthReport, seedLastLedger } from "./index.js";
import { describe, expect, it, vi } from "vitest";

vi.hoisted(() => {
process.env.SOROBAN_RPC_URL ??= "https://soroban-testnet.stellar.org";
});

import { backoffDelay, commitPage, healthReport, requireEnv, seedLastLedger } from "./index.js";

describe("backoffDelay", () => {
it("doubles the delay for each consecutive failure", () => {
Expand Down Expand Up @@ -127,3 +132,33 @@ describe("commitPage", () => {
expect(committed.cursor).toEqual({ lastLedger: 100, paginationCursor: null });
});
});


describe("requireEnv", () => {
it("returns the configured environment variable when set", () => {
expect(
requireEnv("SOROBAN_RPC_URL", undefined, {
SOROBAN_RPC_URL: "https://soroban-testnet.stellar.org",
})
).toBe("https://soroban-testnet.stellar.org");
});

it("logs the fatal error and exits with code 1 when SOROBAN_RPC_URL is unset or blank", () => {
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const exitSpy = vi.spyOn(process, "exit").mockImplementation(((code?: number) => {
throw new Error(`process.exit(${code})`);
}) as never);

try {
for (const env of [{}, { SOROBAN_RPC_URL: " " }]) {
expect(() => requireEnv("SOROBAN_RPC_URL", undefined, env)).toThrow("process.exit(1)");
expect(errorSpy).toHaveBeenCalledWith(
"[indexer] fatal: environment variable SOROBAN_RPC_URL is required but not set"
);
}
} finally {
errorSpy.mockRestore();
exitSpy.mockRestore();
}
});
});
11 changes: 8 additions & 3 deletions indexer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ import {
// ---------------------------------------------------------------------------
// Environment variable validation (#20)
// ---------------------------------------------------------------------------
function requireEnv(name: string, fallback?: string): string {
const val = process.env[name] ?? fallback;
export function requireEnv(
name: string,
fallback?: string,
env: NodeJS.ProcessEnv = process.env
): string {
const raw = env[name]?.trim();
const val = raw ? raw : fallback;
if (val === undefined) {
console.error(`[indexer] fatal: environment variable ${name} is required but not set`);
process.exit(1);
Expand All @@ -36,7 +41,7 @@ function requirePositiveInt(name: string, fallback: number): number {
return n;
}

const RPC_URL = requireEnv("SOROBAN_RPC_URL", "https://soroban-testnet.stellar.org");
const RPC_URL = requireEnv("SOROBAN_RPC_URL");
const POLL_INTERVAL_MS = requirePositiveInt("POLL_INTERVAL_MS", 5000);
const HEALTH_PORT = requirePositiveInt("HEALTH_PORT", 8080);
const MAX_LEDGER_LAG = requirePositiveInt("HEALTH_MAX_LEDGER_LAG", 60);
Expand Down