Skip to content

test(adapter): comprehensive HomegrownAdapter unit tests#69

Merged
Jesssullivan merged 1 commit intomainfrom
feat/homegrown-adapter-tests
Apr 18, 2026
Merged

test(adapter): comprehensive HomegrownAdapter unit tests#69
Jesssullivan merged 1 commit intomainfrom
feat/homegrown-adapter-tests

Conversation

@Jesssullivan
Copy link
Copy Markdown
Owner

Summary

  • Expands HomegrownAdapter test coverage from 3 superficial constructor checks to 28 behavioral tests
  • Mocks Drizzle DB layer with fluent chain builder to test service resolution (UUID vs acuityId), provider lookup (solo practice pattern), client find-or-create, reservation create/release, booking cancellation, and Effect error wrapping
  • Null→undefined mapping, empty result handling, and non-Error throw wrapping validated

Coverage areas

  • Services: getServices (4 tests), getService (3 tests)
  • Providers: getProviders (3), getProvider (2), getProvidersForService (1)
  • Reservations: createReservation (2), releaseReservation (1)
  • Clients: findOrCreateClient (2), getClientByEmail (3)
  • Bookings: cancelBooking (2)
  • Effect wrapping: DB errors (2)
  • Config: creation + method existence + custom config (3)

Availability math (getAvailableDates, getAvailableSlots, checkSlotAvailability) delegates to availability-engine.ts which already has 39 dedicated tests.

Test plan

  • All 28 new tests pass
  • Full suite: 606 tests pass (21 files), 0 failures
  • No type errors

Tracker: TIN-104 (package quality)

Mock Drizzle DB layer to test service resolution, provider lookup,
client find-or-create, reservation lifecycle, booking cancellation,
and Effect error wrapping. Covers 28 cases (up from 3 superficial
constructor checks). Availability math delegation to
availability-engine.ts is already covered by its own 39 tests.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 18, 2026

Greptile Summary

This PR replaces 3 superficial constructor checks with 28 behavioral unit tests for HomegrownAdapter, using a fluent mock-DB chain to cover service/provider lookup, client find-or-create, reservation lifecycle, booking cancellation, and Effect error wrapping. All findings are P2.

  • The four most complex methods — createBooking, createBookingWithPaymentRef, getBooking, and rescheduleBooking — have no unit tests; createBooking in particular nests a live Effect.runPromise call that is worth exercising.
  • The isUuid routing branch in resolveService is not validated because the where mock discards its argument.
  • beforeEach is imported from vitest but never used.

Confidence Score: 5/5

Test-only PR; all findings are P2 suggestions and do not block merge.

No production code is changed. The 28 new tests are correctly structured, the mock chain faithfully mirrors the Drizzle fluent API, and all assertions align with the implementation. The only gaps (missing booking-method tests, unvalidated UUID branch, unused import) are style/coverage suggestions that don't indicate correctness problems in the tests that do exist.

No files require special attention.

Important Files Changed

Filename Overview
src/adapters/tests/homegrown-adapter.test.ts Expands HomegrownAdapter unit tests from 3 constructor checks to 28 behavioral tests using a fluent mock-DB chain; findings are P2 only (unused beforeEach import, no coverage for createBooking/getBooking/rescheduleBooking, UUID routing branch not validated).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[createMockDb] --> B[chainTerminals\nlimit · orderBy · returning]
    A --> C[chain\nfrom · where · values · set]
    C --> D[from returns\nwhere · orderBy · limit]
    D -->|.where| B

    subgraph getServices
        E[select · from · where · orderBy] --> B
    end

    subgraph getService / getProvider / getClientByEmail
        F[select · from · where · limit] --> B
    end

    subgraph findOrCreateClient
        G[select · where · limit] --> B
        H[insert · values · returning] --> B
        I[update · set · where] -->|resolves undefined| J[void]
    end

    subgraph cancelBooking / releaseReservation
        K[update · set · where] -->|resolves undefined| J
    end

    subgraph NOT TESTED
        L[createBooking\nfindOrCreateClient + resolveService + insert]
        M[getBooking\n4x select joins]
        N[rescheduleBooking\nselect + update + getBooking]
    end
Loading

Reviews (1): Last reviewed commit: "test(adapter): comprehensive HomegrownAd..." | Re-trigger Greptile

const mockGetDb = async () => {
throw new Error('No DB in unit tests');
// ---------------------------------------------------------------------------
// Mock schemas — minimal shape matching what Drizzle ORM tables expose
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Unused import: beforeEach

beforeEach is imported from vitest but never used in the test file. This will trigger a lint/TS unused-import warning.

Suggested change
// Mock schemas — minimal shape matching what Drizzle ORM tables expose
import { describe, it, expect, vi } from 'vitest';

Comment on lines +396 to +418
// -------------------------------------------------------------------------

describe('createReservation', () => {
it('inserts a reservation and returns SlotReservation', async () => {
const mockDb = createMockDb({ insert: [RESERVATION_ROW] });
const adapter = createHomegrownAdapter({ getDb: async () => mockDb });

const result = await Effect.runPromise(adapter.createReservation({
serviceId: 'svc-uuid-1',
providerId: 'prac-uuid-1',
datetime: '2026-04-20T14:00:00.000Z',
duration: 60,
expirationMinutes: 10,
}));

expect(result).toEqual({
id: 'res-uuid-1',
datetime: '2026-04-20T14:00:00.000Z',
duration: 60,
expiresAt: '2026-04-20T14:10:00.000Z',
providerId: 'prac-uuid-1',
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Complex booking methods have no unit tests

createBooking, createBookingWithPaymentRef, getBooking, and rescheduleBooking — the four most structurally complex methods in the adapter — are absent from this suite. createBooking is particularly notable: it nests a live Effect.runPromise(adapter.findOrCreateClient(...)) call inside fromAsync, making it the only method that can surface an unexpected error from a nested Effect if the composition assumption ever changes. getBooking performs four separate select calls and reconstructs the full Booking shape including a conditional practitioner lookup.

Worth adding these tests to complete the scope, even with the same fluent-chain mock pattern used elsewhere.

Comment on lines +229 to +249
name: 'Deep Tissue Massage',
description: '60-minute therapeutic session',
duration: 60,
price: 9500,
currency: 'USD',
category: 'therapeutic',
active: true,
}]);
});

it('returns empty array when no active services exist', async () => {
const mockDb = createMockDb();
mockDb._terminals.orderBy.mockResolvedValue([]);

const adapter = createHomegrownAdapter({ getDb: async () => mockDb });
const result = await Effect.runPromise(adapter.getServices());

expect(result).toEqual([]);
});

it('maps null description to undefined', async () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 UUID routing condition not verified

getService with a full UUID and with a numeric acuityId take different code paths through the isUuid branch in resolveService — a UUID triggers or(eq(id,...), eq(acuityId,...)) while a non-UUID uses only eq(acuityId,...). Because the mock ignores the where argument, both tests resolve identically from the canned row and the branch logic is never exercised. Inspecting the where spy's call argument would let you assert that the UUID path actually builds the compound or(...) condition.

@Jesssullivan Jesssullivan merged commit d5fa499 into main Apr 18, 2026
5 checks passed
@Jesssullivan Jesssullivan deleted the feat/homegrown-adapter-tests branch April 18, 2026 15:03
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.

1 participant