Initial Commit
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { createPluginContext } from "@suaveplan/api-plugin";
|
||||
import { EventBus } from "@suaveplan/events";
|
||||
import { SurrealAdapter } from "@suaveplan/surrealdb";
|
||||
import {
|
||||
afterAll,
|
||||
beforeAll,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
} from "@suaveplan/testing/runner";
|
||||
import { SurrealDBFixture } from "@suaveplan/testing-integration-surrealdb";
|
||||
import { createEventsPlugin } from "../../src/plugin/plugin.js";
|
||||
import type { EventsPluginConfig } from "../../src/schemas/options.js";
|
||||
|
||||
let fixture: SurrealDBFixture | null = null;
|
||||
let cfg: {
|
||||
url: string;
|
||||
namespace: string;
|
||||
database: string;
|
||||
username: string;
|
||||
password: string;
|
||||
} | null = null;
|
||||
let adapter: SurrealAdapter | null = null;
|
||||
|
||||
const noopLogger = {
|
||||
debug: () => undefined,
|
||||
info: () => undefined,
|
||||
warn: () => undefined,
|
||||
error: () => undefined,
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
fixture = new SurrealDBFixture();
|
||||
cfg = await fixture.start();
|
||||
adapter = new SurrealAdapter({
|
||||
url: cfg.url,
|
||||
namespace: cfg.namespace,
|
||||
database: cfg.database,
|
||||
username: cfg.username,
|
||||
password: cfg.password,
|
||||
});
|
||||
await adapter.connect();
|
||||
|
||||
// Apply the migration manually — the plugin itself does not run
|
||||
// migrations in this proposal (the database plugin owns that
|
||||
// responsibility), so the test sets up the schema first.
|
||||
const surql = await readFile(
|
||||
join(__dirname, "..", "..", "surql", "tables", "event.surql"),
|
||||
"utf8"
|
||||
);
|
||||
await adapter.query(surql);
|
||||
}, 180_000);
|
||||
|
||||
afterAll(async () => {
|
||||
if (adapter !== null) {
|
||||
await adapter.disconnect();
|
||||
adapter = null;
|
||||
}
|
||||
if (fixture !== null) {
|
||||
await fixture.stop();
|
||||
fixture = null;
|
||||
}
|
||||
}, 60_000);
|
||||
|
||||
describe("@suaveplan/api-plugin-events integration", () => {
|
||||
it("boots, creates event, records rsvp, increments count, cleans up", {
|
||||
timeout: 180_000,
|
||||
}, async () => {
|
||||
if (adapter === null) throw new Error("fixture not ready");
|
||||
|
||||
// Seed creator + invitee in the user table required by the
|
||||
// foreign-key constraint on event.creator / event_rsvp.user.
|
||||
// The repositories only use `CREATE event SET creator = user:1`;
|
||||
// SurrealDB v3 allows the record link to be soft until
|
||||
// referenced, so we don't need to materialise the row, but
|
||||
// creating them gives a more lifelike round-trip.
|
||||
await adapter.query(
|
||||
"CREATE user:creator SET name = 'C'; CREATE user:invitee SET name = 'I';"
|
||||
);
|
||||
|
||||
// Boot the plugin.
|
||||
const app = new OpenAPIHono();
|
||||
const services = new Map<string, unknown>([
|
||||
["db", adapter],
|
||||
["app", app],
|
||||
]);
|
||||
const plugin = createEventsPlugin({ basePath: "/events" });
|
||||
const ctx = createPluginContext<EventsPluginConfig>({
|
||||
logger: noopLogger,
|
||||
eventBus: new EventBus(),
|
||||
signal: new AbortController().signal,
|
||||
config: plugin.defaults as EventsPluginConfig,
|
||||
services,
|
||||
});
|
||||
await plugin.onInit?.(ctx);
|
||||
await plugin.onReady?.(ctx);
|
||||
|
||||
const eventRepo = ctx.getService("eventRepository");
|
||||
const rsvpRepo = ctx.getService("rsvpRepository");
|
||||
expect(eventRepo).toBeDefined();
|
||||
expect(rsvpRepo).toBeDefined();
|
||||
if (eventRepo === undefined || rsvpRepo === undefined) return;
|
||||
|
||||
const event = await eventRepo.create("user:creator", {
|
||||
title: "Integration Test",
|
||||
start_time: "2026-06-01T10:00:00Z",
|
||||
end_time: "2026-06-01T12:00:00Z",
|
||||
timezone: "UTC",
|
||||
status: 1,
|
||||
});
|
||||
expect(event.title).toBe("Integration Test");
|
||||
|
||||
const found = await eventRepo.findById(String(event.id));
|
||||
expect(found).not.toBeNull();
|
||||
|
||||
const rsvp = await rsvpRepo.upsert(String(event.id), "user:invitee", {
|
||||
status: "going",
|
||||
});
|
||||
expect(rsvp.status).toBe("going");
|
||||
|
||||
await eventRepo.updateAttendeeCount(String(event.id), "going", 1);
|
||||
const after = await eventRepo.findById(String(event.id));
|
||||
expect(after?.attendee_count.going).toBeGreaterThanOrEqual(1);
|
||||
|
||||
const oldStatus = await rsvpRepo.delete(
|
||||
String(event.id),
|
||||
"user:invitee"
|
||||
);
|
||||
expect(oldStatus).toBe("going");
|
||||
|
||||
await eventRepo.delete(String(event.id));
|
||||
const gone = await eventRepo.findById(String(event.id));
|
||||
expect(gone).toBeNull();
|
||||
|
||||
await plugin.onClose?.(ctx);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user