Skip to content

Add server-action tests for assignLicense, bulk import users + licenses #47

@studert

Description

@studert

Problem

Three of the highest-traffic, most data-changing server actions have no direct test coverage:

  • assignLicense (and the related update/revoke paths) in src/actions/assignments.ts
  • bulkImportUsers in src/actions/users.ts
  • The bulk-license-assignment flow used by feature 004

These actions touch payments-adjacent data (license counts vs ai_tools.maxLicenses, encrypted API keys, audit history) and are exposed through admin UI flows that are easy to regress. Adding fast Vitest coverage prevents future PRs from silently breaking constraints.

Evidence

  • tests/unit/ and tests/integration/ contain no assignments.test.ts, users.test.ts, or bulk-import tests.
  • src/actions/assignments.ts:assignLicense — validates input with assignmentSchema, checks max-licenses, encrypts API key, writes assignment + history rows.
  • src/actions/users.ts:bulkImportUsers — accepts a CSV/JSON payload (see project spec 011), validates with Zod, dedupes against existing users, writes users + invite tokens.

Proposed approach

Use the existing integration-test harness (Vitest + real Neon test branch). Place new files at:

  • tests/integration/actions/assignments.test.ts
  • tests/integration/actions/users-bulk-import.test.ts
  • tests/integration/actions/license-bulk-assign.test.ts

For each, cover at minimum:

assignments.test.ts

  • Happy path: assignLicense creates a row, increments usage, writes change_history entry.
  • Validation: bad email / bad tier id → { success: false, fieldErrors }.
  • Capacity: tool with maxLicenses = 0 → rejected without creating a row.
  • Auth: non-admin caller → rejected by requireAdmin.
  • Encryption: with apiKey provided, the stored ciphertext decrypts back to the input.

users-bulk-import.test.ts

  • 10-row valid CSV → 10 users created, 10 invite tokens issued, no duplicates.
  • Mixed-case email duplicates within payload → deduped per the spec's rule.
  • One invalid row + nine valid → either entire-batch reject or partial success per the action's contract (mirror current behaviour).
  • Existing user collision → action returns the configured per-row error.

license-bulk-assign.test.ts

  • N rows assigning to N users on a tool with capacity → all succeed.
  • N+1 rows on a tool with capacity N → the (N+1)th is rejected, others succeed (or whole batch fails — match current behaviour).
  • Per-row API-key encryption.

For all three: stub Resend (@/lib/email) and any other external side-effects. Reuse the test-DB setup pattern from tests/integration/sync/.

Acceptance criteria

  • Three new test files exist under tests/integration/actions/.
  • Each file has at least 4 tests covering happy + validation + auth + edge cases.
  • All tests pass under pnpm test:integration.
  • No real emails are sent (Resend mocked).
  • pnpm lint && pnpm typecheck pass.

Verification

  1. Run pnpm test:integration tests/integration/actions/ against a clean Neon test branch.
  2. Mutate assignLicense to skip the max-licenses check → confirm the capacity test fails. Revert.
  3. Mutate bulkImportUsers to skip dedupe → confirm the dedupe test fails. Revert.

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority:mediumImportant, not urgenttestsTest coverage / disabled tests

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions