All files / src/app/api/admin/dev-tools/tests route.ts

0% Statements 0/105
100% Branches 0/0
0% Functions 0/1
0% Lines 0/105

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106                                                                                                                                                                                                                   
/**
 * Test Runner API
 * POST /api/admin/dev-tools/tests - Run tests
 * GET /api/admin/dev-tools/tests - Get test status
 */

import { NextRequest, NextResponse } from 'next/server';
import { } from "next-auth";
import {
  withAdmin,
  withErrorHandling,
  successResponse,
  ApiError,
  ApiSuccessResponse,
  ApiErrorResponse } from "@/lib/api";
import { } from "@/lib/api/middleware";
import { runTests, isProcessRunning, killProcess } from '@/lib/dev-tools/command-runner';
import { parseJestOutput } from '@/lib/dev-tools/output-parsers';
import type { CommandStatusResponse } from '@/lib/dev-tools/types';

const PROCESS_ID = 'dev-tools-tests';

// Store last result
let lastResult: CommandStatusResponse = { status: 'idle' };

/**
 * POST - Run tests
 */
async function handlePost(request: NextRequest): Promise<NextResponse<ApiSuccessResponse<unknown> | ApiErrorResponse>> {
  // Check if tests are already running
  if (isProcessRunning(PROCESS_ID)) {
    throw ApiError.conflict('Tests are already running');
  }

  const body = await request.json().catch(() => ({}));
  const { pattern, coverage } = body as { pattern?: string; coverage?: boolean };

  // Update status to running
  lastResult = {
    status: 'running',
    startedAt: new Date().toISOString() };

  // Run tests asynchronously
  runTests({ pattern, coverage, processId: PROCESS_ID })
    .then((result) => {
      const parsedResult = parseJestOutput(result.output + result.error, result.exitCode);
      lastResult = {
        status: result.success ? 'completed' : 'failed',
        output: result.output + result.error,
        result: parsedResult,
        startedAt: lastResult.startedAt,
        completedAt: new Date().toISOString() };
    })
    .catch((error) => {
      lastResult = {
        status: 'failed',
        output: error.message,
        startedAt: lastResult.startedAt,
        completedAt: new Date().toISOString() };
    });

  return successResponse({
    success: true,
    message: 'Tests started',
    status: 'running' });
}

/**
 * GET - Get test status
 */
async function handleGet(): Promise<NextResponse<ApiSuccessResponse<CommandStatusResponse> | ApiErrorResponse>> {
  // Check if still running
  if (lastResult.status === 'running' && !isProcessRunning(PROCESS_ID)) {
    // Process ended but we didn't catch the result - mark as failed
    lastResult.status = 'failed';
    lastResult.completedAt = new Date().toISOString();
  }

  return successResponse(lastResult);
}

/**
 * DELETE - Stop running tests
 */
async function handleDelete(): Promise<NextResponse<ApiSuccessResponse<unknown> | ApiErrorResponse>> {
  if (!isProcessRunning(PROCESS_ID)) {
    throw ApiError.notFound('No tests running');
  }

  const killed = killProcess(PROCESS_ID);
  if (killed) {
    lastResult = {
      status: 'failed',
      output: 'Tests cancelled by user',
      startedAt: lastResult.startedAt,
      completedAt: new Date().toISOString() };
    return successResponse({ success: true, message: 'Tests stopped' });
  }

  throw ApiError.internal('Failed to stop tests');
}

export const GET = withErrorHandling(withAdmin(handleGet));
export const POST = withErrorHandling(withAdmin(handlePost));
export const DELETE = withErrorHandling(withAdmin(handleDelete));