refactor(server): plan 4 — inline canAccessTrip/isOwner off DatabaseService (plugins + MCP surfaces)

This commit is contained in:
jubnl
2026-09-24 05:47:29 +02:00
parent 51327e75c0
commit ea7b93de5d
51 changed files with 232 additions and 179 deletions
@@ -16,7 +16,7 @@ import { BucketItemExistsError } from '../../../src/nest/atlas/atlas.service';
import { VacayRpc } from '../../../src/nest/vacay/vacay.rpc';
import { JournalRpc } from '../../../src/nest/journey/journal.rpc';
import { CollectionsRpc } from '../../../src/nest/collections/collections.rpc';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
import type { PermissionsService } from '../../../src/nest/permissions/permissions.service';
import type { AddonsService } from '../../../src/nest/addons/addons.service';
import type { UsersRepository } from '../../../src/db/repositories/Users.repository';
@@ -39,9 +39,9 @@ function build(overrides: {
collections?: Record<string, unknown>;
} = {}) {
const db = {
canAccessTrip: vi.fn(async () => ({ id: 1, user_id: 42 })),
findAccessible: vi.fn(async () => ({ id: 1, user_id: 42 })),
prepare: vi.fn(() => ({ get: () => ({ role: 'user' }), all: () => [] })),
} as unknown as DatabaseService;
} as unknown as TripsRepository;
const guards = new PluginGuards(
db,
{ checkPermission: vi.fn(() => true) } as unknown as PermissionsService,
@@ -20,7 +20,7 @@ import { VacayRpc } from '../../../src/nest/vacay/vacay.rpc';
import { JournalRpc } from '../../../src/nest/journey/journal.rpc';
import { CollectionsRpc } from '../../../src/nest/collections/collections.rpc';
import { CostsRpc } from '../../../src/nest/budget/costs.rpc';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
import type { PermissionsService } from '../../../src/nest/permissions/permissions.service';
import type { AddonsService } from '../../../src/nest/addons/addons.service';
import type { UsersRepository } from '../../../src/db/repositories/Users.repository';
@@ -57,9 +57,9 @@ function spyService(calls: string[], name: string) {
function build(addonOn: boolean) {
const calls: string[] = [];
const db = {
canAccessTrip: vi.fn(async () => ({ id: 1, user_id: 42 })),
findAccessible: vi.fn(async () => ({ id: 1, user_id: 42 })),
prepare: vi.fn(() => ({ get: () => ({ role: 'user' }), all: () => [] })),
} as unknown as DatabaseService;
} as unknown as TripsRepository;
const guards = new PluginGuards(
db,
{ checkPermission: vi.fn(() => true) } as unknown as PermissionsService,
@@ -16,7 +16,7 @@ vi.mock('../../../src/db/database', () => ({
db: { prepare: () => ({ all: () => tripDays.value }) },
canAccessTrip,
}));
import { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { DayScheduleController } from '../../../src/nest/plugins/contributions/day-schedule.controller';
@@ -32,7 +32,7 @@ function controller(invoke: (id: string) => unknown, providers = ['p1']) {
} as unknown as PluginHooks;
// CT1 (Plan 3j Task 5) — the day-id-set read is now DaysRepository.listIdsByTrip.
const days = { listIdsByTrip: vi.fn(async () => tripDays.value.map((d) => d.id)) } as unknown as DaysRepository;
return { c: new DayScheduleController(runtime, { canAccessTrip } as unknown as DatabaseService, days), runtime };
return { c: new DayScheduleController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository, days), runtime };
}
const item = (over: Record<string, unknown> = {}) => ({ id: 's1', dayId: 10, label: 'Charging', ...over });
@@ -16,7 +16,7 @@ vi.mock('../../../src/db/database', () => ({
db: { prepare: () => ({ all: () => tripDays.value }) },
canAccessTrip,
}));
import { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { DayTintsController } from '../../../src/nest/plugins/contributions/day-tints.controller';
@@ -32,7 +32,7 @@ function controller(invoke: (id: string) => unknown, providers = ['p1']) {
} as unknown as PluginHooks;
// CT2 (Plan 3j Task 5) — the day-id-set read is now DaysRepository.listIdsByTrip.
const days = { listIdsByTrip: vi.fn(async () => tripDays.value.map((d) => d.id)) } as unknown as DaysRepository;
return { c: new DayTintsController(runtime, { canAccessTrip } as unknown as DatabaseService, days), runtime };
return { c: new DayTintsController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository, days), runtime };
}
const tint = (over: Record<string, unknown> = {}) => ({ dayId: 10, tone: 'success', ...over });
+13 -15
View File
@@ -65,7 +65,6 @@ import { UnreadableLlmResponse } from '../../../src/nest/llm-parse/clients/opena
import { getPluginDataDb, closePluginDataDb } from '../../../src/nest/plugins/host/plugin-host-state';
import { verifyChain } from '../../../src/nest/plugins/host/plugin-audit';
import { db as mockDb } from '../../../src/db/database';
import { DatabaseService } from '../../../src/nest/database/database.service';
import { RealtimeService } from '../../../src/nest/realtime/realtime.service';
import type { PermissionsService } from '../../../src/nest/permissions/permissions.service';
import type { AddonsService } from '../../../src/nest/addons/addons.service';
@@ -154,6 +153,13 @@ type RawDb = {
const raw = mockDb as unknown as RawDb;
const tripsRepo = {
// Plan 4 Task 2 — PluginGuards' own canAccessTrip delegate is now this
// repository's findAccessible directly (same access rule the old
// `db/database` mock factory above encoded: trip 1 belongs to user 5,
// user 6 is a member).
async findAccessible(tripId: number | string, userId: number) {
return Number(tripId) === 1 && (userId === 5 || userId === 6) ? { id: 1, user_id: 5, currency: null } : undefined;
},
async sharesTripWith(userIdA: number, userIdB: number) {
return !!raw
.prepare(
@@ -283,17 +289,6 @@ const scheduledTasksRepo = {
},
} as unknown as PluginScheduledTasksRepository;
const dbs = new DatabaseService(mockDb);
// Plan 3c Task 0b: `canAccessTrip` is `TripsRepository.findAccessible` now,
// which needs a real EntityManager over the full migrated schema — this
// file's `mockDb` is a hand-trimmed `:memory:` table set (no `currency`
// column on `trips`, among others), so a real repository read isn't
// possible against it. Spied directly with the exact access rule the old
// `db/database` mock factory above encoded (trip 1 belongs to user 5, user 6
// is a member), which is now dead code for this purpose.
vi.spyOn(dbs, 'canAccessTrip').mockImplementation(async (tripId, userId) =>
Number(tripId) === 1 && (userId === 5 || userId === 6) ? { id: 1, user_id: 5, currency: null } : undefined,
);
// Plan 3j Task 1: PluginGuards' own role lookup (PG3/PG4) now goes through
// UsersRepository.getRole, not `dbs.prepare(...)`. Same reasoning as the
// canAccessTrip spy above — a real UsersRepository would need a real
@@ -315,11 +310,14 @@ const usersRepo = {
);
},
} as unknown as UsersRepository;
const guards = new PluginGuards(dbs, permissions, addons, usersRepo);
// Plan 4 Task 2 — PluginGuards'/MetaRpc's/HostSurfaceRpc's own DatabaseService
// params are gone: canAccessTrip now reads through the TripsRepository each
// already injects (`tripsRepo` above, findAccessible-only).
const guards = new PluginGuards(tripsRepo, permissions, addons, usersRepo);
const registry = createTestPluginRegistry([
new DbRpc(userSettings),
new MetaRpc(dbs, guards, metaRepo, tripsRepo, placesRepo, daysRepo, reservationsRepo, dayAccommodationsRepo),
new HostSurfaceRpc(dbs, new RealtimeService(), notifications, llmConfig, oauth, guards, pluginAuditRepo, usersRepo, tripsRepo, scheduledTasksRepo),
new MetaRpc(guards, metaRepo, tripsRepo, placesRepo, daysRepo, reservationsRepo, dayAccommodationsRepo),
new HostSurfaceRpc(new RealtimeService(), notifications, llmConfig, oauth, guards, pluginAuditRepo, usersRepo, tripsRepo, scheduledTasksRepo),
]);
const factory = new PluginRpcHostFactory(pluginAuditRepo, registry as unknown as PluginRpcRegistryService);
const stubRouter: PluginCallRouter = { callPlugin: async () => undefined, emitPluginEvent: async () => {} };
@@ -16,7 +16,7 @@ vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { MapLayersController } from '../../../src/nest/plugins/contributions/map-layers.controller';
import type { PluginHooks } from '../../../src/nest/plugins/plugin-hooks.service';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const req = (id?: number) => ({ user: id === undefined ? undefined : { id } }) as any;
@@ -25,7 +25,7 @@ function controller(invoke: (id: string) => unknown, providers = ['p1']) {
providersOf: vi.fn(() => providers),
mapLayers: vi.fn(async (id: string) => invoke(id)),
} as unknown as PluginHooks;
return { c: new MapLayersController(runtime, { canAccessTrip } as unknown as DatabaseService), runtime };
return { c: new MapLayersController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository), runtime };
}
const line = (n = 3, over: Record<string, unknown> = {}) => ({
type: 'polyline',
@@ -15,7 +15,7 @@ vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { MapMarkersController } from '../../../src/nest/plugins/contributions/map-markers.controller';
import type { PluginHooks } from '../../../src/nest/plugins/plugin-hooks.service';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const req = (id?: number) => ({ user: id === undefined ? undefined : { id } }) as any;
@@ -24,7 +24,7 @@ function controller(invoke: (id: string) => unknown, providers = ['p1']) {
providersOf: vi.fn(() => providers),
mapMarkers: vi.fn(async (id: string) => invoke(id)),
} as unknown as PluginHooks;
return { c: new MapMarkersController(runtime, { canAccessTrip } as unknown as DatabaseService), runtime };
return { c: new MapMarkersController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository), runtime };
}
const mk = (over: Record<string, unknown> = {}) => ({ id: 'm1', lat: 48.85, lng: 2.35, ...over });
@@ -16,7 +16,7 @@ vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { PdfSectionsController } from '../../../src/nest/plugins/contributions/pdf-sections.controller';
import type { PluginHooks } from '../../../src/nest/plugins/plugin-hooks.service';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const req = (id?: number) => ({ user: id === undefined ? undefined : { id } }) as any;
@@ -25,7 +25,7 @@ function controller(invoke: (id: string) => unknown, providers = ['p1']) {
providersOf: vi.fn(() => providers),
pdfSections: vi.fn(async (id: string) => invoke(id)),
} as unknown as PluginHooks;
return { c: new PdfSectionsController(runtime, { canAccessTrip } as unknown as DatabaseService), runtime };
return { c: new PdfSectionsController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository), runtime };
}
const sec = (over: Record<string, unknown> = {}) => ({ title: 'Weather', ...over });
@@ -9,7 +9,7 @@ vi.mock('../../../src/db/database', () => ({
db: { prepare: () => ({ get: (placeId: number) => placeTrip(placeId) }) },
canAccessTrip,
}));
import { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { PlaceDetailsController } from '../../../src/nest/plugins/contributions/place-details.controller';
@@ -26,7 +26,7 @@ function controller(over: Partial<PluginHooks> = {}) {
} as unknown as PluginHooks;
// CT7 (Plan 3j Task 5) — the place's owning trip id is now Places.repository.ts#findTripId.
const places = { findTripId: vi.fn(async (placeId: number) => placeTrip(placeId)?.trip_id) } as unknown as PlacesRepository;
return { c: new PlaceDetailsController(runtime, { canAccessTrip } as unknown as DatabaseService, places), runtime };
return { c: new PlaceDetailsController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository, places), runtime };
}
describe('PlaceDetailsController', () => {
@@ -7,7 +7,7 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { PluginGuards } from '../../../src/nest/plugins/host/plugin-guards.service';
import { BadParams, ForbiddenResource } from '../../../src/nest/plugins/host/rpc-errors';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
import { PermissionsService } from '../../../src/nest/permissions/permissions.service';
import type { AddonsService } from '../../../src/nest/addons/addons.service';
import type { UsersRepository } from '../../../src/db/repositories/Users.repository';
@@ -47,10 +47,10 @@ const ctx = (actingUserId: number | undefined): PluginRpcContext => ({
*/
function build(overrides: { role?: string | null; allow?: boolean; addonOn?: boolean } = {}) {
const db = {
canAccessTrip: vi.fn(async (tripId: number, userId: number) =>
findAccessible: vi.fn(async (tripId: number, userId: number) =>
tripId === 1 && userId === 42 ? { id: 1, user_id: 42 } : undefined,
),
} as unknown as DatabaseService;
} as unknown as TripsRepository;
const users = {
getRole: vi.fn(async () => ('role' in overrides ? (overrides.role ?? null) : 'user')),
} as unknown as UsersRepository;
@@ -149,8 +149,8 @@ describe('PluginGuards — requireTripEdit and canEditAs', () => {
it('PGUARD-014 a non-owner member is flagged as shared', async () => {
const db = {
canAccessTrip: vi.fn(async () => ({ id: 1, user_id: 7 })),
} as unknown as DatabaseService;
findAccessible: vi.fn(async () => ({ id: 1, user_id: 7 })),
} as unknown as TripsRepository;
const users = { getRole: vi.fn(async () => 'user') } as unknown as UsersRepository;
const permissions = { checkPermission: vi.fn(() => true) } as unknown as PermissionsService;
const guards = new PluginGuards(db, permissions, {} as AddonsService, users);
@@ -201,8 +201,8 @@ describe('PluginGuards — canEditAs/canCreateAs against a real UsersRepository
orm = await createTestOrm(testDb);
const permissions = new PermissionsService(orm.repo(AppSettings), new UnitOfWork(orm.em));
const db = {
canAccessTrip: vi.fn(async (tripId: number) => (tripId === 1 ? { id: 1, user_id: ownerId } : undefined)),
} as unknown as DatabaseService;
findAccessible: vi.fn(async (tripId: number) => (tripId === 1 ? { id: 1, user_id: ownerId } : undefined)),
} as unknown as TripsRepository;
// The permissions cache is module-scoped (permissions-cache.ts), not
// per-instance — invalidate before every case so a stored override from
// another describe block in this same worker can't leak in.
@@ -16,7 +16,7 @@ vi.mock('../../../src/db/database', () => ({
db: { prepare: () => ({ get: () => (capabilitiesRow.value === undefined ? undefined : { capabilities: capabilitiesRow.value }) }) },
canAccessTrip,
}));
import { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { PluginRoutesController } from '../../../src/nest/plugins/contributions/plugin-routes.controller';
@@ -36,7 +36,7 @@ function controller(invoke: () => unknown, providers = ['ev-plug']) {
const plugins = {
findCapabilities: vi.fn(async () => (capabilitiesRow.value === undefined ? null : capabilitiesRow.value)),
} as unknown as PluginsRepository;
return { c: new PluginRoutesController(runtime, { canAccessTrip } as unknown as DatabaseService, plugins), runtime };
return { c: new PluginRoutesController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository, plugins), runtime };
}
const wp = (n = 3) => Array.from({ length: n }, (_, i) => ({ lat: 48 + i * 0.1, lng: 2 + i * 0.1 }));
const goodRoute = (n = 3, over: Record<string, unknown> = {}) => ({
@@ -42,6 +42,7 @@ import { RpcRateLimiter, DEFAULT_RPC_LIMIT } from '../../../src/nest/plugins/hos
import { createTestOrm, type TestOrm } from '../../helpers/test-orm';
import { AppSettings } from '../../../src/db/entities/AppSettings.entity';
import { Users } from '../../../src/db/entities/Users.entity';
import { Trips } from '../../../src/db/entities/Trips.entity';
import type { PluginRpcHost } from '../../../src/nest/plugins/host/rpc-host';
import type { RpcRequest, RpcResponse, RpcError } from '../../../src/nest/plugins/protocol/envelope';
import type { EntityManager } from '@mikro-orm/core';
@@ -76,7 +77,9 @@ beforeAll(async () => {
// above), not a stub, because this suite's whole point is proving that
// read fails outside withRequestContext and succeeds inside it (D6/C3).
// A mocked UsersRepository would defeat that regression coverage.
guards = new PluginGuards(dbs, permissions, await createTestAddonsService(testDb, dbs), t.repo(Users));
// Plan 4 Task 2 — PluginGuards' own canAccessTrip delegate is now
// TripsRepository.findAccessible; same REAL-repository reasoning applies.
guards = new PluginGuards(t.repo(Trips), permissions, await createTestAddonsService(testDb, dbs), t.repo(Users));
userId = createUser(testDb, { role: 'user' }).user.id;
// An admin has tightened trip_create from its 'everybody' default.
testDb.prepare("INSERT OR REPLACE INTO app_settings (key, value) VALUES ('perm_trip_create', 'admin')").run();
@@ -24,7 +24,7 @@ import { DaysRpc } from '../../../src/nest/days/days.rpc';
import { VacayRpc } from '../../../src/nest/vacay/vacay.rpc';
import { NotFoundError, ValidationError } from '../../../src/nest/trips/trips.service';
import type { EntityManager } from '@mikro-orm/core';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
import type { PermissionsService } from '../../../src/nest/permissions/permissions.service';
import type { AddonsService } from '../../../src/nest/addons/addons.service';
import type { UsersRepository } from '../../../src/db/repositories/Users.repository';
@@ -42,9 +42,9 @@ const ALL = new Set([
function guardsFor() {
const db = {
canAccessTrip: vi.fn(async () => ({ id: 1, user_id: 42 })),
findAccessible: vi.fn(async () => ({ id: 1, user_id: 42 })),
prepare: vi.fn(() => ({ get: () => ({ role: 'user' }), all: () => [] })),
} as unknown as DatabaseService;
} as unknown as TripsRepository;
// Plan 3c Task 7: TripsRpc's RP1-RP6 resolve `EntityManager.getRepository(...)`
// now, not `db.prepare(...)` — a generic fake repository (same shape regardless
// of which entity class was requested) covers every RP method these fallback
@@ -18,7 +18,7 @@ import { TripsRpc } from '../../../src/nest/trips/trips.rpc';
import { DbRpc } from '../../../src/nest/plugins/host/rpc/db.rpc';
import { PluginGuards } from '../../../src/nest/plugins/host/plugin-guards.service';
import type { EntityManager } from '@mikro-orm/core';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
import type { PluginUserSettingsService } from '../../../src/nest/plugins/plugin-user-settings.service';
const req = (method: string, params: Record<string, unknown>): RpcRequest => ({ k: 'req', id: 'x', method, params });
@@ -36,9 +36,9 @@ const dbRegistry = () => {
/** trips.getById lives on the decorators now, so the audit cases bind it through them. */
const tripsRegistry = () => {
const db = {
canAccessTrip: async (tripId: number, userId: number) => (tripId === 1 && userId === 42 ? { id: 1, user_id: 42 } : undefined),
findAccessible: async (tripId: number, userId: number) => (tripId === 1 && userId === 42 ? { id: 1, user_id: 42 } : undefined),
prepare: () => ({ get: () => ({ id: 1, title: 'Japan' }), all: () => [] }),
} as unknown as DatabaseService;
} as unknown as TripsRepository;
const guards = new PluginGuards(db, {} as never, {} as never, {} as never);
// Plan 3c Task 7: trips.getById (RP1) now reads through
// `EntityManager.getRepository(Trips).findRaw(...)`, not `db.prepare(...)`.
@@ -17,7 +17,7 @@ vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { TripCardContributionsController } from '../../../src/nest/plugins/contributions/trip-card-contributions.controller';
import type { PluginHooks } from '../../../src/nest/plugins/plugin-hooks.service';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const req = (id?: number) => ({ user: id === undefined ? undefined : { id } }) as any;
@@ -26,7 +26,7 @@ function controller(invoke: (id: string, args: unknown[]) => unknown, providers
providersOf: vi.fn(() => providers),
tripCards: vi.fn(async (id: string, _h: string, _fn: string, args: unknown[]) => invoke(id, args)),
} as unknown as PluginHooks;
return { c: new TripCardContributionsController(runtime, { canAccessTrip } as unknown as DatabaseService), runtime };
return { c: new TripCardContributionsController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository), runtime };
}
const badge = (over: Record<string, unknown> = {}) => ({ tripId: 1, id: 'b1', label: 'Visa', ...over });
@@ -9,7 +9,7 @@ vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { TripWarningsController } from '../../../src/nest/plugins/contributions/trip-warnings.controller';
import type { PluginHooks } from '../../../src/nest/plugins/plugin-hooks.service';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const req = (id?: number) => ({ user: id === undefined ? undefined : { id } }) as any;
@@ -20,7 +20,7 @@ function controller(over: Partial<PluginHooks> = {}) {
id === 'p2' ? [{ level: 'error', message: 'Day 3 is overpacked', dayId: 3 }] : [{ level: 'warning', message: 'Museum closed Mon', placeId: 7 }]),
...over,
} as unknown as PluginHooks;
return { c: new TripWarningsController(runtime, { canAccessTrip } as unknown as DatabaseService), runtime };
return { c: new TripWarningsController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository), runtime };
}
describe('TripWarningsController', () => {
@@ -16,7 +16,7 @@ vi.mock('../../../src/nest/plugins/kill-switch', () => ({ pluginsEnabled }));
import { ViewContributionsController } from '../../../src/nest/plugins/contributions/view-contributions.controller';
import type { PluginHooks } from '../../../src/nest/plugins/plugin-hooks.service';
import type { DatabaseService } from '../../../src/nest/database/database.service';
import type { TripsRepository } from '../../../src/db/repositories/Trips.repository';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const req = (id?: number) => ({ user: id === undefined ? undefined : { id } }) as any;
@@ -25,7 +25,7 @@ function controller(invoke: (id: string) => unknown, providers = ['p1']) {
providersOf: vi.fn(() => providers),
tableContributions: vi.fn(async (id: string) => invoke(id)),
} as unknown as PluginHooks;
return { c: new ViewContributionsController(runtime, { canAccessTrip } as unknown as DatabaseService), runtime };
return { c: new ViewContributionsController(runtime, { findAccessible: canAccessTrip } as unknown as TripsRepository), runtime };
}
const col = (over: Record<string, unknown> = {}) => ({ kind: 'column', entityId: 1, id: 'c1', label: 'X', ...over });
const act = (over: Record<string, unknown> = {}) => ({ kind: 'action', entityId: 1, id: 'a1', label: 'Go', target: { kind: 'frame', sub: '/ui' }, ...over });